Account list edits

This commit is contained in:
Justin Mazzocchi 2021-01-26 16:15:52 -08:00
parent 1fabcb41cc
commit 6b4e17e41e
No known key found for this signature in database
GPG key ID: E223E6937AAFB01C
8 changed files with 61 additions and 27 deletions

View file

@ -12,7 +12,7 @@ public struct AccountListService {
public let navigationService: NavigationService public let navigationService: NavigationService
public let canRefresh = false public let canRefresh = false
private let accountsSubject = PassthroughSubject<[Account], Error>() private let accountsSubject = CurrentValueSubject<[Account], Error>([])
private let endpoint: AccountsEndpoint private let endpoint: AccountsEndpoint
private let mastodonAPIClient: MastodonAPIClient private let mastodonAPIClient: MastodonAPIClient
private let contentDatabase: ContentDatabase private let contentDatabase: ContentDatabase
@ -27,11 +27,7 @@ public struct AccountListService {
self.mastodonAPIClient = mastodonAPIClient self.mastodonAPIClient = mastodonAPIClient
self.contentDatabase = contentDatabase self.contentDatabase = contentDatabase
self.titleComponents = titleComponents self.titleComponents = titleComponents
sections = accountsSubject.scan([]) { sections = accountsSubject
let presentIds = Set($0.map(\.id))
return $0 + $1.filter { !presentIds.contains($0.id) }
}
.map { [.init(items: $0.map { CollectionItem.account($0, endpoint.configuration) })] } .map { [.init(items: $0.map { CollectionItem.account($0, endpoint.configuration) })] }
.eraseToAnyPublisher() .eraseToAnyPublisher()
nextPageMaxId = nextPageMaxIdSubject.eraseToAnyPublisher() nextPageMaxId = nextPageMaxIdSubject.eraseToAnyPublisher()
@ -39,11 +35,18 @@ public struct AccountListService {
} }
} }
public extension AccountListService {
func remove(id: Account.Id) {
accountsSubject.value.removeAll { $0.id == id }
}
}
extension AccountListService: CollectionService { extension AccountListService: CollectionService {
public func request(maxId: String?, minId: String?, search: Search?) -> AnyPublisher<Never, Error> { public func request(maxId: String?, minId: String?, search: Search?) -> AnyPublisher<Never, Error> {
mastodonAPIClient.pagedRequest(endpoint, maxId: maxId, minId: minId) mastodonAPIClient.pagedRequest(endpoint, maxId: maxId, minId: minId)
.handleEvents(receiveOutput: { .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 } guard let maxId = $0.info.maxId else { return }

View file

@ -334,6 +334,8 @@ private extension TableViewController {
confirmDelete(statusViewModel: statusViewModel, redraft: redraft) confirmDelete(statusViewModel: statusViewModel, redraft: redraft)
case let .report(reportViewModel): case let .report(reportViewModel):
report(reportViewModel: 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) present(alertController, animated: true)
} }
func accountListEdit(accountViewModel: AccountViewModel, edit: CollectionItemEvent.AccountListEdit) {
viewModel.applyAccountListEdit(viewModel: accountViewModel, edit: edit)
}
func set(expandAllState: ExpandAllState) { func set(expandAllState: ExpandAllState) {
switch expandAllState { switch expandAllState {
case .hidden: case .hidden:

View file

@ -12,4 +12,12 @@ public enum CollectionItemEvent {
case confirmDelete(StatusViewModel, redraft: Bool) case confirmDelete(StatusViewModel, redraft: Bool)
case report(ReportViewModel) case report(ReportViewModel)
case share(URL) case share(URL)
case accountListEdit(AccountViewModel, AccountListEdit)
}
public extension CollectionItemEvent {
enum AccountListEdit {
case acceptFollowRequest
case rejectFollowRequest
}
} }

View file

@ -21,6 +21,8 @@ public final class AccountViewModel: CollectionItemViewModel, ObservableObject {
} }
public extension AccountViewModel { public extension AccountViewModel {
var id: Account.Id { accountService.account.id }
var headerURL: URL { var headerURL: URL {
if !identityContext.appPreferences.shouldReduceMotion, identityContext.appPreferences.animateHeaders { if !identityContext.appPreferences.shouldReduceMotion, identityContext.appPreferences.animateHeaders {
return accountService.account.header return accountService.account.header
@ -140,27 +142,11 @@ public extension AccountViewModel {
} }
func acceptFollowRequest() { func acceptFollowRequest() {
ignorableOutputEvent( accountListEdit(accountService.acceptFollowRequest(), event: .acceptFollowRequest)
accountService.acceptFollowRequest()
.collect()
.flatMap { [weak self] _ -> AnyPublisher<Never, Error> in
guard let self = self else { return Empty().eraseToAnyPublisher() }
return self.identityContext.service.verifyCredentials()
}
.eraseToAnyPublisher())
} }
func rejectFollowRequest() { func rejectFollowRequest() {
ignorableOutputEvent( accountListEdit(accountService.rejectFollowRequest(), event: .rejectFollowRequest)
accountService.rejectFollowRequest()
.collect()
.flatMap { [weak self] _ -> AnyPublisher<Never, Error> in
guard let self = self else { return Empty().eraseToAnyPublisher() }
return self.identityContext.service.verifyCredentials()
}
.eraseToAnyPublisher())
} }
func domainBlock() { func domainBlock() {
@ -176,4 +162,15 @@ private extension AccountViewModel {
func ignorableOutputEvent(_ action: AnyPublisher<Never, Error>) { func ignorableOutputEvent(_ action: AnyPublisher<Never, Error>) {
eventsSubject.send(action.map { _ in .ignorableOutput }.eraseToAnyPublisher()) eventsSubject.send(action.map { _ in .ignorableOutput }.eraseToAnyPublisher())
} }
func accountListEdit(_ action: AnyPublisher<Never, Error>, event: CollectionItemEvent.AccountListEdit) {
eventsSubject.send(
action.collect()
.map { [weak self] _ -> CollectionItemEvent in
guard let self = self else { return .ignorableOutput }
return .accountListEdit(self, .acceptFollowRequest)
}
.eraseToAnyPublisher())
}
} }

View file

@ -321,6 +321,20 @@ extension CollectionItemsViewModel: CollectionViewModel {
.store(in: &cancellables) .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 { private extension CollectionItemsViewModel {

View file

@ -21,4 +21,5 @@ public protocol CollectionViewModel {
func canSelect(indexPath: IndexPath) -> Bool func canSelect(indexPath: IndexPath) -> Bool
func viewModel(indexPath: IndexPath) -> CollectionItemViewModel func viewModel(indexPath: IndexPath) -> CollectionItemViewModel
func toggleExpandAll() func toggleExpandAll()
func applyAccountListEdit(viewModel: AccountViewModel, edit: CollectionItemEvent.AccountListEdit)
} }

View file

@ -139,4 +139,8 @@ extension ProfileViewModel: CollectionViewModel {
public func toggleExpandAll() { public func toggleExpandAll() {
collectionViewModel.value.toggleExpandAll() collectionViewModel.value.toggleExpandAll()
} }
public func applyAccountListEdit(viewModel: AccountViewModel, edit: CollectionItemEvent.AccountListEdit) {
collectionViewModel.value.applyAccountListEdit(viewModel: viewModel, edit: edit)
}
} }

View file

@ -80,6 +80,7 @@ extension AccountView: UITextViewDelegate {
} }
private extension AccountView { private extension AccountView {
// swiftlint:disable:next function_body_length
func initialSetup() { func initialSetup() {
let stackView = UIStackView() let stackView = UIStackView()