mirror of
https://github.com/Dimillian/IceCubesApp.git
synced 2024-12-25 00:20:45 +00:00
Reworked account management / selection UI
This commit is contained in:
parent
6c6d25fc63
commit
40ca3940f6
5 changed files with 89 additions and 29 deletions
|
@ -21,6 +21,7 @@ struct SettingsTabs: View {
|
||||||
@StateObject private var routerPath = RouterPath()
|
@StateObject private var routerPath = RouterPath()
|
||||||
|
|
||||||
@State private var addAccountSheetPresented = false
|
@State private var addAccountSheetPresented = false
|
||||||
|
@State private var isEditingAccount = false
|
||||||
|
|
||||||
@Binding var popToRootTab: Tab
|
@Binding var popToRootTab: Tab
|
||||||
|
|
||||||
|
@ -69,28 +70,48 @@ struct SettingsTabs: View {
|
||||||
private var accountsSection: some View {
|
private var accountsSection: some View {
|
||||||
Section("settings.section.accounts") {
|
Section("settings.section.accounts") {
|
||||||
ForEach(appAccountsManager.availableAccounts) { account in
|
ForEach(appAccountsManager.availableAccounts) { account in
|
||||||
AppAccountView(viewModel: .init(appAccount: account))
|
HStack {
|
||||||
|
if isEditingAccount {
|
||||||
|
Button {
|
||||||
|
Task {
|
||||||
|
await logoutAccount(account: account)
|
||||||
|
}
|
||||||
|
} label: {
|
||||||
|
Image(systemName: "trash")
|
||||||
|
.renderingMode(.template)
|
||||||
|
.tint(.red)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AppAccountView(viewModel: .init(appAccount: account))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.onDelete { indexSet in
|
.onDelete { indexSet in
|
||||||
if let index = indexSet.first {
|
if let index = indexSet.first {
|
||||||
let account = appAccountsManager.availableAccounts[index]
|
let account = appAccountsManager.availableAccounts[index]
|
||||||
if let token = account.oauthToken,
|
Task {
|
||||||
let sub = pushNotifications.subscriptions.first(where: { $0.account.token == token })
|
await logoutAccount(account: account)
|
||||||
{
|
|
||||||
Task {
|
|
||||||
let client = Client(server: account.server, oauthToken: token)
|
|
||||||
await TimelineCache.shared.clearCache(for: client)
|
|
||||||
await sub.deleteSubscription()
|
|
||||||
appAccountsManager.delete(account: account)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if !appAccountsManager.availableAccounts.isEmpty {
|
||||||
|
editAccountButton
|
||||||
|
}
|
||||||
addAccountButton
|
addAccountButton
|
||||||
}
|
}
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func logoutAccount(account: AppAccount) async {
|
||||||
|
if let token = account.oauthToken,
|
||||||
|
let sub = pushNotifications.subscriptions.first(where: { $0.account.token == token })
|
||||||
|
{
|
||||||
|
let client = Client(server: account.server, oauthToken: token)
|
||||||
|
await TimelineCache.shared.clearCache(for: client)
|
||||||
|
await sub.deleteSubscription()
|
||||||
|
appAccountsManager.delete(account: account)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ViewBuilder
|
@ViewBuilder
|
||||||
private var generalSection: some View {
|
private var generalSection: some View {
|
||||||
Section("settings.section.general") {
|
Section("settings.section.general") {
|
||||||
|
@ -215,6 +236,20 @@ struct SettingsTabs: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var editAccountButton: some View {
|
||||||
|
Button(role: isEditingAccount ? .none : .destructive) {
|
||||||
|
withAnimation {
|
||||||
|
isEditingAccount.toggle()
|
||||||
|
}
|
||||||
|
} label: {
|
||||||
|
if isEditingAccount {
|
||||||
|
Text("action.done")
|
||||||
|
} else {
|
||||||
|
Text("account.action.logout")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private var remoteLocalTimelinesView: some View {
|
private var remoteLocalTimelinesView: some View {
|
||||||
Form {
|
Form {
|
||||||
ForEach(preferences.remoteLocalTimelines, id: \.self) { server in
|
ForEach(preferences.remoteLocalTimelines, id: \.self) { server in
|
||||||
|
|
|
@ -6,12 +6,22 @@ import SwiftUI
|
||||||
@MainActor
|
@MainActor
|
||||||
public class AppAccountViewModel: ObservableObject {
|
public class AppAccountViewModel: ObservableObject {
|
||||||
private static var avatarsCache: [String: UIImage] = [:]
|
private static var avatarsCache: [String: UIImage] = [:]
|
||||||
|
private static var accountsCache: [String: Account] = [:]
|
||||||
|
|
||||||
var appAccount: AppAccount
|
var appAccount: AppAccount
|
||||||
let client: Client
|
let client: Client
|
||||||
let isCompact: Bool
|
let isCompact: Bool
|
||||||
|
|
||||||
@Published var account: Account?
|
@Published var account: Account? {
|
||||||
|
didSet {
|
||||||
|
if let account {
|
||||||
|
refreshAcct(account: account)
|
||||||
|
Task {
|
||||||
|
await refreshAvatar(account: account)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@Published var roundedAvatar: UIImage?
|
@Published var roundedAvatar: UIImage?
|
||||||
|
|
||||||
var acct: String {
|
var acct: String {
|
||||||
|
@ -30,23 +40,31 @@ public class AppAccountViewModel: ObservableObject {
|
||||||
|
|
||||||
func fetchAccount() async {
|
func fetchAccount() async {
|
||||||
do {
|
do {
|
||||||
account = try await client.get(endpoint: Accounts.verifyCredentials)
|
account = Self.accountsCache[appAccount.id]
|
||||||
if appAccount.accountName == nil, let account {
|
roundedAvatar = Self.avatarsCache[appAccount.id]
|
||||||
appAccount.accountName = "\(account.acct)@\(appAccount.server)"
|
|
||||||
try appAccount.save()
|
|
||||||
}
|
|
||||||
|
|
||||||
if let account {
|
account = try await client.get(endpoint: Accounts.verifyCredentials)
|
||||||
if let image = Self.avatarsCache[account.id] {
|
|
||||||
roundedAvatar = image
|
Self.accountsCache[appAccount.id] = account
|
||||||
} else if let (data, _) = try? await URLSession.shared.data(from: account.avatar),
|
|
||||||
let image = UIImage(data: data)?.roundedImage
|
|
||||||
{
|
|
||||||
roundedAvatar = image
|
|
||||||
Self.avatarsCache[account.id] = image
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch {}
|
} catch {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func refreshAcct(account: Account) {
|
||||||
|
do {
|
||||||
|
if appAccount.accountName == nil {
|
||||||
|
appAccount.accountName = "\(account.acct)@\(appAccount.server)"
|
||||||
|
try appAccount.save()
|
||||||
|
}
|
||||||
|
} catch { }
|
||||||
|
}
|
||||||
|
|
||||||
|
private func refreshAvatar(account: Account) async {
|
||||||
|
if roundedAvatar == nil,
|
||||||
|
let (data, _) = try? await URLSession.shared.data(from: account.avatar),
|
||||||
|
let image = UIImage(data: data)?.roundedImage {
|
||||||
|
roundedAvatar = image
|
||||||
|
Self.avatarsCache[account.id] = image
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,7 +54,8 @@ public struct AppAccountsSelectorView: View {
|
||||||
if let avatar = currentAccount.account?.avatar, !currentAccount.isLoadingAccount {
|
if let avatar = currentAccount.account?.avatar, !currentAccount.isLoadingAccount {
|
||||||
AvatarView(url: avatar, size: avatarSize)
|
AvatarView(url: avatar, size: avatarSize)
|
||||||
} else {
|
} else {
|
||||||
ProgressView()
|
AvatarView(url: nil, size: avatarSize)
|
||||||
|
.redacted(reason: .placeholder)
|
||||||
}
|
}
|
||||||
}.overlay(alignment: .topTrailing) {
|
}.overlay(alignment: .topTrailing) {
|
||||||
if !currentAccount.followRequests.isEmpty {
|
if !currentAccount.followRequests.isEmpty {
|
||||||
|
|
|
@ -39,10 +39,10 @@ public struct AvatarView: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public let url: URL
|
public let url: URL?
|
||||||
public let size: Size
|
public let size: Size
|
||||||
|
|
||||||
public init(url: URL, size: Size = .status) {
|
public init(url: URL?, size: Size = .status) {
|
||||||
self.url = url
|
self.url = url
|
||||||
self.size = size
|
self.size = size
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,8 @@ import Network
|
||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
public class CurrentAccount: ObservableObject {
|
public class CurrentAccount: ObservableObject {
|
||||||
|
static private var accountsCache: [String: Account] = [:]
|
||||||
|
|
||||||
@Published public private(set) var account: Account?
|
@Published public private(set) var account: Account?
|
||||||
@Published public private(set) var lists: [List] = []
|
@Published public private(set) var lists: [List] = []
|
||||||
@Published public private(set) var tags: [Tag] = []
|
@Published public private(set) var tags: [Tag] = []
|
||||||
|
@ -57,9 +59,13 @@ public class CurrentAccount: ObservableObject {
|
||||||
account = nil
|
account = nil
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
isLoadingAccount = true
|
account = Self.accountsCache[client.id]
|
||||||
|
if account == nil {
|
||||||
|
isLoadingAccount = true
|
||||||
|
}
|
||||||
account = try? await client.get(endpoint: Accounts.verifyCredentials)
|
account = try? await client.get(endpoint: Accounts.verifyCredentials)
|
||||||
isLoadingAccount = false
|
isLoadingAccount = false
|
||||||
|
Self.accountsCache[client.id] = account
|
||||||
}
|
}
|
||||||
|
|
||||||
public func fetchLists() async {
|
public func fetchLists() async {
|
||||||
|
|
Loading…
Reference in a new issue