From a2f5c7f5c44ce107129538be594116602b48059b Mon Sep 17 00:00:00 2001 From: mormaer Date: Sun, 10 Sep 2023 18:49:02 +0100 Subject: [PATCH] introduce `AppFlow` to support different application flows (#587) Co-authored-by: Sjmarf <78750526+Sjmarf@users.noreply.github.com> --- .../Views/Shared/Accounts/Accounts Page.swift | 92 +++++++++++-------- Mlem/Views/Tabs/Feeds/Feed Root.swift | 3 +- Mlem/Views/Tabs/Profile/User View.swift | 8 +- 3 files changed, 56 insertions(+), 47 deletions(-) diff --git a/Mlem/Views/Shared/Accounts/Accounts Page.swift b/Mlem/Views/Shared/Accounts/Accounts Page.swift index 22fc11f9d..0d6daedba 100644 --- a/Mlem/Views/Shared/Accounts/Accounts Page.swift +++ b/Mlem/Views/Shared/Accounts/Accounts Page.swift @@ -1,5 +1,5 @@ // -// Instance and Community List View.swift +// AccountsPage.swift // Mlem // // Created by David Bureš on 27.03.2022. @@ -11,20 +11,13 @@ import SwiftUI struct AccountsPage: View { @Dependency(\.accountsTracker) var accountsTracker + @Environment(\.setAppFlow) private var setFlow + @EnvironmentObject var appState: AppState - - @Environment(\.forceOnboard) var forceOnboard @State private var isShowingInstanceAdditionSheet: Bool = false - @State var selectedAccount: SavedAccount? - - @State var isShowingDeleteConfirm: Bool = false - - let onboarding: Bool - init(onboarding: Bool = false) { - self.onboarding = onboarding - } + @State var accountForDeletion: SavedAccount? @Environment(\.dismiss) var dismiss @@ -32,11 +25,8 @@ struct AccountsPage: View { let instances = Array(accountsTracker.accountsByInstance.keys).sorted() Group { - if onboarding || instances.isEmpty || isShowingInstanceAdditionSheet { - AddSavedInstanceView( - onboarding: onboarding, - currentAccount: $selectedAccount - ) + if instances.isEmpty || isShowingInstanceAdditionSheet { + AddSavedInstanceView(onboarding: false) } else { List { ForEach(instances, id: \.self) { instance in @@ -44,18 +34,30 @@ struct AccountsPage: View { ForEach(accountsTracker.accountsByInstance[instance] ?? []) { account in Button(account.username) { dismiss() - - // this tiny delay prevents the modal dismiss animation from being cancelled - DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) { - appState.setActiveAccount(account) - } + setFlow(using: account) } .swipeActions { Button("Remove", role: .destructive) { - accountsTracker.removeAccount(account: account, appState: appState, forceOnboard: forceOnboard) + dismiss() + accountsTracker.removeAccount(account: account) + if account == appState.currentActiveAccount { + // if we just deleted the current account we (currently!) have a decision to make + if let first = accountsTracker.savedAccounts.first { + // if we have another account available, go to that... + + // TODO: once onboarding is updated to support showing a user's + // current accounts, we can scrap this and always go to onboarding. + // This leaves the decision of which account to enter in the + // user's hands, as opposed to us picking the first account with `.first`. + setFlow(using: first) + } else { + // no accounts, so go to onboarding + setFlow(using: nil) + } + } } } - .foregroundColor(appState.currentActiveAccount == account ? .secondary : .primary) + .foregroundColor(color(for: account)) } } } @@ -63,32 +65,44 @@ struct AccountsPage: View { Button { isShowingInstanceAdditionSheet = true } label: { - Text("Add Account") + Label("Add Account", systemImage: AppConstants.switchUserSymbolName) } .accessibilityLabel("Add a new account.") - Button(role: .destructive) { - isShowingDeleteConfirm = true - } label: { - Label("Delete Current Account", systemImage: "trash") - .foregroundColor(.red) + if let account = appState.currentActiveAccount { + Button(role: .destructive) { + accountForDeletion = account + } label: { + Label("Delete Current Account", systemImage: "trash") + .foregroundColor(.red) + } } } } } .hoistNavigation(dismiss: dismiss) - .onAppear { - selectedAccount = appState.currentActiveAccount - } - .onChange(of: selectedAccount) { account in - guard let account else { return } - appState.setActiveAccount(account) - } .sheet(isPresented: $isShowingInstanceAdditionSheet) { - AddSavedInstanceView(onboarding: false, currentAccount: $selectedAccount) + AddSavedInstanceView(onboarding: false) } - .sheet(isPresented: $isShowingDeleteConfirm) { - DeleteAccountView() + .sheet(item: $accountForDeletion) { account in + DeleteAccountView(account: account) + } + } + + private func color(for account: SavedAccount) -> Color { + guard let currentAccount = appState.currentActiveAccount else { return .primary } + return account == currentAccount ? .secondary : .primary + } + + private func setFlow(using account: SavedAccount?) { + // this tiny delay prevents the modal dismiss animation from being cancelled + DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) { + if let account { + setFlow(.account(account)) + return + } + + setFlow(.onboarding) } } } diff --git a/Mlem/Views/Tabs/Feeds/Feed Root.swift b/Mlem/Views/Tabs/Feeds/Feed Root.swift index 28dc37354..ae069d62a 100644 --- a/Mlem/Views/Tabs/Feeds/Feed Root.swift +++ b/Mlem/Views/Tabs/Feeds/Feed Root.swift @@ -35,7 +35,6 @@ struct FeedRoot: View { ScrollViewReader { proxy in NavigationSplitView(columnVisibility: $columnVisibility) { CommunityListView(selectedCommunity: $rootDetails) - .id(appState.currentActiveAccount.id) } detail: { NavigationStack(path: $feedRouter.path) { if let rootDetails { @@ -54,7 +53,7 @@ struct FeedRoot: View { Text("Please select a community") } } - .id((rootDetails?.id ?? 0) + appState.currentActiveAccount.id) + .id(rootDetails?.id ?? 0) } .environment(\.scrollViewProxy, proxy) } diff --git a/Mlem/Views/Tabs/Profile/User View.swift b/Mlem/Views/Tabs/Profile/User View.swift index 215751e8a..0538c5f17 100644 --- a/Mlem/Views/Tabs/Profile/User View.swift +++ b/Mlem/Views/Tabs/Profile/User View.swift @@ -200,7 +200,7 @@ struct UserView: View { } private func isShowingOwnProfile() -> Bool { - userID == appState.currentActiveAccount.id + appState.isCurrentAccountId(userID) } @MainActor @@ -405,11 +405,7 @@ struct UserViewPreview: PreviewProvider { person: generatePreviewUser(name: "actualUsername", displayName: "PreferredUsername", userType: .normal), counts: APIPersonAggregates(id: 123, personId: 123, postCount: 123, postScore: 567, commentCount: 14, commentScore: 974) ) - ).environmentObject(AppState(defaultAccount: SavedAccount( - id: 0, - instanceLink: URL(string: "https://google.com")!, - accessToken: "", - username: "Preview User"), selectedAccount: Binding.constant(nil))) + ).environmentObject(AppState()) } }