This commit is contained in:
Justin Mazzocchi 2021-02-07 17:46:51 -08:00
parent 6bbfb2d06e
commit b32a85aebc
No known key found for this signature in database
GPG Key ID: E223E6937AAFB01C
16 changed files with 86 additions and 52 deletions

View File

@ -544,7 +544,7 @@ public extension ContentDatabase {
accountIds.firstIndex(of: $0.record.id) ?? 0 accountIds.firstIndex(of: $0.record.id) ?? 0
< accountIds.firstIndex(of: $1.record.id) ?? 0 < accountIds.firstIndex(of: $1.record.id) ?? 0
} }
.map { CollectionItem.account(.init(info: $0), .withoutNote) } .map { CollectionItem.account(.init(info: $0), .withoutNote, nil) } // TODO: revisit
if let limit = limit, accounts.count >= limit { if let limit = limit, accounts.count >= limit {
accounts.append(.moreResults(.init(scope: .accounts))) accounts.append(.moreResults(.init(scope: .accounts)))
@ -568,7 +568,8 @@ public extension ContentDatabase {
CollectionItem.status( CollectionItem.status(
.init(info: $0), .init(info: $0),
.init(showContentToggled: $0.showContentToggled, .init(showContentToggled: $0.showContentToggled,
showAttachmentsToggled: $0.showAttachmentsToggled)) showAttachmentsToggled: $0.showAttachmentsToggled),
$0.reblogRelationship ?? $0.relationship)
} }
if let limit = limit, statuses.count >= limit { if let limit = limit, statuses.count >= limit {

View File

@ -49,7 +49,8 @@ extension ContextItemsInfo {
showAttachmentsToggled: statusInfo.showAttachmentsToggled, showAttachmentsToggled: statusInfo.showAttachmentsToggled,
isContextParent: isContextParent, isContextParent: isContextParent,
isReplyInContext: isReplyInContext, isReplyInContext: isReplyInContext,
hasReplyFollowing: hasReplyFollowing)) hasReplyFollowing: hasReplyFollowing),
statusInfo.reblogRelationship ?? statusInfo.relationship)
} }
} }
.map { CollectionSection(items: $0) } .map { CollectionSection(items: $0) }

View File

@ -2,12 +2,15 @@
import Foundation import Foundation
import GRDB import GRDB
import Mastodon
struct StatusInfo: Codable, Hashable, FetchableRecord { struct StatusInfo: Codable, Hashable, FetchableRecord {
let record: StatusRecord let record: StatusRecord
let accountInfo: AccountInfo let accountInfo: AccountInfo
let relationship: Relationship?
let reblogAccountInfo: AccountInfo? let reblogAccountInfo: AccountInfo?
let reblogRecord: StatusRecord? let reblogRecord: StatusRecord?
let reblogRelationship: Relationship?
let showContentToggle: StatusShowContentToggle? let showContentToggle: StatusShowContentToggle?
let reblogShowContentToggle: StatusShowContentToggle? let reblogShowContentToggle: StatusShowContentToggle?
let showAttachmentsToggle: StatusShowAttachmentsToggle? let showAttachmentsToggle: StatusShowAttachmentsToggle?
@ -50,7 +53,9 @@ private extension StatusInfo {
static func addingOptionalIncludes<T: DerivableRequest>(_ request: T) -> T where T.RowDecoder == StatusRecord { static func addingOptionalIncludes<T: DerivableRequest>(_ request: T) -> T where T.RowDecoder == StatusRecord {
request.including(optional: AccountInfo.addingIncludes(StatusRecord.reblogAccount) request.including(optional: AccountInfo.addingIncludes(StatusRecord.reblogAccount)
.forKey(CodingKeys.reblogAccountInfo)) .forKey(CodingKeys.reblogAccountInfo))
.including(optional: StatusRecord.relationship.forKey(CodingKeys.relationship))
.including(optional: StatusRecord.reblog.forKey(CodingKeys.reblogRecord)) .including(optional: StatusRecord.reblog.forKey(CodingKeys.reblogRecord))
.including(optional: StatusRecord.reblogRelationship.forKey(CodingKeys.reblogRelationship))
.including(optional: StatusRecord.showContentToggle.forKey(CodingKeys.showContentToggle)) .including(optional: StatusRecord.showContentToggle.forKey(CodingKeys.showContentToggle))
.including(optional: StatusRecord.reblogShowContentToggle.forKey(CodingKeys.reblogShowContentToggle)) .including(optional: StatusRecord.reblogShowContentToggle.forKey(CodingKeys.reblogShowContentToggle))
.including(optional: StatusRecord.showAttachmentsToggle.forKey(CodingKeys.showAttachmentsToggle)) .including(optional: StatusRecord.showAttachmentsToggle.forKey(CodingKeys.showAttachmentsToggle))

View File

@ -72,6 +72,9 @@ extension StatusRecord {
extension StatusRecord { extension StatusRecord {
static let account = belongsTo(AccountRecord.self) static let account = belongsTo(AccountRecord.self)
static let relationship = hasOne(Relationship.self,
through: Self.account,
using: AccountRecord.relationship)
static let accountMoved = hasOne(AccountRecord.self, static let accountMoved = hasOne(AccountRecord.self,
through: Self.account, through: Self.account,
using: AccountRecord.moved) using: AccountRecord.moved)
@ -82,6 +85,10 @@ extension StatusRecord {
through: Self.reblogAccount, through: Self.reblogAccount,
using: AccountRecord.moved) using: AccountRecord.moved)
static let reblog = belongsTo(StatusRecord.self) static let reblog = belongsTo(StatusRecord.self)
static let reblogRelationship = hasOne(
Relationship.self,
through: Self.reblog,
using: Self.relationship)
static let showContentToggle = hasOne(StatusShowContentToggle.self) static let showContentToggle = hasOne(StatusShowContentToggle.self)
static let reblogShowContentToggle = hasOne( static let reblogShowContentToggle = hasOne(
StatusShowContentToggle.self, StatusShowContentToggle.self,

View File

@ -40,12 +40,13 @@ extension TimelineItemsInfo {
CollectionItem.status( CollectionItem.status(
.init(info: $0), .init(info: $0),
.init(showContentToggled: $0.showContentToggled, .init(showContentToggled: $0.showContentToggled,
showAttachmentsToggled: $0.showAttachmentsToggled)) showAttachmentsToggled: $0.showAttachmentsToggled),
$0.reblogRelationship ?? $0.relationship)
} }
for loadMoreRecord in loadMoreRecords { for loadMoreRecord in loadMoreRecords {
guard let index = timelineItems.firstIndex(where: { guard let index = timelineItems.firstIndex(where: {
guard case let .status(status, _) = $0 else { return false } guard case let .status(status, _, _) = $0 else { return false }
return loadMoreRecord.afterStatusId > status.id return loadMoreRecord.afterStatusId > status.id
}) else { continue } }) else { continue }
@ -66,7 +67,8 @@ extension TimelineItemsInfo {
.init(info: $0), .init(info: $0),
.init(showContentToggled: $0.showContentToggled, .init(showContentToggled: $0.showContentToggled,
showAttachmentsToggled: $0.showAttachmentsToggled, showAttachmentsToggled: $0.showAttachmentsToggled,
isPinned: true)) isPinned: true),
$0.reblogRelationship ?? $0.relationship)
}), }),
.init(items: timelineItems)] .init(items: timelineItems)]
} else { } else {

View File

@ -3,9 +3,9 @@
import Mastodon import Mastodon
public enum CollectionItem: Hashable { public enum CollectionItem: Hashable {
case status(Status, StatusConfiguration) case status(Status, StatusConfiguration, Relationship?)
case loadMore(LoadMore) case loadMore(LoadMore)
case account(Account, AccountConfiguration) case account(Account, AccountConfiguration, Relationship?)
case notification(MastodonNotification, StatusConfiguration?) case notification(MastodonNotification, StatusConfiguration?)
case conversation(Conversation) case conversation(Conversation)
case tag(Tag) case tag(Tag)
@ -46,11 +46,11 @@ public extension CollectionItem {
var itemId: Id? { var itemId: Id? {
switch self { switch self {
case let .status(status, _): case let .status(status, _, _):
return status.id return status.id
case .loadMore: case .loadMore:
return nil return nil
case let .account(account, _): case let .account(account, _, _):
return account.id return account.id
case let .notification(notification, _): case let .notification(notification, _):
return notification.id return notification.id

View File

@ -35,13 +35,13 @@ extension CollectionItem {
func estimatedHeight(width: CGFloat, identityContext: IdentityContext) -> CGFloat { func estimatedHeight(width: CGFloat, identityContext: IdentityContext) -> CGFloat {
switch self { switch self {
case let .status(status, configuration): case let .status(status, configuration, _):
return StatusView.estimatedHeight( return StatusView.estimatedHeight(
width: width, width: width,
identityContext: identityContext, identityContext: identityContext,
status: status, status: status,
configuration: configuration) configuration: configuration)
case let .account(account, configuration): case let .account(account, configuration, _):
return AccountView.estimatedHeight(width: width, account: account, configuration: configuration) return AccountView.estimatedHeight(width: width, account: account, configuration: configuration)
case .loadMore: case .loadMore:
return LoadMoreView.estimatedHeight return LoadMoreView.estimatedHeight
@ -65,9 +65,9 @@ extension CollectionItem {
func mediaPrefetchURLs(identityContext: IdentityContext) -> Set<URL> { func mediaPrefetchURLs(identityContext: IdentityContext) -> Set<URL> {
switch self { switch self {
case let .status(status, _): case let .status(status, _, _):
return status.mediaPrefetchURLs(identityContext: identityContext) return status.mediaPrefetchURLs(identityContext: identityContext)
case let .account(account, _): case let .account(account, _, _):
return account.mediaPrefetchURLs(identityContext: identityContext) return account.mediaPrefetchURLs(identityContext: identityContext)
case let .notification(notification, _): case let .notification(notification, _):
var urls = notification.account.mediaPrefetchURLs(identityContext: identityContext) var urls = notification.account.mediaPrefetchURLs(identityContext: identityContext)

View File

@ -9,6 +9,7 @@ import MastodonAPI
public struct AccountListService { public struct AccountListService {
public let sections: AnyPublisher<[CollectionSection], Error> public let sections: AnyPublisher<[CollectionSection], Error>
public let nextPageMaxId: AnyPublisher<String, Never> public let nextPageMaxId: AnyPublisher<String, Never>
public let accountIdsForRelationships: AnyPublisher<Set<Account.Id>, Never>
public let navigationService: NavigationService public let navigationService: NavigationService
public let canRefresh = false public let canRefresh = false
@ -18,6 +19,7 @@ public struct AccountListService {
private let contentDatabase: ContentDatabase private let contentDatabase: ContentDatabase
private let titleComponents: [String]? private let titleComponents: [String]?
private let nextPageMaxIdSubject = PassthroughSubject<String, Never>() private let nextPageMaxIdSubject = PassthroughSubject<String, Never>()
private let accountIdsForRelationshipsSubject = PassthroughSubject<Set<Account.Id>, Never>()
init(endpoint: AccountsEndpoint, init(endpoint: AccountsEndpoint,
mastodonAPIClient: MastodonAPIClient, mastodonAPIClient: MastodonAPIClient,
@ -28,9 +30,11 @@ public struct AccountListService {
self.contentDatabase = contentDatabase self.contentDatabase = contentDatabase
self.titleComponents = titleComponents self.titleComponents = titleComponents
sections = accountsSubject sections = accountsSubject
.map { [.init(items: $0.map { CollectionItem.account($0, endpoint.configuration) })] } .map { [.init(items: $0.map { CollectionItem.account($0, endpoint.configuration, nil) })] } // TODO: revisit
.removeDuplicates()
.eraseToAnyPublisher() .eraseToAnyPublisher()
nextPageMaxId = nextPageMaxIdSubject.eraseToAnyPublisher() nextPageMaxId = nextPageMaxIdSubject.eraseToAnyPublisher()
accountIdsForRelationships = accountIdsForRelationshipsSubject.eraseToAnyPublisher()
navigationService = NavigationService(mastodonAPIClient: mastodonAPIClient, contentDatabase: contentDatabase) navigationService = NavigationService(mastodonAPIClient: mastodonAPIClient, contentDatabase: contentDatabase)
} }
} }
@ -51,6 +55,7 @@ extension AccountListService: CollectionService {
guard let maxId = $0.info.maxId else { return } guard let maxId = $0.info.maxId else { return }
nextPageMaxIdSubject.send(maxId) nextPageMaxIdSubject.send(maxId)
accountIdsForRelationshipsSubject.send(Set($0.result.map(\.id)))
}) })
.flatMap { contentDatabase.insert(accounts: $0.result) } .flatMap { contentDatabase.insert(accounts: $0.result) }
.ignoreOutput() .ignoreOutput()

View File

@ -8,24 +8,17 @@ import MastodonAPI
public struct AccountService { public struct AccountService {
public let account: Account public let account: Account
public let relationship: Relationship?
public let identityProofs: [IdentityProof]
public let featuredTags: [FeaturedTag]
public let navigationService: NavigationService public let navigationService: NavigationService
private let mastodonAPIClient: MastodonAPIClient private let mastodonAPIClient: MastodonAPIClient
private let contentDatabase: ContentDatabase private let contentDatabase: ContentDatabase
public init(account: Account, public init(account: Account,
relationship: Relationship? = nil,
identityProofs: [IdentityProof] = [], identityProofs: [IdentityProof] = [],
featuredTags: [FeaturedTag] = [], featuredTags: [FeaturedTag] = [],
mastodonAPIClient: MastodonAPIClient, mastodonAPIClient: MastodonAPIClient,
contentDatabase: ContentDatabase) { contentDatabase: ContentDatabase) {
self.account = account self.account = account
self.relationship = relationship
self.identityProofs = identityProofs
self.featuredTags = featuredTags
navigationService = NavigationService(mastodonAPIClient: mastodonAPIClient, contentDatabase: contentDatabase) navigationService = NavigationService(mastodonAPIClient: mastodonAPIClient, contentDatabase: contentDatabase)
self.mastodonAPIClient = mastodonAPIClient self.mastodonAPIClient = mastodonAPIClient
self.contentDatabase = contentDatabase self.contentDatabase = contentDatabase

View File

@ -6,6 +6,7 @@ import Mastodon
public protocol CollectionService { public protocol CollectionService {
var sections: AnyPublisher<[CollectionSection], Error> { get } var sections: AnyPublisher<[CollectionSection], Error> { get }
var nextPageMaxId: AnyPublisher<String, Never> { get } var nextPageMaxId: AnyPublisher<String, Never> { get }
var accountIdsForRelationships: AnyPublisher<Set<Account.Id>, Never> { get }
var preferLastPresentIdOverNextPageMaxId: Bool { get } var preferLastPresentIdOverNextPageMaxId: Bool { get }
var canRefresh: Bool { get } var canRefresh: Bool { get }
var title: AnyPublisher<String, Never> { get } var title: AnyPublisher<String, Never> { get }
@ -18,6 +19,8 @@ public protocol CollectionService {
extension CollectionService { extension CollectionService {
public var nextPageMaxId: AnyPublisher<String, Never> { Empty().eraseToAnyPublisher() } public var nextPageMaxId: AnyPublisher<String, Never> { Empty().eraseToAnyPublisher() }
public var accountIdsForRelationships: AnyPublisher<Set<Account.Id>, Never> { Empty().eraseToAnyPublisher() }
public var preferLastPresentIdOverNextPageMaxId: Bool { false } public var preferLastPresentIdOverNextPageMaxId: Bool { false }
public var canRefresh: Bool { true } public var canRefresh: Bool { true }

View File

@ -114,6 +114,12 @@ public extension IdentityService {
.eraseToAnyPublisher() .eraseToAnyPublisher()
} }
func requestRelationships(ids: Set<Account.Id>) -> AnyPublisher<Never, Error> {
mastodonAPIClient.request(RelationshipsEndpoint.relationships(ids: Array(ids)))
.flatMap(contentDatabase.insert(relationships:))
.eraseToAnyPublisher()
}
func getMarker(_ markerTimeline: Marker.Timeline) -> AnyPublisher<Marker, Error> { func getMarker(_ markerTimeline: Marker.Timeline) -> AnyPublisher<Marker, Error> {
mastodonAPIClient.request(MarkersEndpoint.get([markerTimeline])) mastodonAPIClient.request(MarkersEndpoint.get([markerTimeline]))
.compactMap { $0[markerTimeline.rawValue] } .compactMap { $0[markerTimeline.rawValue] }

View File

@ -7,7 +7,7 @@ import Mastodon
import MastodonAPI import MastodonAPI
public struct ProfileService { public struct ProfileService {
public let accountServicePublisher: AnyPublisher<AccountService, Error> public let profilePublisher: AnyPublisher<Profile, Error>
private let id: Account.Id private let id: Account.Id
private let mastodonAPIClient: MastodonAPIClient private let mastodonAPIClient: MastodonAPIClient
@ -34,26 +34,16 @@ public struct ProfileService {
self.mastodonAPIClient = mastodonAPIClient self.mastodonAPIClient = mastodonAPIClient
self.contentDatabase = contentDatabase self.contentDatabase = contentDatabase
var accountPublisher = contentDatabase.profilePublisher(id: id) var profilePublisher = contentDatabase.profilePublisher(id: id)
if let account = account { if let account = account {
accountPublisher = accountPublisher profilePublisher = profilePublisher
.merge(with: Just(Profile(account: account)).setFailureType(to: Error.self)) .merge(with: Just(Profile(account: account)).setFailureType(to: Error.self))
.removeDuplicates() .removeDuplicates()
.eraseToAnyPublisher() .eraseToAnyPublisher()
} }
accountServicePublisher = accountPublisher self.profilePublisher = profilePublisher
.map {
AccountService(
account: $0.account,
relationship: $0.relationship,
identityProofs: $0.identityProofs,
featuredTags: $0.featuredTags,
mastodonAPIClient: mastodonAPIClient,
contentDatabase: contentDatabase)
}
.eraseToAnyPublisher()
} }
} }

View File

@ -10,6 +10,7 @@ public struct TimelineService {
public let sections: AnyPublisher<[CollectionSection], Error> public let sections: AnyPublisher<[CollectionSection], Error>
public let navigationService: NavigationService public let navigationService: NavigationService
public let nextPageMaxId: AnyPublisher<String, Never> public let nextPageMaxId: AnyPublisher<String, Never>
public let accountIdsForRelationships: AnyPublisher<Set<Account.Id>, Never>
public let title: AnyPublisher<String, Never> public let title: AnyPublisher<String, Never>
public let titleLocalizationComponents: AnyPublisher<[String], Never> public let titleLocalizationComponents: AnyPublisher<[String], Never>
@ -17,6 +18,7 @@ public struct TimelineService {
private let mastodonAPIClient: MastodonAPIClient private let mastodonAPIClient: MastodonAPIClient
private let contentDatabase: ContentDatabase private let contentDatabase: ContentDatabase
private let nextPageMaxIdSubject = PassthroughSubject<String, Never>() private let nextPageMaxIdSubject = PassthroughSubject<String, Never>()
private let accountIdsForRelationshipsSubject = PassthroughSubject<Set<Account.Id>, Never>()
init(timeline: Timeline, mastodonAPIClient: MastodonAPIClient, contentDatabase: ContentDatabase) { init(timeline: Timeline, mastodonAPIClient: MastodonAPIClient, contentDatabase: ContentDatabase) {
self.timeline = timeline self.timeline = timeline
@ -25,6 +27,7 @@ public struct TimelineService {
sections = contentDatabase.timelinePublisher(timeline) sections = contentDatabase.timelinePublisher(timeline)
navigationService = NavigationService(mastodonAPIClient: mastodonAPIClient, contentDatabase: contentDatabase) navigationService = NavigationService(mastodonAPIClient: mastodonAPIClient, contentDatabase: contentDatabase)
nextPageMaxId = nextPageMaxIdSubject.eraseToAnyPublisher() nextPageMaxId = nextPageMaxIdSubject.eraseToAnyPublisher()
accountIdsForRelationships = accountIdsForRelationshipsSubject.eraseToAnyPublisher()
switch timeline { switch timeline {
case let .list(list): case let .list(list):
@ -66,6 +69,10 @@ extension TimelineService: CollectionService {
if let maxId = $0.info.maxId { if let maxId = $0.info.maxId {
nextPageMaxIdSubject.send(maxId) nextPageMaxIdSubject.send(maxId)
} }
accountIdsForRelationshipsSubject.send(
Set($0.result.map(\.account.id))
.union(Set($0.result.compactMap(\.reblog?.account.id))))
}) })
.flatMap { contentDatabase.insert(statuses: $0.result, timeline: timeline) } .flatMap { contentDatabase.insert(statuses: $0.result, timeline: timeline) }
.eraseToAnyPublisher() .eraseToAnyPublisher()

View File

@ -8,6 +8,9 @@ import ServiceLayer
public final class AccountViewModel: ObservableObject { public final class AccountViewModel: ObservableObject {
public let identityContext: IdentityContext public let identityContext: IdentityContext
public internal(set) var configuration = CollectionItem.AccountConfiguration.withNote public internal(set) var configuration = CollectionItem.AccountConfiguration.withNote
public internal(set) var relationship: Relationship?
public internal(set) var identityProofs = [IdentityProof]()
public internal(set) var featuredTags = [FeaturedTag]()
private let accountService: AccountService private let accountService: AccountService
private let eventsSubject: PassthroughSubject<AnyPublisher<CollectionItemEvent, Error>, Never> private let eventsSubject: PassthroughSubject<AnyPublisher<CollectionItemEvent, Error>, Never>
@ -44,12 +47,6 @@ public extension AccountViewModel {
var isLocked: Bool { accountService.account.locked } var isLocked: Bool { accountService.account.locked }
var relationship: Relationship? { accountService.relationship }
var identityProofs: [IdentityProof] { accountService.identityProofs }
var featuredTags: [FeaturedTag] { accountService.featuredTags }
var fields: [Account.Field] { accountService.account.fields } var fields: [Account.Field] { accountService.account.fields }
var note: NSAttributedString { accountService.account.note.attributed } var note: NSAttributedString { accountService.account.note.attributed }

View File

@ -42,6 +42,13 @@ public class CollectionItemsViewModel: ObservableObject {
.sink { [weak self] in self?.nextPageMaxId = $0 } .sink { [weak self] in self?.nextPageMaxId = $0 }
.store(in: &cancellables) .store(in: &cancellables)
collectionService.accountIdsForRelationships
.filter { !$0.isEmpty }
.flatMap(identityContext.service.requestRelationships(ids:))
.catch { _ in Empty().setFailureType(to: Never.self) }
.sink { _ in }
.store(in: &cancellables)
if let markerTimeline = collectionService.markerTimeline { if let markerTimeline = collectionService.markerTimeline {
shouldRestorePositionOfLocalLastReadId = shouldRestorePositionOfLocalLastReadId =
identityContext.appPreferences.positionBehavior(markerTimeline: markerTimeline) == .rememberPosition identityContext.appPreferences.positionBehavior(markerTimeline: markerTimeline) == .rememberPosition
@ -134,14 +141,14 @@ extension CollectionItemsViewModel: CollectionViewModel {
let item = lastUpdate.sections[indexPath.section].items[indexPath.item] let item = lastUpdate.sections[indexPath.section].items[indexPath.item]
switch item { switch item {
case let .status(status, _): case let .status(status, _, relationship):
send(event: .navigation(.collection(collectionService send(event: .navigation(.collection(collectionService
.navigationService .navigationService
.contextService(id: status.displayStatus.id)))) .contextService(id: status.displayStatus.id))))
case let .loadMore(loadMore): case let .loadMore(loadMore):
lastSelectedLoadMore = loadMore lastSelectedLoadMore = loadMore
(viewModel(indexPath: indexPath) as? LoadMoreViewModel)?.loadMore() (viewModel(indexPath: indexPath) as? LoadMoreViewModel)?.loadMore()
case let .account(account, _): case let .account(account, _, relationship):
send(event: .navigation(.profile(collectionService send(event: .navigation(.profile(collectionService
.navigationService .navigationService
.profileService(account: account)))) .profileService(account: account))))
@ -182,7 +189,7 @@ extension CollectionItemsViewModel: CollectionViewModel {
public func canSelect(indexPath: IndexPath) -> Bool { public func canSelect(indexPath: IndexPath) -> Bool {
switch lastUpdate.sections[indexPath.section].items[indexPath.item] { switch lastUpdate.sections[indexPath.section].items[indexPath.item] {
case let .status(_, configuration): case let .status(_, configuration, _):
return !configuration.isContextParent return !configuration.isContextParent
case .loadMore: case .loadMore:
return !((viewModel(indexPath: indexPath) as? LoadMoreViewModel)?.loading ?? false) return !((viewModel(indexPath: indexPath) as? LoadMoreViewModel)?.loading ?? false)
@ -197,7 +204,7 @@ extension CollectionItemsViewModel: CollectionViewModel {
let cachedViewModel = viewModelCache[item] let cachedViewModel = viewModelCache[item]
switch item { switch item {
case let .status(status, configuration): case let .status(status, configuration, relationship):
let viewModel: StatusViewModel let viewModel: StatusViewModel
if let cachedViewModel = cachedViewModel as? StatusViewModel { if let cachedViewModel = cachedViewModel as? StatusViewModel {
@ -211,6 +218,7 @@ extension CollectionItemsViewModel: CollectionViewModel {
} }
viewModel.configuration = configuration viewModel.configuration = configuration
viewModel.accountViewModel.relationship = relationship
return viewModel return viewModel
case let .loadMore(loadMore): case let .loadMore(loadMore):
@ -225,7 +233,7 @@ extension CollectionItemsViewModel: CollectionViewModel {
viewModelCache[item] = viewModel viewModelCache[item] = viewModel
return viewModel return viewModel
case let .account(account, configuration): case let .account(account, configuration, relationship):
let viewModel: AccountViewModel let viewModel: AccountViewModel
if let cachedViewModel = cachedViewModel as? AccountViewModel { if let cachedViewModel = cachedViewModel as? AccountViewModel {
@ -239,6 +247,7 @@ extension CollectionItemsViewModel: CollectionViewModel {
} }
viewModel.configuration = configuration viewModel.configuration = configuration
viewModel.relationship = relationship
return viewModel return viewModel
case let .notification(notification, statusConfiguration): case let .notification(notification, statusConfiguration):
@ -302,7 +311,7 @@ extension CollectionItemsViewModel: CollectionViewModel {
public func toggleExpandAll() { public func toggleExpandAll() {
let statusIds = Set(lastUpdate.sections.map(\.items).reduce([], +).compactMap { item -> Status.Id? in let statusIds = Set(lastUpdate.sections.map(\.items).reduce([], +).compactMap { item -> Status.Id? in
guard case let .status(status, _) = item else { return nil } guard case let .status(status, _, _) = item else { return nil }
return status.id return status.id
}) })
@ -388,7 +397,7 @@ private extension CollectionItemsViewModel {
if collectionService is ContextService, if collectionService is ContextService,
lastUpdate.sections.isEmpty || lastUpdate.sections.map(\.items.count) == [0, 1, 0], lastUpdate.sections.isEmpty || lastUpdate.sections.map(\.items.count) == [0, 1, 0],
let contextParent = newItems.first(where: { let contextParent = newItems.first(where: {
guard case let .status(_, configuration) = $0 else { return false } guard case let .status(_, configuration, _) = $0 else { return false }
return configuration.isContextParent // Maintain scroll position of parent after initial load of context return configuration.isContextParent // Maintain scroll position of parent after initial load of context
}) { }) {
@ -404,7 +413,7 @@ private extension CollectionItemsViewModel {
let direction = (viewModelCache[item] as? LoadMoreViewModel)?.direction, let direction = (viewModelCache[item] as? LoadMoreViewModel)?.direction,
direction == .up, direction == .up,
let statusAfterLoadMore = items.first(where: { let statusAfterLoadMore = items.first(where: {
guard case let .status(status, _) = $0 else { return false } guard case let .status(status, _, _) = $0 else { return false }
return status.id == loadMore.beforeStatusId return status.id == loadMore.beforeStatusId
}) { }) {

View File

@ -30,11 +30,19 @@ final public class ProfileViewModel {
self.accountEventsSubject = accountEventsSubject self.accountEventsSubject = accountEventsSubject
profileService.accountServicePublisher profileService.profilePublisher
.map { .map {
AccountViewModel(accountService: $0, let vm = AccountViewModel(accountService: identityContext.service
.navigationService
.accountService(account: $0.account),
identityContext: identityContext, identityContext: identityContext,
eventsSubject: accountEventsSubject) eventsSubject: accountEventsSubject)
vm.relationship = $0.relationship
vm.identityProofs = $0.identityProofs
vm.featuredTags = $0.featuredTags
return vm
} }
.assignErrorsToAlertItem(to: \.alertItem, on: self) .assignErrorsToAlertItem(to: \.alertItem, on: self)
.assign(to: &$accountViewModel) .assign(to: &$accountViewModel)