From 28ef54fa4b59ac90b347578e0767d96aff5c649d Mon Sep 17 00:00:00 2001 From: Thomas Ricouard Date: Sun, 1 Jan 2023 09:19:00 +0100 Subject: [PATCH] Improve logged out experience --- .../App/Tabs/Settings/AddAccountsView.swift | 12 ++++++ .../App/Tabs/Settings/SettingsTab.swift | 5 +++ IceCubesApp/App/Tabs/TimelineTab.swift | 17 +++++++- .../Account/AccountDetailViewModel.swift | 13 +++--- Packages/Env/Sources/Env/Routeur.swift | 41 ++++++++++++++----- 5 files changed, 71 insertions(+), 17 deletions(-) diff --git a/IceCubesApp/App/Tabs/Settings/AddAccountsView.swift b/IceCubesApp/App/Tabs/Settings/AddAccountsView.swift index 31517f29..ebe30ecc 100644 --- a/IceCubesApp/App/Tabs/Settings/AddAccountsView.swift +++ b/IceCubesApp/App/Tabs/Settings/AddAccountsView.swift @@ -20,6 +20,9 @@ struct AddAccountView: View { @State private var isSigninIn = false @State private var signInClient: Client? @State private var instances: [InstanceSocial] = [] + @State private var instanceFetchError: String? + + @FocusState private var isInstanceURLFieldFocused: Bool var body: some View { NavigationStack { @@ -29,6 +32,10 @@ struct AddAccountView: View { .keyboardType(.URL) .textContentType(.URL) .textInputAutocapitalization(.never) + .focused($isInstanceURLFieldFocused) + if let instanceFetchError { + Text(instanceFetchError) + } if let instance { Button { isSigninIn = true @@ -53,12 +60,14 @@ struct AddAccountView: View { .navigationBarTitleDisplayMode(.inline) .scrollContentBackground(.hidden) .background(theme.secondaryBackgroundColor) + .scrollDismissesKeyboard(.immediately) .toolbar { ToolbarItem(placement: .navigationBarLeading) { Button("Cancel", action: { dismiss() }) } } .onAppear { + isInstanceURLFieldFocused = true let client = InstanceSocialClient() Task { self.instances = await client.fetchInstances() @@ -69,6 +78,9 @@ struct AddAccountView: View { Task { do { self.instance = try await client.get(endpoint: Instances.instance) + } catch _ as DecodingError { + self.instance = nil + self.instanceFetchError = "This instance is not currently supported." } catch { self.instance = nil } diff --git a/IceCubesApp/App/Tabs/Settings/SettingsTab.swift b/IceCubesApp/App/Tabs/Settings/SettingsTab.swift index 3bce9d11..c8893ba9 100644 --- a/IceCubesApp/App/Tabs/Settings/SettingsTab.swift +++ b/IceCubesApp/App/Tabs/Settings/SettingsTab.swift @@ -12,6 +12,8 @@ struct SettingsTabs: View { @EnvironmentObject private var appAccountsManager: AppAccountsManager @EnvironmentObject private var theme: Theme + @StateObject private var routeurPath = RouterPath() + @State private var addAccountSheetPresented = false var body: some View { @@ -27,6 +29,9 @@ struct SettingsTabs: View { .navigationTitle(Text("Settings")) .navigationBarTitleDisplayMode(.inline) } + .onAppear { + routeurPath.client = client + } .task { if appAccountsManager.currentAccount.oauthToken != nil { await currentInstance.fetchCurrentInstance() diff --git a/IceCubesApp/App/Tabs/TimelineTab.swift b/IceCubesApp/App/Tabs/TimelineTab.swift index 58e59327..eab33ab8 100644 --- a/IceCubesApp/App/Tabs/TimelineTab.swift +++ b/IceCubesApp/App/Tabs/TimelineTab.swift @@ -11,6 +11,7 @@ struct TimelineTab: View { @Binding var popToRootTab: Tab @State private var timeline: TimelineFilter = .home @State private var scrollToTopSignal: Int = 0 + @State private var isAddAccountSheetDisplayed = false var body: some View { NavigationStack(path: $routeurPath.path) { @@ -23,6 +24,10 @@ struct TimelineTab: View { ToolbarItem(placement: .navigationBarLeading) { timelineFilterButton } + } else { + ToolbarItem(placement: .navigationBarTrailing) { + addAccountButton + } } } .id(currentAccount.account?.id) @@ -56,6 +61,16 @@ struct TimelineTab: View { } label: { Image(systemName: "line.3.horizontal.decrease.circle") } - + } + + private var addAccountButton: some View { + Button { + isAddAccountSheetDisplayed = true + } label: { + Image(systemName: "person.badge.plus") + } + .sheet(isPresented: $isAddAccountSheetDisplayed) { + AddAccountView() + } } } diff --git a/Packages/Account/Sources/Account/AccountDetailViewModel.swift b/Packages/Account/Sources/Account/AccountDetailViewModel.swift index 16c9f575..409c8141 100644 --- a/Packages/Account/Sources/Account/AccountDetailViewModel.swift +++ b/Packages/Account/Sources/Account/AccountDetailViewModel.swift @@ -98,21 +98,22 @@ class AccountDetailViewModel: ObservableObject, StatusesFetcher { guard let client else { return } do { async let account: Account = client.get(endpoint: Accounts.accounts(id: accountId)) - async let followedTags: [Tag] = client.get(endpoint: Accounts.followedTags) - async let relationships: [Relationshionship] = client.get(endpoint: Accounts.relationships(ids: [accountId])) async let featuredTags: [FeaturedTag] = client.get(endpoint: Accounts.featuredTags(id: accountId)) - async let familliarFollowers: [FamilliarAccounts] = client.get(endpoint: Accounts.familiarFollowers(withAccount: accountId)) let loadedAccount = try await account self.account = loadedAccount self.featuredTags = try await featuredTags self.featuredTags.sort { $0.statusesCountInt > $1.statusesCountInt } self.fields = loadedAccount.fields if isCurrentUser { + async let followedTags: [Tag] = client.get(endpoint: Accounts.followedTags) self.followedTags = try await followedTags } else { - let relationships = try await relationships - self.relationship = relationships.first - self.familliarFollowers = try await familliarFollowers.first?.accounts ?? [] + if client.isAuth { + async let relationships: [Relationshionship] = client.get(endpoint: Accounts.relationships(ids: [accountId])) + async let familliarFollowers: [FamilliarAccounts] = client.get(endpoint: Accounts.familiarFollowers(withAccount: accountId)) + self.relationship = try await relationships.first + self.familliarFollowers = try await familliarFollowers.first?.accounts ?? [] + } } accountState = .data(account: loadedAccount) } catch { diff --git a/Packages/Env/Sources/Env/Routeur.swift b/Packages/Env/Sources/Env/Routeur.swift index dcf11a58..0ba07e1c 100644 --- a/Packages/Env/Sources/Env/Routeur.swift +++ b/Packages/Env/Sources/Env/Routeur.swift @@ -55,16 +55,7 @@ public class RouterPath: ObservableObject { navigate(to: .statusDetail(id: String(id))) } else { Task { - let results: SearchResults? = try? await client.get(endpoint: Search.search(query: url.absoluteString, - type: "statuses", - offset: nil, - following: nil), - forceVersion: .v2) - if let status = results?.statuses.first { - navigate(to: .statusDetail(id: status.id)) - } else { - await UIApplication.shared.open(url) - } + await navigateToStatusFrom(url: url) } } return .handled @@ -80,4 +71,34 @@ public class RouterPath: ObservableObject { } return .systemAction } + + public func navigateToStatusFrom(url: URL) async { + guard let client else { return } + Task { + let results: SearchResults? = try? await client.get(endpoint: Search.search(query: url.absoluteString, + type: "statuses", + offset: nil, + following: nil), + forceVersion: .v2) + if let status = results?.statuses.first { + navigate(to: .statusDetail(id: status.id)) + } else { + await UIApplication.shared.open(url) + } + } + } + + public func navigateToAccountFrom(acct: String) async { + guard let client else { return } + Task { + let results: SearchResults? = try? await client.get(endpoint: Search.search(query: acct, + type: "accounts", + offset: nil, + following: nil), + forceVersion: .v2) + if let account = results?.accounts.first { + navigate(to: .accountDetailWithAccount(account: account)) + } + } + } }