Account list wip
This commit is contained in:
parent
3328306c44
commit
a2f84197ef
|
@ -0,0 +1,34 @@
|
|||
// Copyright © 2020 Metabolist. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import HTTP
|
||||
import Mastodon
|
||||
|
||||
public enum AccountsEndpoint {
|
||||
case statusFavouritedBy(id: String)
|
||||
}
|
||||
|
||||
extension AccountsEndpoint: Endpoint {
|
||||
public typealias ResultType = [Account]
|
||||
|
||||
public var context: [String] {
|
||||
switch self {
|
||||
case .statusFavouritedBy:
|
||||
return defaultContext + ["statuses"]
|
||||
}
|
||||
}
|
||||
|
||||
public var pathComponentsInContext: [String] {
|
||||
switch self {
|
||||
case let .statusFavouritedBy(id):
|
||||
return [id, "favourited_by"]
|
||||
}
|
||||
}
|
||||
|
||||
public var method: HTTPMethod {
|
||||
switch self {
|
||||
case .statusFavouritedBy:
|
||||
return .get
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,7 +8,8 @@ import MastodonAPI
|
|||
|
||||
public struct AccountListService {
|
||||
public let accountSections: AnyPublisher<[[Account]], Error>
|
||||
public let paginates: Bool
|
||||
public let nextPageMaxIDs: AnyPublisher<String?, Never>
|
||||
public let navigationService: NavigationService
|
||||
|
||||
private let mastodonAPIClient: MastodonAPIClient
|
||||
private let contentDatabase: ContentDatabase
|
||||
|
@ -16,7 +17,31 @@ public struct AccountListService {
|
|||
}
|
||||
|
||||
extension AccountListService {
|
||||
init(favoritedByStatusID statusID: String, mastodonAPIClient: MastodonAPIClient, contentDatabase: ContentDatabase) {
|
||||
let accountSectionsSubject = PassthroughSubject<[[Account]], Error>()
|
||||
let nextPageMaxIDsSubject = PassthroughSubject<String?, Never>()
|
||||
|
||||
self.init(
|
||||
accountSections: accountSectionsSubject.eraseToAnyPublisher(),
|
||||
nextPageMaxIDs: nextPageMaxIDsSubject.eraseToAnyPublisher(),
|
||||
navigationService: NavigationService(
|
||||
status: nil,
|
||||
mastodonAPIClient: mastodonAPIClient,
|
||||
contentDatabase: contentDatabase),
|
||||
mastodonAPIClient: mastodonAPIClient,
|
||||
contentDatabase: contentDatabase) { maxID, minID -> AnyPublisher<Never, Error> in
|
||||
mastodonAPIClient.pagedRequest(
|
||||
AccountsEndpoint.statusFavouritedBy(id: statusID), maxID: maxID, minID: minID)
|
||||
.handleEvents(
|
||||
receiveOutput: {
|
||||
nextPageMaxIDsSubject.send($0.info.maxID)
|
||||
accountSectionsSubject.send([$0.result])
|
||||
},
|
||||
receiveCompletion: accountSectionsSubject.send)
|
||||
.flatMap { contentDatabase.insert(accounts: $0.result) }
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public extension AccountListService {
|
||||
|
|
|
@ -8,13 +8,13 @@ import MastodonAPI
|
|||
|
||||
public struct AccountService {
|
||||
public let account: Account
|
||||
public let urlService: URLService
|
||||
public let navigationService: NavigationService
|
||||
private let mastodonAPIClient: MastodonAPIClient
|
||||
private let contentDatabase: ContentDatabase
|
||||
|
||||
init(account: Account, mastodonAPIClient: MastodonAPIClient, contentDatabase: ContentDatabase) {
|
||||
self.account = account
|
||||
self.urlService = URLService(
|
||||
self.navigationService = NavigationService(
|
||||
status: nil,
|
||||
mastodonAPIClient: mastodonAPIClient,
|
||||
contentDatabase: contentDatabase)
|
||||
|
|
|
@ -6,14 +6,13 @@ import Foundation
|
|||
import Mastodon
|
||||
import MastodonAPI
|
||||
|
||||
public enum URLItem {
|
||||
public enum Navigation {
|
||||
case url(URL)
|
||||
case statusID(String)
|
||||
case accountID(String)
|
||||
case tag(String)
|
||||
case statusList(StatusListService)
|
||||
case accountStatuses(AccountStatusesService)
|
||||
}
|
||||
|
||||
public struct URLService {
|
||||
public struct NavigationService {
|
||||
private let status: Status?
|
||||
private let mastodonAPIClient: MastodonAPIClient
|
||||
private let contentDatabase: ContentDatabase
|
||||
|
@ -25,21 +24,49 @@ public struct URLService {
|
|||
}
|
||||
}
|
||||
|
||||
public extension URLService {
|
||||
func item(url: URL) -> AnyPublisher<URLItem, Never> {
|
||||
public extension NavigationService {
|
||||
func item(url: URL) -> AnyPublisher<Navigation, Never> {
|
||||
if let tag = tag(url: url) {
|
||||
return Just(.tag(tag)).eraseToAnyPublisher()
|
||||
return Just(
|
||||
.statusList(
|
||||
StatusListService(
|
||||
timeline: .tag(tag),
|
||||
mastodonAPIClient: mastodonAPIClient,
|
||||
contentDatabase: contentDatabase)))
|
||||
.eraseToAnyPublisher()
|
||||
} else if let accountID = accountID(url: url) {
|
||||
return Just(.accountID(accountID)).eraseToAnyPublisher()
|
||||
return Just(.accountStatuses(accountStatusesService(id: accountID))).eraseToAnyPublisher()
|
||||
} else if mastodonAPIClient.instanceURL.host == url.host, let statusID = url.statusID {
|
||||
return Just(.statusID(statusID)).eraseToAnyPublisher()
|
||||
return Just(
|
||||
.statusList(
|
||||
StatusListService(
|
||||
statusID: statusID,
|
||||
mastodonAPIClient: mastodonAPIClient,
|
||||
contentDatabase: contentDatabase)))
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
return Just(.url(url)).eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
func contextStatusListService(id: String) -> StatusListService {
|
||||
StatusListService(statusID: id, mastodonAPIClient: mastodonAPIClient, contentDatabase: contentDatabase)
|
||||
}
|
||||
|
||||
func accountStatusesService(id: String) -> AccountStatusesService {
|
||||
AccountStatusesService(id: id, mastodonAPIClient: mastodonAPIClient, contentDatabase: contentDatabase)
|
||||
}
|
||||
|
||||
func statusService(status: Status) -> StatusService {
|
||||
StatusService(status: status, mastodonAPIClient: mastodonAPIClient, contentDatabase: contentDatabase)
|
||||
}
|
||||
|
||||
func accountService(account: Account) -> AccountService {
|
||||
AccountService(account: account, mastodonAPIClient: mastodonAPIClient, contentDatabase: contentDatabase)
|
||||
}
|
||||
}
|
||||
|
||||
private extension URLService {
|
||||
private extension NavigationService {
|
||||
func tag(url: URL) -> String? {
|
||||
if status?.tags.first(where: { $0.url.path.lowercased() == url.path.lowercased() }) != nil {
|
||||
return url.lastPathComponent
|
|
@ -11,6 +11,7 @@ public struct StatusListService {
|
|||
public let nextPageMaxIDs: AnyPublisher<String?, Never>
|
||||
public let contextParentID: String?
|
||||
public let title: String?
|
||||
public let navigationService: NavigationService
|
||||
|
||||
private let filterContext: Filter.Context
|
||||
private let mastodonAPIClient: MastodonAPIClient
|
||||
|
@ -41,6 +42,10 @@ extension StatusListService {
|
|||
nextPageMaxIDs: nextPageMaxIDsSubject.eraseToAnyPublisher(),
|
||||
contextParentID: nil,
|
||||
title: title,
|
||||
navigationService: NavigationService(
|
||||
status: nil,
|
||||
mastodonAPIClient: mastodonAPIClient,
|
||||
contentDatabase: contentDatabase),
|
||||
filterContext: filterContext,
|
||||
mastodonAPIClient: mastodonAPIClient,
|
||||
contentDatabase: contentDatabase) { maxID, minID in
|
||||
|
@ -51,6 +56,29 @@ extension StatusListService {
|
|||
}
|
||||
}
|
||||
|
||||
init(statusID: String, mastodonAPIClient: MastodonAPIClient, contentDatabase: ContentDatabase) {
|
||||
self.init(statusSections: contentDatabase.contextObservation(parentID: statusID),
|
||||
nextPageMaxIDs: Empty().eraseToAnyPublisher(),
|
||||
contextParentID: statusID,
|
||||
title: nil,
|
||||
navigationService: NavigationService(
|
||||
status: nil,
|
||||
mastodonAPIClient: mastodonAPIClient,
|
||||
contentDatabase: contentDatabase),
|
||||
filterContext: .thread,
|
||||
mastodonAPIClient: mastodonAPIClient,
|
||||
contentDatabase: contentDatabase) { _, _ in
|
||||
Publishers.Merge(
|
||||
mastodonAPIClient.request(StatusEndpoint.status(id: statusID))
|
||||
.flatMap(contentDatabase.insert(status:))
|
||||
.eraseToAnyPublisher(),
|
||||
mastodonAPIClient.request(ContextEndpoint.context(id: statusID))
|
||||
.flatMap { contentDatabase.insert(context: $0, parentID: statusID) }
|
||||
.eraseToAnyPublisher())
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
}
|
||||
|
||||
init(
|
||||
accountID: String,
|
||||
collection: CurrentValueSubject<AccountStatusCollection, Never>,
|
||||
|
@ -65,6 +93,10 @@ extension StatusListService {
|
|||
nextPageMaxIDs: nextPageMaxIDsSubject.eraseToAnyPublisher(),
|
||||
contextParentID: nil,
|
||||
title: nil,
|
||||
navigationService: NavigationService(
|
||||
status: nil,
|
||||
mastodonAPIClient: mastodonAPIClient,
|
||||
contentDatabase: contentDatabase),
|
||||
filterContext: .account,
|
||||
mastodonAPIClient: mastodonAPIClient,
|
||||
contentDatabase: contentDatabase) { maxID, minID in
|
||||
|
@ -90,7 +122,12 @@ extension StatusListService {
|
|||
pinned: false)
|
||||
return mastodonAPIClient.pagedRequest(endpoint, maxID: maxID, minID: minID)
|
||||
.handleEvents(receiveOutput: { nextPageMaxIDsSubject.send($0.info.maxID) })
|
||||
.flatMap { contentDatabase.insert(statuses: $0.result, accountID: accountID, collection: collection.value) }
|
||||
.flatMap {
|
||||
contentDatabase.insert(
|
||||
statuses: $0.result,
|
||||
accountID: accountID,
|
||||
collection: collection.value)
|
||||
}
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
}
|
||||
|
@ -104,35 +141,4 @@ public extension StatusListService {
|
|||
var filters: AnyPublisher<[Filter], Error> {
|
||||
contentDatabase.activeFiltersObservation(date: Date(), context: filterContext)
|
||||
}
|
||||
|
||||
func statusService(status: Status) -> StatusService {
|
||||
StatusService(status: status, mastodonAPIClient: mastodonAPIClient, contentDatabase: contentDatabase)
|
||||
}
|
||||
|
||||
func service(timeline: Timeline) -> Self {
|
||||
Self(timeline: timeline, mastodonAPIClient: mastodonAPIClient, contentDatabase: contentDatabase)
|
||||
}
|
||||
|
||||
func service(accountID: String) -> AccountStatusesService {
|
||||
AccountStatusesService(id: accountID, mastodonAPIClient: mastodonAPIClient, contentDatabase: contentDatabase)
|
||||
}
|
||||
|
||||
func contextService(statusID: String) -> Self {
|
||||
Self(statusSections: contentDatabase.contextObservation(parentID: statusID),
|
||||
nextPageMaxIDs: Empty().eraseToAnyPublisher(),
|
||||
contextParentID: statusID,
|
||||
title: nil,
|
||||
filterContext: .thread,
|
||||
mastodonAPIClient: mastodonAPIClient,
|
||||
contentDatabase: contentDatabase) { _, _ in
|
||||
Publishers.Merge(
|
||||
mastodonAPIClient.request(StatusEndpoint.status(id: statusID))
|
||||
.flatMap(contentDatabase.insert(status:))
|
||||
.eraseToAnyPublisher(),
|
||||
mastodonAPIClient.request(ContextEndpoint.context(id: statusID))
|
||||
.flatMap { contentDatabase.insert(context: $0, parentID: statusID) }
|
||||
.eraseToAnyPublisher())
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,13 +8,13 @@ import MastodonAPI
|
|||
|
||||
public struct StatusService {
|
||||
public let status: Status
|
||||
public let urlService: URLService
|
||||
public let navigationService: NavigationService
|
||||
private let mastodonAPIClient: MastodonAPIClient
|
||||
private let contentDatabase: ContentDatabase
|
||||
|
||||
init(status: Status, mastodonAPIClient: MastodonAPIClient, contentDatabase: ContentDatabase) {
|
||||
self.status = status
|
||||
self.urlService = URLService(
|
||||
self.navigationService = NavigationService(
|
||||
status: status.displayStatus,
|
||||
mastodonAPIClient: mastodonAPIClient,
|
||||
contentDatabase: contentDatabase)
|
||||
|
@ -31,4 +31,11 @@ public extension StatusService {
|
|||
.flatMap(contentDatabase.insert(status:))
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
func favoritedByService() -> AccountListService {
|
||||
AccountListService(
|
||||
favoritedByStatusID: status.id,
|
||||
mastodonAPIClient: mastodonAPIClient,
|
||||
contentDatabase: contentDatabase)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,126 @@
|
|||
// Copyright © 2020 Metabolist. All rights reserved.
|
||||
|
||||
import Combine
|
||||
import Foundation
|
||||
import Mastodon
|
||||
import ServiceLayer
|
||||
|
||||
public class AccountListViewModel: ObservableObject {
|
||||
|
||||
public final class AccountListViewModel: ObservableObject {
|
||||
@Published public private(set) var items = [[CollectionItem]]()
|
||||
@Published public var alertItem: AlertItem?
|
||||
public let navigationEvents: AnyPublisher<NavigationEvent, Never>
|
||||
public private(set) var nextPageMaxID: String?
|
||||
|
||||
private let accountListService: AccountListService
|
||||
private var accounts = [String: Account]()
|
||||
private var accountViewModelCache = [Account: (AccountViewModel, AnyCancellable)]()
|
||||
private let navigationEventsSubject = PassthroughSubject<NavigationEvent, Never>()
|
||||
private let loadingSubject = PassthroughSubject<Bool, Never>()
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
init(accountListService: AccountListService) {
|
||||
self.accountListService = accountListService
|
||||
navigationEvents = navigationEventsSubject.eraseToAnyPublisher()
|
||||
|
||||
accountListService.accountSections
|
||||
.handleEvents(receiveOutput: { [weak self] in
|
||||
self?.cleanViewModelCache(newAccountSections: $0)
|
||||
self?.accounts = Dictionary(uniqueKeysWithValues: Set($0.reduce([], +)).map { ($0.id, $0) })
|
||||
})
|
||||
.map { $0.map { $0.map { CollectionItem(id: $0.id, kind: .account) } } }
|
||||
.receive(on: DispatchQueue.main)
|
||||
.assignErrorsToAlertItem(to: \.alertItem, on: self)
|
||||
.assign(to: &$items)
|
||||
|
||||
accountListService.nextPageMaxIDs
|
||||
.sink { [weak self] in self?.nextPageMaxID = $0 }
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
}
|
||||
|
||||
extension AccountListViewModel: CollectionViewModel {
|
||||
public var collectionItems: AnyPublisher<[[CollectionItem]], Never> { $items.eraseToAnyPublisher() }
|
||||
|
||||
public var title: AnyPublisher<String?, Never> { Just(nil).eraseToAnyPublisher() }
|
||||
|
||||
public var alertItems: AnyPublisher<AlertItem, Never> { $alertItem.compactMap { $0 }.eraseToAnyPublisher() }
|
||||
|
||||
public var loading: AnyPublisher<Bool, Never> { loadingSubject.eraseToAnyPublisher() }
|
||||
|
||||
public var maintainScrollPositionOfItem: CollectionItem? {
|
||||
nil
|
||||
}
|
||||
|
||||
public func request(maxID: String?, minID: String?) {
|
||||
accountListService.request(maxID: maxID, minID: minID)
|
||||
.receive(on: DispatchQueue.main)
|
||||
.assignErrorsToAlertItem(to: \.alertItem, on: self)
|
||||
.handleEvents(
|
||||
receiveSubscription: { [weak self] _ in self?.loadingSubject.send(true) },
|
||||
receiveCompletion: { [weak self] _ in self?.loadingSubject.send(false) })
|
||||
.sink { _ in }
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
||||
public func itemSelected(_ item: CollectionItem) {
|
||||
switch item.kind {
|
||||
case .account:
|
||||
navigationEventsSubject.send(
|
||||
.collectionNavigation(
|
||||
AccountStatusesViewModel(
|
||||
accountStatusesService: accountListService
|
||||
.navigationService
|
||||
.accountStatusesService(id: item.id))))
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func canSelect(item: CollectionItem) -> Bool {
|
||||
true
|
||||
}
|
||||
|
||||
public func viewModel(item: CollectionItem) -> Any? {
|
||||
switch item.kind {
|
||||
case .account:
|
||||
return accountViewModel(id: item.id)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private extension AccountListViewModel {
|
||||
func accountViewModel(id: String) -> AccountViewModel? {
|
||||
guard let account = accounts[id] else { return nil }
|
||||
|
||||
var accountViewModel: AccountViewModel
|
||||
|
||||
if let cachedViewModel = accountViewModelCache[account]?.0 {
|
||||
accountViewModel = cachedViewModel
|
||||
} else {
|
||||
accountViewModel = AccountViewModel(
|
||||
accountService: accountListService.navigationService.accountService(account: account))
|
||||
accountViewModelCache[account] = (accountViewModel,
|
||||
accountViewModel.events
|
||||
.flatMap { $0 }
|
||||
.assignErrorsToAlertItem(to: \.alertItem, on: self)
|
||||
.sink { [weak self] in
|
||||
guard
|
||||
let self = self,
|
||||
let event = NavigationEvent($0)
|
||||
else { return }
|
||||
|
||||
self.navigationEventsSubject.send(event)
|
||||
})
|
||||
}
|
||||
|
||||
return accountViewModel
|
||||
}
|
||||
|
||||
func cleanViewModelCache(newAccountSections: [[Account]]) {
|
||||
let newAccounts = Set(newAccountSections.reduce([], +))
|
||||
|
||||
accountViewModelCache = accountViewModelCache.filter { newAccounts.contains($0.key) }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,10 +6,14 @@ import Mastodon
|
|||
import ServiceLayer
|
||||
|
||||
public class AccountViewModel: ObservableObject {
|
||||
public let events: AnyPublisher<AnyPublisher<CollectionItemEvent, Error>, Never>
|
||||
|
||||
private let accountService: AccountService
|
||||
private let eventsSubject = PassthroughSubject<AnyPublisher<CollectionItemEvent, Error>, Never>()
|
||||
|
||||
init(accountService: AccountService) {
|
||||
self.accountService = accountService
|
||||
events = eventsSubject.eraseToAnyPublisher()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
// Copyright © 2020 Metabolist. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import ServiceLayer
|
||||
|
||||
public enum CollectionItemEvent {
|
||||
case ignorableOutput
|
||||
case navigation(Navigation)
|
||||
case accountListNavigation(AccountListViewModel)
|
||||
case share(URL)
|
||||
}
|
|
@ -7,3 +7,25 @@ public enum NavigationEvent {
|
|||
case urlNavigation(URL)
|
||||
case share(URL)
|
||||
}
|
||||
|
||||
extension NavigationEvent {
|
||||
init?(_ event: CollectionItemEvent) {
|
||||
switch event {
|
||||
case .ignorableOutput:
|
||||
return nil
|
||||
case let .navigation(item):
|
||||
switch item {
|
||||
case let .url(url):
|
||||
self = .urlNavigation(url)
|
||||
case let .statusList(statusListService):
|
||||
self = .collectionNavigation(StatusListViewModel(statusListService: statusListService))
|
||||
case let .accountStatuses(accountStatusesService):
|
||||
self = .collectionNavigation(AccountStatusesViewModel(accountStatusesService: accountStatusesService))
|
||||
}
|
||||
case let .accountListNavigation(accountListViewModel):
|
||||
self = .collectionNavigation(accountListViewModel)
|
||||
case let .share(url):
|
||||
self = .share(url)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -64,9 +64,7 @@ extension StatusListViewModel: CollectionViewModel {
|
|||
|
||||
public var alertItems: AnyPublisher<AlertItem, Never> { $alertItem.compactMap { $0 }.eraseToAnyPublisher() }
|
||||
|
||||
public var loading: AnyPublisher<Bool, Never> {
|
||||
loadingSubject.eraseToAnyPublisher()
|
||||
}
|
||||
public var loading: AnyPublisher<Bool, Never> { loadingSubject.eraseToAnyPublisher() }
|
||||
|
||||
public func itemSelected(_ item: CollectionItem) {
|
||||
switch item.kind {
|
||||
|
@ -76,7 +74,9 @@ extension StatusListViewModel: CollectionViewModel {
|
|||
navigationEventsSubject.send(
|
||||
.collectionNavigation(
|
||||
StatusListViewModel(
|
||||
statusListService: statusListService.contextService(statusID: displayStatusID))))
|
||||
statusListService: statusListService
|
||||
.navigationService
|
||||
.contextStatusListService(id: displayStatusID))))
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
@ -100,7 +100,15 @@ extension StatusListViewModel: CollectionViewModel {
|
|||
}
|
||||
}
|
||||
|
||||
public extension StatusListViewModel {
|
||||
private extension StatusListViewModel {
|
||||
static func filter(statusSections: [[Status]], regularExpression: String?) -> [[Status]] {
|
||||
guard let regEx = regularExpression else { return statusSections }
|
||||
|
||||
return statusSections.map {
|
||||
$0.filter { $0.filterableContent.range(of: regEx, options: [.regularExpression, .caseInsensitive]) == nil }
|
||||
}
|
||||
}
|
||||
|
||||
var contextParentID: String? { statusListService.contextParentID }
|
||||
|
||||
func statusViewModel(id: String) -> StatusViewModel? {
|
||||
|
@ -111,15 +119,18 @@ public extension StatusListViewModel {
|
|||
if let cachedViewModel = statusViewModelCache[status]?.0 {
|
||||
statusViewModel = cachedViewModel
|
||||
} else {
|
||||
statusViewModel = StatusViewModel(statusService: statusListService.statusService(status: status))
|
||||
statusViewModel = StatusViewModel(
|
||||
statusService: statusListService.navigationService.statusService(status: status))
|
||||
statusViewModelCache[status] = (statusViewModel,
|
||||
statusViewModel.events
|
||||
.flatMap { $0 }
|
||||
.assignErrorsToAlertItem(to: \.alertItem, on: self)
|
||||
.sink { [weak self] in
|
||||
guard let self = self,
|
||||
let event = self.navigationEvent(statusEvent: $0)
|
||||
guard
|
||||
let self = self,
|
||||
let event = NavigationEvent($0)
|
||||
else { return }
|
||||
|
||||
self.navigationEventsSubject.send(event)
|
||||
})
|
||||
}
|
||||
|
@ -131,44 +142,6 @@ public extension StatusListViewModel {
|
|||
|
||||
return statusViewModel
|
||||
}
|
||||
}
|
||||
|
||||
private extension StatusListViewModel {
|
||||
static func filter(statusSections: [[Status]], regularExpression: String?) -> [[Status]] {
|
||||
guard let regEx = regularExpression else { return statusSections }
|
||||
|
||||
return statusSections.map {
|
||||
$0.filter { $0.filterableContent.range(of: regEx, options: [.regularExpression, .caseInsensitive]) == nil }
|
||||
}
|
||||
}
|
||||
|
||||
func navigationEvent(statusEvent: StatusViewModel.Event) -> NavigationEvent? {
|
||||
switch statusEvent {
|
||||
case .ignorableOutput:
|
||||
return nil
|
||||
case let .navigation(item):
|
||||
switch item {
|
||||
case let .url(url):
|
||||
return .urlNavigation(url)
|
||||
case let .accountID(id):
|
||||
return .collectionNavigation(
|
||||
AccountStatusesViewModel(accountStatusesService: statusListService.service(accountID: id)))
|
||||
case let .statusID(id):
|
||||
return .collectionNavigation(
|
||||
StatusListViewModel(
|
||||
statusListService: statusListService.contextService(statusID: id)))
|
||||
case let .tag(tag):
|
||||
return .collectionNavigation(
|
||||
StatusListViewModel(
|
||||
statusListService: statusListService.service(timeline: Timeline.tag(tag))))
|
||||
}
|
||||
case let .accountListNavigation(accountListViewModel):
|
||||
// return .collectionNavigation(accountListViewModel)
|
||||
return nil
|
||||
case let .share(url):
|
||||
return .share(url)
|
||||
}
|
||||
}
|
||||
|
||||
func determineIfScrollPositionShouldBeMaintained(newStatusSections: [[Status]]) {
|
||||
maintainScrollPositionOfItem = nil // clear old value
|
||||
|
|
|
@ -22,10 +22,10 @@ public struct StatusViewModel {
|
|||
public var isReplyInContext = false
|
||||
public var hasReplyFollowing = false
|
||||
public var sensitiveContentToggled = false
|
||||
public let events: AnyPublisher<AnyPublisher<Event, Error>, Never>
|
||||
public let events: AnyPublisher<AnyPublisher<CollectionItemEvent, Error>, Never>
|
||||
|
||||
private let statusService: StatusService
|
||||
private let eventsSubject = PassthroughSubject<AnyPublisher<Event, Error>, Never>()
|
||||
private let eventsSubject = PassthroughSubject<AnyPublisher<CollectionItemEvent, Error>, Never>()
|
||||
|
||||
init(statusService: StatusService) {
|
||||
self.statusService = statusService
|
||||
|
@ -49,15 +49,6 @@ public struct StatusViewModel {
|
|||
}
|
||||
}
|
||||
|
||||
public extension StatusViewModel {
|
||||
enum Event {
|
||||
case ignorableOutput
|
||||
case navigation(URLItem)
|
||||
case accountListNavigation(AccountListViewModel)
|
||||
case share(URL)
|
||||
}
|
||||
}
|
||||
|
||||
public extension StatusViewModel {
|
||||
var shouldDisplaySensitiveContent: Bool {
|
||||
if statusService.status.displayStatus.sensitive {
|
||||
|
@ -118,31 +109,42 @@ public extension StatusViewModel {
|
|||
|
||||
func urlSelected(_ url: URL) {
|
||||
eventsSubject.send(
|
||||
statusService.urlService.item(url: url)
|
||||
.map { Event.navigation($0) }
|
||||
statusService.navigationService.item(url: url)
|
||||
.map { CollectionItemEvent.navigation($0) }
|
||||
.setFailureType(to: Error.self)
|
||||
.eraseToAnyPublisher())
|
||||
}
|
||||
|
||||
func accountSelected() {
|
||||
eventsSubject.send(
|
||||
Just(Event.navigation(.accountID(statusService.status.displayStatus.account.id)))
|
||||
Just(CollectionItemEvent.navigation(
|
||||
.accountStatuses(
|
||||
statusService.navigationService.accountStatusesService(
|
||||
id: statusService.status.displayStatus.account.id))))
|
||||
.setFailureType(to: Error.self)
|
||||
.eraseToAnyPublisher())
|
||||
}
|
||||
|
||||
func favoritedBySelected() {
|
||||
|
||||
eventsSubject.send(
|
||||
Just(CollectionItemEvent.accountListNavigation(
|
||||
AccountListViewModel(
|
||||
accountListService: statusService.favoritedByService())))
|
||||
.setFailureType(to: Error.self)
|
||||
.eraseToAnyPublisher())
|
||||
}
|
||||
|
||||
func toggleFavorited() {
|
||||
eventsSubject.send(statusService.toggleFavorited().map { _ in Event.ignorableOutput }.eraseToAnyPublisher())
|
||||
eventsSubject.send(
|
||||
statusService.toggleFavorited()
|
||||
.map { _ in CollectionItemEvent.ignorableOutput }
|
||||
.eraseToAnyPublisher())
|
||||
}
|
||||
|
||||
func shareStatus() {
|
||||
guard let url = statusService.status.displayStatus.url else { return }
|
||||
|
||||
eventsSubject.send(Just(Event.share(url)).setFailureType(to: Error.self).eraseToAnyPublisher())
|
||||
eventsSubject.send(Just(CollectionItemEvent.share(url)).setFailureType(to: Error.self).eraseToAnyPublisher())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue