From 6b4e17e41e4e1638cfdfa66b8edd9024166c7a96 Mon Sep 17 00:00:00 2001 From: Justin Mazzocchi <2831158+jzzocc@users.noreply.github.com> Date: Tue, 26 Jan 2021 16:15:52 -0800 Subject: [PATCH] Account list edits --- .../Services/AccountListService.swift | 21 +++++++----- View Controllers/TableViewController.swift | 6 ++++ .../Entities/CollectionItemEvent.swift | 8 +++++ .../View Models/AccountViewModel.swift | 33 +++++++++---------- .../CollectionItemsViewModel.swift | 14 ++++++++ .../View Models/CollectionViewModel.swift | 1 + .../View Models/ProfileViewModel.swift | 4 +++ Views/AccountView.swift | 1 + 8 files changed, 61 insertions(+), 27 deletions(-) diff --git a/ServiceLayer/Sources/ServiceLayer/Services/AccountListService.swift b/ServiceLayer/Sources/ServiceLayer/Services/AccountListService.swift index 2a90de1..8c8aeb8 100644 --- a/ServiceLayer/Sources/ServiceLayer/Services/AccountListService.swift +++ b/ServiceLayer/Sources/ServiceLayer/Services/AccountListService.swift @@ -12,7 +12,7 @@ public struct AccountListService { public let navigationService: NavigationService public let canRefresh = false - private let accountsSubject = PassthroughSubject<[Account], Error>() + private let accountsSubject = CurrentValueSubject<[Account], Error>([]) private let endpoint: AccountsEndpoint private let mastodonAPIClient: MastodonAPIClient private let contentDatabase: ContentDatabase @@ -27,23 +27,26 @@ public struct AccountListService { self.mastodonAPIClient = mastodonAPIClient self.contentDatabase = contentDatabase self.titleComponents = titleComponents - sections = accountsSubject.scan([]) { - let presentIds = Set($0.map(\.id)) - - return $0 + $1.filter { !presentIds.contains($0.id) } - } - .map { [.init(items: $0.map { CollectionItem.account($0, endpoint.configuration) })] } - .eraseToAnyPublisher() + sections = accountsSubject + .map { [.init(items: $0.map { CollectionItem.account($0, endpoint.configuration) })] } + .eraseToAnyPublisher() nextPageMaxId = nextPageMaxIdSubject.eraseToAnyPublisher() navigationService = NavigationService(mastodonAPIClient: mastodonAPIClient, contentDatabase: contentDatabase) } } +public extension AccountListService { + func remove(id: Account.Id) { + accountsSubject.value.removeAll { $0.id == id } + } +} + extension AccountListService: CollectionService { public func request(maxId: String?, minId: String?, search: Search?) -> AnyPublisher { mastodonAPIClient.pagedRequest(endpoint, maxId: maxId, minId: minId) .handleEvents(receiveOutput: { - accountsSubject.send($0.result) + let presentIds = Set(accountsSubject.value.map(\.id)) + accountsSubject.value.append(contentsOf: $0.result.filter { !presentIds.contains($0.id) }) guard let maxId = $0.info.maxId else { return } diff --git a/View Controllers/TableViewController.swift b/View Controllers/TableViewController.swift index db9b283..9681032 100644 --- a/View Controllers/TableViewController.swift +++ b/View Controllers/TableViewController.swift @@ -334,6 +334,8 @@ private extension TableViewController { confirmDelete(statusViewModel: statusViewModel, redraft: redraft) case let .report(reportViewModel): report(reportViewModel: reportViewModel) + case let .accountListEdit(accountViewModel, edit): + accountListEdit(accountViewModel: accountViewModel, edit: edit) } } @@ -454,6 +456,10 @@ private extension TableViewController { present(alertController, animated: true) } + func accountListEdit(accountViewModel: AccountViewModel, edit: CollectionItemEvent.AccountListEdit) { + viewModel.applyAccountListEdit(viewModel: accountViewModel, edit: edit) + } + func set(expandAllState: ExpandAllState) { switch expandAllState { case .hidden: diff --git a/ViewModels/Sources/ViewModels/Entities/CollectionItemEvent.swift b/ViewModels/Sources/ViewModels/Entities/CollectionItemEvent.swift index 317fddd..fb513c0 100644 --- a/ViewModels/Sources/ViewModels/Entities/CollectionItemEvent.swift +++ b/ViewModels/Sources/ViewModels/Entities/CollectionItemEvent.swift @@ -12,4 +12,12 @@ public enum CollectionItemEvent { case confirmDelete(StatusViewModel, redraft: Bool) case report(ReportViewModel) case share(URL) + case accountListEdit(AccountViewModel, AccountListEdit) +} + +public extension CollectionItemEvent { + enum AccountListEdit { + case acceptFollowRequest + case rejectFollowRequest + } } diff --git a/ViewModels/Sources/ViewModels/View Models/AccountViewModel.swift b/ViewModels/Sources/ViewModels/View Models/AccountViewModel.swift index 1db81fd..03f32e0 100644 --- a/ViewModels/Sources/ViewModels/View Models/AccountViewModel.swift +++ b/ViewModels/Sources/ViewModels/View Models/AccountViewModel.swift @@ -21,6 +21,8 @@ public final class AccountViewModel: CollectionItemViewModel, ObservableObject { } public extension AccountViewModel { + var id: Account.Id { accountService.account.id } + var headerURL: URL { if !identityContext.appPreferences.shouldReduceMotion, identityContext.appPreferences.animateHeaders { return accountService.account.header @@ -140,27 +142,11 @@ public extension AccountViewModel { } func acceptFollowRequest() { - ignorableOutputEvent( - accountService.acceptFollowRequest() - .collect() - .flatMap { [weak self] _ -> AnyPublisher in - guard let self = self else { return Empty().eraseToAnyPublisher() } - - return self.identityContext.service.verifyCredentials() - } - .eraseToAnyPublisher()) + accountListEdit(accountService.acceptFollowRequest(), event: .acceptFollowRequest) } func rejectFollowRequest() { - ignorableOutputEvent( - accountService.rejectFollowRequest() - .collect() - .flatMap { [weak self] _ -> AnyPublisher in - guard let self = self else { return Empty().eraseToAnyPublisher() } - - return self.identityContext.service.verifyCredentials() - } - .eraseToAnyPublisher()) + accountListEdit(accountService.rejectFollowRequest(), event: .rejectFollowRequest) } func domainBlock() { @@ -176,4 +162,15 @@ private extension AccountViewModel { func ignorableOutputEvent(_ action: AnyPublisher) { eventsSubject.send(action.map { _ in .ignorableOutput }.eraseToAnyPublisher()) } + + func accountListEdit(_ action: AnyPublisher, event: CollectionItemEvent.AccountListEdit) { + eventsSubject.send( + action.collect() + .map { [weak self] _ -> CollectionItemEvent in + guard let self = self else { return .ignorableOutput } + + return .accountListEdit(self, .acceptFollowRequest) + } + .eraseToAnyPublisher()) + } } diff --git a/ViewModels/Sources/ViewModels/View Models/CollectionItemsViewModel.swift b/ViewModels/Sources/ViewModels/View Models/CollectionItemsViewModel.swift index 5238c23..0ba67ff 100644 --- a/ViewModels/Sources/ViewModels/View Models/CollectionItemsViewModel.swift +++ b/ViewModels/Sources/ViewModels/View Models/CollectionItemsViewModel.swift @@ -321,6 +321,20 @@ extension CollectionItemsViewModel: CollectionViewModel { .store(in: &cancellables) } } + + public func applyAccountListEdit(viewModel: AccountViewModel, edit: CollectionItemEvent.AccountListEdit) { + (collectionService as? AccountListService)?.remove(id: viewModel.id) + + switch edit { + case .acceptFollowRequest, .rejectFollowRequest: + identityContext.service.verifyCredentials() + .assignErrorsToAlertItem(to: \.alertItem, on: self) + .sink { _ in } + .store(in: &cancellables) + default: + break + } + } } private extension CollectionItemsViewModel { diff --git a/ViewModels/Sources/ViewModels/View Models/CollectionViewModel.swift b/ViewModels/Sources/ViewModels/View Models/CollectionViewModel.swift index 5b93468..7aeee16 100644 --- a/ViewModels/Sources/ViewModels/View Models/CollectionViewModel.swift +++ b/ViewModels/Sources/ViewModels/View Models/CollectionViewModel.swift @@ -21,4 +21,5 @@ public protocol CollectionViewModel { func canSelect(indexPath: IndexPath) -> Bool func viewModel(indexPath: IndexPath) -> CollectionItemViewModel func toggleExpandAll() + func applyAccountListEdit(viewModel: AccountViewModel, edit: CollectionItemEvent.AccountListEdit) } diff --git a/ViewModels/Sources/ViewModels/View Models/ProfileViewModel.swift b/ViewModels/Sources/ViewModels/View Models/ProfileViewModel.swift index 2b5bf2d..f7fe9ea 100644 --- a/ViewModels/Sources/ViewModels/View Models/ProfileViewModel.swift +++ b/ViewModels/Sources/ViewModels/View Models/ProfileViewModel.swift @@ -139,4 +139,8 @@ extension ProfileViewModel: CollectionViewModel { public func toggleExpandAll() { collectionViewModel.value.toggleExpandAll() } + + public func applyAccountListEdit(viewModel: AccountViewModel, edit: CollectionItemEvent.AccountListEdit) { + collectionViewModel.value.applyAccountListEdit(viewModel: viewModel, edit: edit) + } } diff --git a/Views/AccountView.swift b/Views/AccountView.swift index 973fb5b..61d52c6 100644 --- a/Views/AccountView.swift +++ b/Views/AccountView.swift @@ -80,6 +80,7 @@ extension AccountView: UITextViewDelegate { } private extension AccountView { + // swiftlint:disable:next function_body_length func initialSetup() { let stackView = UIStackView()