diff --git a/Mastodon/Protocol/Provider/DataSourceFacade+SearchHistory.swift b/Mastodon/Protocol/Provider/DataSourceFacade+SearchHistory.swift index 5fd9c1681..b4b9613cb 100644 --- a/Mastodon/Protocol/Provider/DataSourceFacade+SearchHistory.swift +++ b/Mastodon/Protocol/Provider/DataSourceFacade+SearchHistory.swift @@ -27,7 +27,7 @@ extension DataSourceFacade { hashtag: nil ) - try? FileManager.default.addSearchItem(searchEntry) + try? FileManager.default.addSearchItem(searchEntry, for: provider.authContext.mastodonAuthenticationBox) case .hashtag(let tag): let now = Date() @@ -39,7 +39,7 @@ extension DataSourceFacade { hashtag: tag ) - try? FileManager.default.addSearchItem(searchEntry) + try? FileManager.default.addSearchItem(searchEntry, for: provider.authContext.mastodonAuthenticationBox) case .status: break case .user(_): diff --git a/Mastodon/Scene/Onboarding/Login/MastodonLoginViewController.swift b/Mastodon/Scene/Onboarding/Login/MastodonLoginViewController.swift index 97d26a968..3d51393b9 100644 --- a/Mastodon/Scene/Onboarding/Login/MastodonLoginViewController.swift +++ b/Mastodon/Scene/Onboarding/Login/MastodonLoginViewController.swift @@ -139,30 +139,23 @@ class MastodonLoginViewController: UIViewController, NeedsDependency { @objc func login() { guard let server = viewModel.selectedServer else { return } - + authenticationViewModel - .authenticated - .asyncMap { domain, user -> Result in - do { - let result = try await self.context.authenticationService.activeMastodonUser(domain: domain, userID: user.id) - return .success(user) - } catch { - return .failure(error) - } - } - .receive(on: DispatchQueue.main) - .sink { [weak self] result in - guard let self = self else { return } - switch result { - case .failure(let error): - assertionFailure(error.localizedDescription) - case .success(let account): - FileManager.default.store(account: account, forUserID: account.id) - self.coordinator.setup() + .authenticated.sink { (domain, account) in + Task { + do { + _ = try await self.context.authenticationService.activeMastodonUser(domain: domain, userID: account.id) + FileManager.default.store(account: account, forUserID: MastodonUserIdentifier(domain: domain, userID: account.id)) + Task { @MainActor in + self.coordinator.setup() + } + } catch { + assertionFailure(error.localizedDescription) + } } } .store(in: &disposeBag) - + authenticationViewModel.isAuthenticating.send(true) context.apiService.createApplication(domain: server.domain) .tryMap { response -> AuthenticationViewModel.AuthenticateInfo in diff --git a/Mastodon/Scene/Search/SearchDetail/SearchHistory/SearchHistoryViewController.swift b/Mastodon/Scene/Search/SearchDetail/SearchHistory/SearchHistoryViewController.swift index afddfbbac..a01100be7 100644 --- a/Mastodon/Scene/Search/SearchDetail/SearchHistory/SearchHistoryViewController.swift +++ b/Mastodon/Scene/Search/SearchDetail/SearchHistory/SearchHistoryViewController.swift @@ -50,8 +50,7 @@ extension SearchHistoryViewController { } override func viewWillAppear(_ animated: Bool) { - let userID = authContext.mastodonAuthenticationBox.userID - viewModel.items = (try? FileManager.default.searchItems(forUser: userID)) ?? [] + viewModel.items = (try? FileManager.default.searchItems(for: authContext.mastodonAuthenticationBox)) ?? [] } } @@ -103,9 +102,7 @@ extension SearchHistoryViewController: SearchHistorySectionHeaderCollectionReusa _ searchHistorySectionHeaderCollectionReusableView: SearchHistorySectionHeaderCollectionReusableView, clearButtonDidPressed button: UIButton ) { - let userID = authContext.mastodonAuthenticationBox.userID - - FileManager.default.removeSearchHistory(forUser: userID) + FileManager.default.removeSearchHistory(for: authContext.mastodonAuthenticationBox) viewModel.items = [] } } @@ -113,7 +110,6 @@ extension SearchHistoryViewController: SearchHistorySectionHeaderCollectionReusa //MARK: - SearchResultOverviewCoordinatorDelegate extension SearchHistoryViewController: SearchResultOverviewCoordinatorDelegate { func newSearchHistoryItemAdded(_ coordinator: SearchResultOverviewCoordinator) { - let userID = authContext.mastodonAuthenticationBox.userID - viewModel.items = (try? FileManager.default.searchItems(forUser: userID)) ?? [] + viewModel.items = (try? FileManager.default.searchItems(for: authContext.mastodonAuthenticationBox)) ?? [] } } diff --git a/Mastodon/Scene/Search/SearchDetail/SearchHistory/SearchHistoryViewModel.swift b/Mastodon/Scene/Search/SearchDetail/SearchHistory/SearchHistoryViewModel.swift index 63f1cd0e8..1a1e38365 100644 --- a/Mastodon/Scene/Search/SearchDetail/SearchHistory/SearchHistoryViewModel.swift +++ b/Mastodon/Scene/Search/SearchDetail/SearchHistory/SearchHistoryViewModel.swift @@ -24,7 +24,7 @@ final class SearchHistoryViewModel { init(context: AppContext, authContext: AuthContext) { self.context = context self.authContext = authContext - self.items = (try? FileManager.default.searchItems(forUser: authContext.mastodonAuthenticationBox.userID)) ?? [] + self.items = (try? FileManager.default.searchItems(for: authContext.mastodonAuthenticationBox)) ?? [] } } diff --git a/MastodonSDK/Sources/MastodonCore/AuthenticationServiceProvider.swift b/MastodonSDK/Sources/MastodonCore/AuthenticationServiceProvider.swift index 1f120e522..f5a9c5ea8 100644 --- a/MastodonSDK/Sources/MastodonCore/AuthenticationServiceProvider.swift +++ b/MastodonSDK/Sources/MastodonCore/AuthenticationServiceProvider.swift @@ -116,7 +116,7 @@ public extension AuthenticationServiceProvider { userID: authentication.userID, authorization: Mastodon.API.OAuth.Authorization(accessToken: authentication.userAccessToken)).value else { continue } - FileManager.default.store(account: account, forUserID: authentication.userID) + FileManager.default.store(account: account, forUserID: authentication.userIdentifier()) } NotificationCenter.default.post(name: .userFetched, object: nil) diff --git a/MastodonSDK/Sources/MastodonCore/MastodonAuthentication.swift b/MastodonSDK/Sources/MastodonCore/MastodonAuthentication.swift index 317e9ad52..2ed7542df 100644 --- a/MastodonSDK/Sources/MastodonCore/MastodonAuthentication.swift +++ b/MastodonSDK/Sources/MastodonCore/MastodonAuthentication.swift @@ -100,11 +100,19 @@ public struct MastodonAuthentication: Codable, Hashable { } public func account() -> Mastodon.Entity.Account? { - let account = FileManager.default.accounts(forUserID: userID).first(where: { $0.id == userID }) + + let account = FileManager + .default + .accounts(for: self.userIdentifier()) + .first(where: { $0.id == userID }) return account } + public func userIdentifier() -> MastodonUserIdentifier { + MastodonUserIdentifier(domain: domain, userID: userID) + } + func updating(instance: Instance) -> Self { copy(instanceObjectIdURI: instance.objectID.uriRepresentation()) } diff --git a/MastodonSDK/Sources/MastodonCore/Persistence/FileManager+Account.swift b/MastodonSDK/Sources/MastodonCore/Persistence/FileManager+Account.swift index 1df3aef1e..d9b21600a 100644 --- a/MastodonSDK/Sources/MastodonCore/Persistence/FileManager+Account.swift +++ b/MastodonSDK/Sources/MastodonCore/Persistence/FileManager+Account.swift @@ -3,10 +3,9 @@ import Foundation import MastodonSDK -extension FileManager { - public func store(account: Mastodon.Entity.Account, forUserID userID: String) { - // store accounts for each loged in user - var accounts = accounts(forUserID: userID) +public extension FileManager { + func store(account: Mastodon.Entity.Account, forUserID userID: UserIdentifier) { + var accounts = accounts(for: userID) if let index = accounts.firstIndex(of: account) { accounts.remove(at: index) @@ -17,10 +16,10 @@ extension FileManager { storeJSON(accounts, userID: userID) } - public func accounts(forUserID userID: String) -> [Mastodon.Entity.Account] { + func accounts(for userId: UserIdentifier) -> [Mastodon.Entity.Account] { guard let documentsDirectory else { return [] } - let accountPath = Persistence.accounts(userID: userID).filepath(baseURL: documentsDirectory) + let accountPath = Persistence.accounts(userId).filepath(baseURL: documentsDirectory) guard let data = try? Data(contentsOf: accountPath) else { return [] } @@ -35,8 +34,10 @@ extension FileManager { } } +} - private func storeJSON(_ encodable: Encodable, userID: String) { +private extension FileManager { + private func storeJSON(_ encodable: Encodable, userID: UserIdentifier) { guard let documentsDirectory else { return } let jsonEncoder = JSONEncoder() @@ -44,7 +45,7 @@ extension FileManager { do { let data = try jsonEncoder.encode(encodable) - let accountsPath = Persistence.accounts(userID: userID).filepath(baseURL: documentsDirectory) + let accountsPath = Persistence.accounts( userID).filepath(baseURL: documentsDirectory) try data.write(to: accountsPath) } catch { debugPrint(error.localizedDescription) diff --git a/MastodonSDK/Sources/MastodonCore/Persistence/FileManager+SearchHistory.swift b/MastodonSDK/Sources/MastodonCore/Persistence/FileManager+SearchHistory.swift index 91c689f6b..a63515cb1 100644 --- a/MastodonSDK/Sources/MastodonCore/Persistence/FileManager+SearchHistory.swift +++ b/MastodonSDK/Sources/MastodonCore/Persistence/FileManager+SearchHistory.swift @@ -1,17 +1,12 @@ // Copyright © 2023 Mastodon gGmbH. All rights reserved. import Foundation -import MastodonCore -extension FileManager { - public func searchItems(forUser userID: String) throws -> [Persistence.SearchHistory.Item] { - return try searchItems().filter { $0.userID == userID } - } - - public func searchItems() throws -> [Persistence.SearchHistory.Item] { +public extension FileManager { + func searchItems(for userId: UserIdentifier) throws -> [Persistence.SearchHistory.Item] { guard let documentsDirectory else { return [] } - let searchHistoryPath = Persistence.searchHistory.filepath(baseURL: documentsDirectory) + let searchHistoryPath = Persistence.searchHistory(userId).filepath(baseURL: documentsDirectory) guard let data = try? Data(contentsOf: searchHistoryPath) else { return [] } @@ -28,16 +23,23 @@ extension FileManager { } } - public func addSearchItem(_ newSearchItem: Persistence.SearchHistory.Item) throws { - var searchItems = (try? searchItems()) ?? [] + func addSearchItem(_ newSearchItem: Persistence.SearchHistory.Item, for userId: UserIdentifier) throws { + var searchItems = (try? searchItems(for: userId)) ?? [] if let index = searchItems.firstIndex(of: newSearchItem) { searchItems.remove(at: index) } - + searchItems.append(newSearchItem) - storeJSON(searchItems, .searchHistory) + storeJSON(searchItems, .searchHistory(userId)) + } + + func removeSearchHistory(for userId: UserIdentifier) { + let searchItems = (try? searchItems(for: userId)) ?? [] + let newSearchItems = searchItems.filter { $0.userID != userId.userID } + + storeJSON(newSearchItems, .searchHistory(userId)) } private func storeJSON(_ encodable: Encodable, _ persistence: Persistence) { @@ -53,13 +55,5 @@ extension FileManager { } catch { debugPrint(error.localizedDescription) } - - } - - public func removeSearchHistory(forUser userID: String) { - let searchItems = (try? searchItems()) ?? [] - let newSearchItems = searchItems.filter { $0.userID != userID } - - storeJSON(newSearchItems, .searchHistory) } } diff --git a/MastodonSDK/Sources/MastodonCore/Persistence/Persistence.swift b/MastodonSDK/Sources/MastodonCore/Persistence/Persistence.swift index ffb45a26f..4326b6e47 100644 --- a/MastodonSDK/Sources/MastodonCore/Persistence/Persistence.swift +++ b/MastodonSDK/Sources/MastodonCore/Persistence/Persistence.swift @@ -9,11 +9,11 @@ import Foundation public enum Persistence { - case searchHistory + case searchHistory(UserIdentifier) case homeTimeline(UserIdentifier) case notificationsMentions(UserIdentifier) case notificationsAll(UserIdentifier) - case accounts(userID: String) + case accounts(UserIdentifier) private func uniqueUserDomainIdentifier(for userIdentifier: UserIdentifier) -> String { "\(userIdentifier.userID)@\(userIdentifier.domain)" @@ -21,16 +21,16 @@ public enum Persistence { private var filename: String { switch self { - case .searchHistory: - return "search_history" // todo: @zeitschlag should this be user-scoped as well? + case .searchHistory(let userIdentifier): + return "search_history_\(uniqueUserDomainIdentifier(for: userIdentifier))" // todo: @zeitschlag should this be user-scoped as well? case let .homeTimeline(userIdentifier): - return "home_timeline_\(userIdentifier.uniqueUserDomainIdentifier)" + return "home_timeline_\(uniqueUserDomainIdentifier(for: userIdentifier))" case let .notificationsMentions(userIdentifier): return "notifications_mentions_\(userIdentifier.uniqueUserDomainIdentifier)" case let .notificationsAll(userIdentifier): return "notifications_all_\(uniqueUserDomainIdentifier(for: userIdentifier))" - case .accounts(let userID): - return "account_\(userID)" + case .accounts(let userIdentifier): + return "account_\(uniqueUserDomainIdentifier(for: userIdentifier))" } }