User UserIdentification for search and accounts (IOS-192)

Thanks to @kimar!
This commit is contained in:
Nathan Mattes 2023-12-28 22:39:24 +01:00
parent 2a14e293e9
commit 460ede4852
9 changed files with 59 additions and 67 deletions

View File

@ -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(_):

View File

@ -139,30 +139,23 @@ class MastodonLoginViewController: UIViewController, NeedsDependency {
@objc func login() {
guard let server = viewModel.selectedServer else { return }
authenticationViewModel
.authenticated
.asyncMap { domain, user -> Result<Mastodon.Entity.Account, Error> 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

View File

@ -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)) ?? []
}
}

View File

@ -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)) ?? []
}
}

View File

@ -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)

View File

@ -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())
}

View File

@ -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)

View File

@ -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)
}
}

View File

@ -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))"
}
}