diff --git a/Tusker/Screens/Compose/ComposeAutocompleteView.swift b/Tusker/Screens/Compose/ComposeAutocompleteView.swift index b7b62fba..fca50d0a 100644 --- a/Tusker/Screens/Compose/ComposeAutocompleteView.swift +++ b/Tusker/Screens/Compose/ComposeAutocompleteView.swift @@ -48,7 +48,7 @@ struct ComposeAutocompleteMentionsView: View { @ObservedObject private var preferences = Preferences.shared // can't use AccountProtocol because of associated type requirements - @State private var accounts: [EitherAccount] = [] + @State private var accounts: [AnyAccount] = [] @State private var searchRequest: URLSessionTask? @@ -56,26 +56,20 @@ struct ComposeAutocompleteMentionsView: View { ScrollView(.horizontal) { // can't use LazyHStack because changing the contents of the ForEach causes the ScrollView to hang HStack(spacing: 8) { - ForEach(accounts, id: \.id) { (account) in + ForEach(accounts, id: \.value.id) { (account) in Button { - uiState.currentInput?.autocomplete(with: "@\(account.acct)") + uiState.currentInput?.autocomplete(with: "@\(account.value.acct)") } label: { HStack(spacing: 4) { - ComposeAvatarImageView(url: account.avatar) + ComposeAvatarImageView(url: account.value.avatar) .frame(width: 30, height: 30) .cornerRadius(preferences.avatarStyle.cornerRadiusFraction * 30) VStack(alignment: .leading) { - switch account { - case let .pachyderm(underlying): - AccountDisplayNameLabel(account: underlying, fontSize: 14) - .foregroundColor(Color(UIColor.label)) - case let .coreData(underlying): - AccountDisplayNameLabel(account: underlying, fontSize: 14) - .foregroundColor(Color(UIColor.label)) - } + AccountDisplayNameLabel(account: account.value, fontSize: 14) + .foregroundColor(Color(UIColor.label)) - Text(verbatim: "@\(account.acct)") + Text(verbatim: "@\(account.value.acct)") .font(.system(size: 12)) .foregroundColor(Color(UIColor.label)) } @@ -110,7 +104,7 @@ struct ComposeAutocompleteMentionsView: View { request.predicate = NSPredicate(format: "displayName LIKE %@ OR acct LIKE %@", wildcardedQuery, wildcardedQuery) if let results = try? mastodonController.persistentContainer.viewContext.fetch(request) { - loadAccounts(results.map { .coreData($0) }, query: query) + loadAccounts(results.map { .init(value: $0) }, query: query) } } @@ -131,27 +125,27 @@ struct ComposeAutocompleteMentionsView: View { DispatchQueue.main.async { // if the query has changed, don't bother loading the now-outdated results if case .mention(query) = uiState.autocompleteState { - self.loadAccounts(accounts.map { .pachyderm($0) }, query: query) + self.loadAccounts(accounts.map { .init(value: $0) }, query: query) } } } } - private func loadAccounts(_ accounts: [EitherAccount], query: String) { + private func loadAccounts(_ accounts: [AnyAccount], query: String) { // when sorting account suggestions, ignore the domain component of the acct unless the user is typing it themself let ignoreDomain = !query.contains("@") self.accounts = - accounts.map { (account: EitherAccount) -> (EitherAccount, (matched: Bool, score: Int)) in - let fuzzyStr = ignoreDomain ? String(account.acct.split(separator: "@").first!) : account.acct + accounts.map { (account) -> (AnyAccount, (matched: Bool, score: Int)) in + let fuzzyStr = ignoreDomain ? String(account.value.acct.split(separator: "@").first!) : account.value.acct let res = (account, FuzzyMatcher.match(pattern: query, str: fuzzyStr)) return res } .filter(\.1.matched) - .map { (account, res) -> (EitherAccount, Int) in + .map { (account, res) -> (AnyAccount, Int) in // give higher weight to accounts that the user follows or is followed by var score = res.score - if let relationship = mastodonController.persistentContainer.relationship(forAccount: account.id) { + if let relationship = mastodonController.persistentContainer.relationship(forAccount: account.value.id) { if relationship.following { score += 3 } @@ -165,39 +159,11 @@ struct ComposeAutocompleteMentionsView: View { .map(\.0) } - private enum EitherAccount: Equatable { - case pachyderm(Account) - case coreData(AccountMO) + private struct AnyAccount: Equatable { + let value: any AccountProtocol - var id: String { - switch self { - case let .pachyderm(account): - return account.id - case let .coreData(account): - return account.id - } - } - - var acct: String { - switch self { - case let .pachyderm(account): - return account.acct - case let .coreData(account): - return account.acct - } - } - - var avatar: URL? { - switch self { - case let .pachyderm(account): - return account.avatar - case let .coreData(account): - return account.avatar - } - } - - static func ==(lhs: EitherAccount, rhs: EitherAccount) -> Bool { - return lhs.id == rhs.id + static func ==(lhs: AnyAccount, rhs: AnyAccount) -> Bool { + return lhs.value.id == rhs.value.id } } } diff --git a/Tusker/Views/AccountDisplayNameLabel.swift b/Tusker/Views/AccountDisplayNameLabel.swift index c5fd76a6..a58f8915 100644 --- a/Tusker/Views/AccountDisplayNameLabel.swift +++ b/Tusker/Views/AccountDisplayNameLabel.swift @@ -12,13 +12,13 @@ import WebURLFoundationExtras private let emojiRegex = try! NSRegularExpression(pattern: ":(\\w+):", options: []) -struct AccountDisplayNameLabel: View { - let account: Account +struct AccountDisplayNameLabel: View { + let account: any AccountProtocol let fontSize: Int @State var text: Text @State var emojiRequests = [ImageCache.Request]() - init(account: Account, fontSize: Int) { + init(account: any AccountProtocol, fontSize: Int) { self.account = account self.fontSize = fontSize let name = account.displayName.isEmpty ? account.username : account.displayName