forked from shadowfacts/Tusker
Start account-specific push subscriptions
This commit is contained in:
parent
9fad2a882a
commit
f98589b419
|
@ -12,13 +12,82 @@ public struct PushSubscription: Decodable, Sendable {
|
|||
public let id: String
|
||||
public let endpoint: URL
|
||||
public let serverKey: String
|
||||
// TODO: WTF is this?
|
||||
// public let alerts
|
||||
public let alerts: Alerts
|
||||
public let policy: Policy
|
||||
|
||||
public static func create(endpoint: URL, publicKey: Data, authSecret: Data, alerts: Alerts, policy: Policy) -> Request<PushSubscription> {
|
||||
return Request(method: .post, path: "/api/v1/push/subscription", body: ParametersBody([
|
||||
"subscription[endpoint]" => endpoint.absoluteString,
|
||||
"subscription[keys][p256dh]" => publicKey.base64EncodedString(),
|
||||
"subscription[keys][auth]" => authSecret.base64EncodedString(),
|
||||
"data[alerts][mention]" => alerts.mention,
|
||||
"data[alerts][status]" => alerts.status,
|
||||
"data[alerts][reblog]" => alerts.reblog,
|
||||
"data[alerts][follow]" => alerts.follow,
|
||||
"data[alerts][follow_request]" => alerts.followRequest,
|
||||
"data[alerts][favourite]" => alerts.favourite,
|
||||
"data[alerts][poll]" => alerts.poll,
|
||||
"data[alerts][update]" => alerts.update,
|
||||
"data[policy]" => policy.rawValue,
|
||||
]))
|
||||
}
|
||||
|
||||
public static func update(alerts: Alerts, policy: Policy) -> Request<PushSubscription> {
|
||||
return Request(method: .put, path: "/api/v1/push/subscription", body: ParametersBody([
|
||||
"data[alerts][mention]" => alerts.mention,
|
||||
"data[alerts][status]" => alerts.status,
|
||||
"data[alerts][reblog]" => alerts.reblog,
|
||||
"data[alerts][follow]" => alerts.follow,
|
||||
"data[alerts][follow_request]" => alerts.followRequest,
|
||||
"data[alerts][favourite]" => alerts.favourite,
|
||||
"data[alerts][poll]" => alerts.poll,
|
||||
"data[alerts][update]" => alerts.update,
|
||||
"data[policy]" => policy.rawValue,
|
||||
]))
|
||||
}
|
||||
|
||||
public static func delete() -> Request<Empty> {
|
||||
return Request(method: .delete, path: "/api/v1/push/subscription")
|
||||
}
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case id
|
||||
case endpoint
|
||||
case serverKey = "server_key"
|
||||
// case alerts
|
||||
case alerts
|
||||
case policy
|
||||
}
|
||||
}
|
||||
|
||||
extension PushSubscription {
|
||||
public struct Alerts: Decodable, Sendable {
|
||||
public let mention: Bool
|
||||
public let status: Bool
|
||||
public let reblog: Bool
|
||||
public let follow: Bool
|
||||
public let followRequest: Bool
|
||||
public let favourite: Bool
|
||||
public let poll: Bool
|
||||
public let update: Bool
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case mention
|
||||
case status
|
||||
case reblog
|
||||
case follow
|
||||
case followRequest = "follow_request"
|
||||
case favourite
|
||||
case poll
|
||||
case update
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension PushSubscription {
|
||||
public enum Policy: String, Decodable, Sendable {
|
||||
case all
|
||||
case followed
|
||||
case followers
|
||||
case none
|
||||
}
|
||||
}
|
||||
|
|
|
@ -124,6 +124,7 @@
|
|||
D64B967C2BC19C28002C8990 /* NotificationsPrefsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D64B967B2BC19C28002C8990 /* NotificationsPrefsView.swift */; };
|
||||
D64B967F2BC1D447002C8990 /* PushManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D64B967E2BC1D447002C8990 /* PushManager.swift */; };
|
||||
D64B96812BC3279D002C8990 /* PrefsAccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D64B96802BC3279D002C8990 /* PrefsAccountView.swift */; };
|
||||
D64B96842BC3893C002C8990 /* PushSubscriptionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D64B96832BC3893C002C8990 /* PushSubscriptionView.swift */; };
|
||||
D64D0AB12128D9AE005A6F37 /* OnboardingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D64D0AB02128D9AE005A6F37 /* OnboardingViewController.swift */; };
|
||||
D64D8CA92463B494006B0BAA /* MultiThreadDictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = D64D8CA82463B494006B0BAA /* MultiThreadDictionary.swift */; };
|
||||
D651C5B42915B00400236EF6 /* ProfileFieldsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D651C5B32915B00400236EF6 /* ProfileFieldsView.swift */; };
|
||||
|
@ -132,6 +133,8 @@
|
|||
D6552367289870790048A653 /* ScreenCorners in Frameworks */ = {isa = PBXBuildFile; platformFilters = (ios, maccatalyst, ); productRef = D6552366289870790048A653 /* ScreenCorners */; };
|
||||
D659F35E2953A212002D944A /* TTTKit in Frameworks */ = {isa = PBXBuildFile; productRef = D659F35D2953A212002D944A /* TTTKit */; };
|
||||
D659F36229541065002D944A /* TTTView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D659F36129541065002D944A /* TTTView.swift */; };
|
||||
D65A261B2BC3928A005EB5D8 /* TriStateToggle.swift in Sources */ = {isa = PBXBuildFile; fileRef = D65A261A2BC3928A005EB5D8 /* TriStateToggle.swift */; };
|
||||
D65A261D2BC39399005EB5D8 /* PushInstanceSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D65A261C2BC39399005EB5D8 /* PushInstanceSettingsView.swift */; };
|
||||
D65B4B542971F71D00DABDFB /* EditedReport.swift in Sources */ = {isa = PBXBuildFile; fileRef = D65B4B532971F71D00DABDFB /* EditedReport.swift */; };
|
||||
D65B4B562971F98300DABDFB /* ReportView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D65B4B552971F98300DABDFB /* ReportView.swift */; };
|
||||
D65B4B58297203A700DABDFB /* ReportSelectRulesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D65B4B57297203A700DABDFB /* ReportSelectRulesView.swift */; };
|
||||
|
@ -527,12 +530,15 @@
|
|||
D64B967B2BC19C28002C8990 /* NotificationsPrefsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsPrefsView.swift; sourceTree = "<group>"; };
|
||||
D64B967E2BC1D447002C8990 /* PushManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PushManager.swift; sourceTree = "<group>"; };
|
||||
D64B96802BC3279D002C8990 /* PrefsAccountView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrefsAccountView.swift; sourceTree = "<group>"; };
|
||||
D64B96832BC3893C002C8990 /* PushSubscriptionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PushSubscriptionView.swift; sourceTree = "<group>"; };
|
||||
D64D0AB02128D9AE005A6F37 /* OnboardingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingViewController.swift; sourceTree = "<group>"; };
|
||||
D64D8CA82463B494006B0BAA /* MultiThreadDictionary.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultiThreadDictionary.swift; sourceTree = "<group>"; };
|
||||
D651C5B32915B00400236EF6 /* ProfileFieldsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileFieldsView.swift; sourceTree = "<group>"; };
|
||||
D6531DED246B81C9000F9538 /* GifvPlayerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GifvPlayerView.swift; sourceTree = "<group>"; };
|
||||
D6538944214D6D7500E3CEFC /* TableViewSwipeActionProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TableViewSwipeActionProvider.swift; sourceTree = "<group>"; };
|
||||
D659F36129541065002D944A /* TTTView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TTTView.swift; sourceTree = "<group>"; };
|
||||
D65A261A2BC3928A005EB5D8 /* TriStateToggle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TriStateToggle.swift; sourceTree = "<group>"; };
|
||||
D65A261C2BC39399005EB5D8 /* PushInstanceSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PushInstanceSettingsView.swift; sourceTree = "<group>"; };
|
||||
D65B4B532971F71D00DABDFB /* EditedReport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditedReport.swift; sourceTree = "<group>"; };
|
||||
D65B4B552971F98300DABDFB /* ReportView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportView.swift; sourceTree = "<group>"; };
|
||||
D65B4B57297203A700DABDFB /* ReportSelectRulesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportSelectRulesView.swift; sourceTree = "<group>"; };
|
||||
|
@ -1126,7 +1132,7 @@
|
|||
0427033922B31269000D31B6 /* AdvancedPrefsView.swift */,
|
||||
D67895BF246870DE00D4CD9E /* LocalAccountAvatarView.swift */,
|
||||
D68A76ED295369C7001DA1B3 /* AcknowledgementsView.swift */,
|
||||
D64B967B2BC19C28002C8990 /* NotificationsPrefsView.swift */,
|
||||
D64B96822BC3892B002C8990 /* Notifications */,
|
||||
D60089172981FEA4005B4D00 /* Tip Jar */,
|
||||
D68A76EF2953910A001DA1B3 /* About */,
|
||||
);
|
||||
|
@ -1197,6 +1203,17 @@
|
|||
path = Push;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
D64B96822BC3892B002C8990 /* Notifications */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D64B967B2BC19C28002C8990 /* NotificationsPrefsView.swift */,
|
||||
D65A261C2BC39399005EB5D8 /* PushInstanceSettingsView.swift */,
|
||||
D64B96832BC3893C002C8990 /* PushSubscriptionView.swift */,
|
||||
D65A261A2BC3928A005EB5D8 /* TriStateToggle.swift */,
|
||||
);
|
||||
path = Notifications;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
D65A37F221472F300087646E /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -2082,6 +2099,7 @@
|
|||
D68329EF299540050026EB24 /* MoreTrendsFooterCollectionViewCell.swift in Sources */,
|
||||
D623A5412635FB3C0095BD04 /* PollOptionView.swift in Sources */,
|
||||
D61F75B1293BD85300C0B37F /* CreateFilterService.swift in Sources */,
|
||||
D65A261D2BC39399005EB5D8 /* PushInstanceSettingsView.swift in Sources */,
|
||||
D65C6BF525478A9C00A6E89C /* BackgroundableViewController.swift in Sources */,
|
||||
D61DC84D28F500D200B82C6E /* ProfileViewController.swift in Sources */,
|
||||
D600891F29848DE2005B4D00 /* AddInstancePinnedTimelineView.swift in Sources */,
|
||||
|
@ -2141,6 +2159,7 @@
|
|||
D6333B792139AEFD00CE884A /* Date+TimeAgo.swift in Sources */,
|
||||
D6D706A029466649000827ED /* ScrollingSegmentedControl.swift in Sources */,
|
||||
D686BBE324FBF8110068E6AA /* WrappedProgressView.swift in Sources */,
|
||||
D65A261B2BC3928A005EB5D8 /* TriStateToggle.swift in Sources */,
|
||||
D620483423D3801D008A63EF /* LinkTextView.swift in Sources */,
|
||||
D64B967F2BC1D447002C8990 /* PushManager.swift in Sources */,
|
||||
D61F75882932DB6000C0B37F /* StatusSwipeActions.swift in Sources */,
|
||||
|
@ -2239,6 +2258,7 @@
|
|||
D6C3F5172991C1A00009FCFF /* View+AppListStyle.swift in Sources */,
|
||||
D65B4B6A297777D900DABDFB /* StatusNotFoundView.swift in Sources */,
|
||||
D6412B0924B0291E00F5412E /* MyProfileViewController.swift in Sources */,
|
||||
D64B96842BC3893C002C8990 /* PushSubscriptionView.swift in Sources */,
|
||||
D6CF5B832AC65DDF00F15D83 /* NSCollectionLayoutSection+Readable.swift in Sources */,
|
||||
D601FA5D297B2E6F00A8E8B5 /* ConversationCollectionViewController.swift in Sources */,
|
||||
D6D12B56292D57E800D528E1 /* AccountCollectionViewCell.swift in Sources */,
|
||||
|
|
|
@ -12,6 +12,7 @@ import OSLog
|
|||
import Sentry
|
||||
#endif
|
||||
import Pachyderm
|
||||
import UserAccounts
|
||||
|
||||
private let logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "PushManager")
|
||||
|
||||
|
@ -40,10 +41,12 @@ struct PushManager {
|
|||
}
|
||||
|
||||
@MainActor
|
||||
protocol _PushManager: ObservableObject {
|
||||
protocol _PushManager {
|
||||
var enabled: Bool { get }
|
||||
var pushProxyRegistration: PushProxyRegistration? { get }
|
||||
|
||||
func pushSubscription(account: UserAccountInfo) -> PushSubscription?
|
||||
|
||||
func register(transactionID: UInt64) async throws -> PushProxyRegistration
|
||||
func unregister() async throws
|
||||
func updateIfNecessary() async
|
||||
|
@ -61,6 +64,10 @@ private class DisabledPushManager: _PushManager {
|
|||
nil
|
||||
}
|
||||
|
||||
func pushSubscription(account: UserAccountInfo) -> PushSubscription? {
|
||||
nil
|
||||
}
|
||||
|
||||
func register(transactionID: UInt64) async throws -> PushProxyRegistration {
|
||||
throw Disabled()
|
||||
}
|
||||
|
@ -112,7 +119,6 @@ private class PushManagerImpl: _PushManager {
|
|||
}
|
||||
}
|
||||
set {
|
||||
objectWillChange.send()
|
||||
defaults.setValue(newValue?.defaultsDict, forKey: "PushProxyRegistration")
|
||||
}
|
||||
}
|
||||
|
@ -125,7 +131,6 @@ private class PushManagerImpl: _PushManager {
|
|||
}
|
||||
}
|
||||
set {
|
||||
objectWillChange.send()
|
||||
defaults.setValue(newValue.map(\.defaultsDict), forKey: "PushSubscriptions")
|
||||
}
|
||||
}
|
||||
|
@ -134,6 +139,10 @@ private class PushManagerImpl: _PushManager {
|
|||
self.endpoint = endpoint
|
||||
}
|
||||
|
||||
func pushSubscription(account: UserAccountInfo) -> PushSubscription? {
|
||||
pushSubscriptions.first { $0.accountID == account.id }
|
||||
}
|
||||
|
||||
func register(transactionID: UInt64) async throws -> PushProxyRegistration {
|
||||
guard remoteNotificationsRegistrationContinuation == nil else {
|
||||
throw PushRegistrationError.alreadyRegistering
|
||||
|
|
|
@ -56,7 +56,7 @@ struct NotificationsPrefsView: View {
|
|||
private var accountsSection: some View {
|
||||
Section {
|
||||
ForEach(userAccounts.accounts) { account in
|
||||
PrefsAccountView(account: account)
|
||||
PushInstanceSettingsView(account: account)
|
||||
}
|
||||
}
|
||||
.appGroupedListRowBackground()
|
||||
|
@ -112,52 +112,6 @@ struct NotificationsPrefsView: View {
|
|||
}
|
||||
}
|
||||
|
||||
private struct TriStateToggle: View {
|
||||
let titleKey: LocalizedStringKey
|
||||
@Binding var mode: Mode
|
||||
let onChange: (Bool) async -> Void
|
||||
@State private var isOn: Bool = false
|
||||
|
||||
var body: some View {
|
||||
toggleOrSpinner
|
||||
.onChange(of: mode) { newValue in
|
||||
switch newValue {
|
||||
case .off:
|
||||
isOn = false
|
||||
case .loading:
|
||||
break
|
||||
case .on:
|
||||
isOn = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private var toggleOrSpinner: some View {
|
||||
if mode == .loading {
|
||||
ProgressView()
|
||||
} else {
|
||||
Toggle(titleKey, isOn: Binding {
|
||||
isOn
|
||||
} set: { newValue in
|
||||
isOn = newValue
|
||||
mode = .loading
|
||||
Task {
|
||||
await onChange(newValue)
|
||||
mode = newValue ? .on : .off
|
||||
}
|
||||
})
|
||||
.labelsHidden()
|
||||
}
|
||||
}
|
||||
|
||||
enum Mode {
|
||||
case off
|
||||
case loading
|
||||
case on
|
||||
}
|
||||
}
|
||||
|
||||
private enum NotificationsSetupError: LocalizedError {
|
||||
case requestingAuthorization(any Error)
|
||||
case registering(any Error)
|
|
@ -0,0 +1,82 @@
|
|||
//
|
||||
// PushInstanceSettingsView.swift
|
||||
// Tusker
|
||||
//
|
||||
// Created by Shadowfacts on 4/7/24.
|
||||
// Copyright © 2024 Shadowfacts. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import UserAccounts
|
||||
import Pachyderm
|
||||
|
||||
struct PushInstanceSettingsView: View {
|
||||
let account: UserAccountInfo
|
||||
let pushProxyRegistration: PushProxyRegistration
|
||||
@State private var mode: TriStateToggle.Mode
|
||||
@State private var error: Error?
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .prefsAvatar) {
|
||||
HStack {
|
||||
PrefsAccountView(account: account)
|
||||
TriStateToggle(titleKey: "\(account.instanceURL.host!) notifications enabled", mode: $mode, onChange: updateNotificationsEnabled(enabled:))
|
||||
}
|
||||
PushSubscriptionView(account: account)
|
||||
}
|
||||
.alertWithData("An Error Occurred", data: $error) { data in
|
||||
Button("OK") {}
|
||||
} message: { data in
|
||||
Text(data.localizedDescription)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private func updateNotificationsEnabled(enabled: Bool) async {
|
||||
if enabled {
|
||||
do {
|
||||
try await enableNotifications()
|
||||
} catch {
|
||||
self.error = .enabling(error)
|
||||
}
|
||||
} else {
|
||||
do {
|
||||
try await disableNotifications()
|
||||
} catch {
|
||||
self.error = .disabling(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func enableNotifications() async throws {
|
||||
let req = Pachyderm.PushSubscription.create(
|
||||
endpoint: pushProxyRegistration.endpoint,
|
||||
publicKey: <#T##Data#>,
|
||||
authSecret: <#T##Data#>,
|
||||
alerts: <#T##PushSubscription.Alerts#>,
|
||||
policy: <#T##PushSubscription.Policy#>
|
||||
)
|
||||
}
|
||||
|
||||
private func disableNotifications() async throws {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private enum Error: LocalizedError {
|
||||
case enabling(any Swift.Error)
|
||||
case disabling(any Swift.Error)
|
||||
|
||||
var errorDescription: String? {
|
||||
switch self {
|
||||
case .enabling(let error):
|
||||
"Enabling push: \(error.localizedDescription)"
|
||||
case .disabling(let error):
|
||||
"Disabling push: \(error.localizedDescription)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//#Preview {
|
||||
// PushInstanceSettingsView()
|
||||
//}
|
|
@ -0,0 +1,33 @@
|
|||
//
|
||||
// PushSubscriptionView.swift
|
||||
// Tusker
|
||||
//
|
||||
// Created by Shadowfacts on 4/7/24.
|
||||
// Copyright © 2024 Shadowfacts. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import UserAccounts
|
||||
|
||||
struct PushSubscriptionView: View {
|
||||
let account: UserAccountInfo
|
||||
@State private var subscription: PushSubscription?
|
||||
|
||||
@MainActor
|
||||
init(account: UserAccountInfo) {
|
||||
self.account = account
|
||||
self.subscription = PushManager.shared.pushSubscription(account: account)
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
if let subscription {
|
||||
|
||||
} else {
|
||||
Text("No notifications")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//#Preview {
|
||||
// PushSubscriptionView()
|
||||
//}
|
|
@ -0,0 +1,63 @@
|
|||
//
|
||||
// TriStateToggle.swift
|
||||
// Tusker
|
||||
//
|
||||
// Created by Shadowfacts on 4/7/24.
|
||||
// Copyright © 2024 Shadowfacts. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct TriStateToggle: View {
|
||||
let titleKey: LocalizedStringKey
|
||||
@Binding var mode: Mode
|
||||
let onChange: (Bool) async -> Void
|
||||
@State private var isOn: Bool = false
|
||||
|
||||
var body: some View {
|
||||
toggleOrSpinner
|
||||
.onChange(of: mode) { newValue in
|
||||
switch newValue {
|
||||
case .off:
|
||||
isOn = false
|
||||
case .loading:
|
||||
break
|
||||
case .on:
|
||||
isOn = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private var toggleOrSpinner: some View {
|
||||
if mode == .loading {
|
||||
ProgressView()
|
||||
} else {
|
||||
Toggle(titleKey, isOn: Binding {
|
||||
isOn
|
||||
} set: { newValue in
|
||||
isOn = newValue
|
||||
mode = .loading
|
||||
Task {
|
||||
await onChange(newValue)
|
||||
mode = newValue ? .on : .off
|
||||
}
|
||||
})
|
||||
.labelsHidden()
|
||||
}
|
||||
}
|
||||
|
||||
enum Mode {
|
||||
case off
|
||||
case loading
|
||||
case on
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#Preview {
|
||||
@State var mode = TriStateToggle.Mode.on
|
||||
return TriStateToggle(titleKey: "", mode: $mode) { _ in
|
||||
try! await Task.sleep(nanoseconds: NSEC_PER_SEC)
|
||||
}
|
||||
}
|
|
@ -16,7 +16,7 @@ struct PrefsAccountView: View {
|
|||
var body: some View {
|
||||
HStack {
|
||||
LocalAccountAvatarView(localAccountInfo: account)
|
||||
VStack(alignment: .leading) {
|
||||
VStack(alignment: .prefsAvatar) {
|
||||
Text(verbatim: account.username)
|
||||
.foregroundColor(.primary)
|
||||
let instance = if let domain = WebURL.Domain(account.instanceURL.host!) {
|
||||
|
@ -28,10 +28,23 @@ struct PrefsAccountView: View {
|
|||
.font(.caption)
|
||||
.foregroundColor(.primary)
|
||||
}
|
||||
.alignmentGuide(.prefsAvatar, computeValue: { dimension in
|
||||
dimension[.leading]
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private struct AvatarAlignment: AlignmentID {
|
||||
static func defaultValue(in context: ViewDimensions) -> CGFloat {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
extension HorizontalAlignment {
|
||||
static let prefsAvatar = HorizontalAlignment(AvatarAlignment.self)
|
||||
}
|
||||
|
||||
//#Preview {
|
||||
// PrefsAccountView()
|
||||
//}
|
||||
|
|
Loading…
Reference in New Issue