Use AnyAccount instead of EitherAccount for compose autocomplete

This commit is contained in:
Shadowfacts 2022-09-15 21:05:18 -04:00
parent 8f8d50efbd
commit 80c4fcce82
2 changed files with 21 additions and 55 deletions

View File

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

View File

@ -12,13 +12,13 @@ import WebURLFoundationExtras
private let emojiRegex = try! NSRegularExpression(pattern: ":(\\w+):", options: []) private let emojiRegex = try! NSRegularExpression(pattern: ":(\\w+):", options: [])
struct AccountDisplayNameLabel<Account: AccountProtocol>: View { struct AccountDisplayNameLabel: View {
let account: Account let account: any AccountProtocol
let fontSize: Int let fontSize: Int
@State var text: Text @State var text: Text
@State var emojiRequests = [ImageCache.Request]() @State var emojiRequests = [ImageCache.Request]()
init(account: Account, fontSize: Int) { init(account: any AccountProtocol, fontSize: Int) {
self.account = account self.account = account
self.fontSize = fontSize self.fontSize = fontSize
let name = account.displayName.isEmpty ? account.username : account.displayName let name = account.displayName.isEmpty ? account.username : account.displayName