mirror of
https://github.com/metabolist/metatext.git
synced 2024-11-22 00:01:00 +00:00
Refactoring
This commit is contained in:
parent
dc7d0df55b
commit
feef7d794d
8 changed files with 68 additions and 83 deletions
|
@ -8,10 +8,10 @@ final class ProfileViewController: TableViewController {
|
|||
private let viewModel: ProfileViewModel
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
required init(viewModel: ProfileViewModel) {
|
||||
required init(viewModel: ProfileViewModel, identification: Identification) {
|
||||
self.viewModel = viewModel
|
||||
|
||||
super.init(viewModel: viewModel)
|
||||
super.init(viewModel: viewModel, identification: identification)
|
||||
}
|
||||
|
||||
override func viewDidLoad() {
|
||||
|
|
|
@ -7,6 +7,7 @@ import ViewModels
|
|||
|
||||
class TableViewController: UITableViewController {
|
||||
private let viewModel: CollectionViewModel
|
||||
private let identification: Identification
|
||||
private let loadingTableFooterView = LoadingTableFooterView()
|
||||
private let webfingerIndicatorView = WebfingerIndicatorView()
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
@ -37,8 +38,9 @@ class TableViewController: UITableViewController {
|
|||
}
|
||||
}()
|
||||
|
||||
init(viewModel: CollectionViewModel) {
|
||||
init(viewModel: CollectionViewModel, identification: Identification) {
|
||||
self.viewModel = viewModel
|
||||
self.identification = identification
|
||||
|
||||
super.init(style: .plain)
|
||||
}
|
||||
|
@ -183,25 +185,9 @@ private extension TableViewController {
|
|||
|
||||
viewModel.sections.sink { [weak self] in self?.update(items: $0) }.store(in: &cancellables)
|
||||
|
||||
viewModel.navigationEvents.receive(on: DispatchQueue.main).sink { [weak self] in
|
||||
guard let self = self else { return }
|
||||
|
||||
switch $0 {
|
||||
case let .share(url):
|
||||
self.share(url: url)
|
||||
case let .collectionNavigation(viewModel):
|
||||
self.show(TableViewController(viewModel: viewModel), sender: self)
|
||||
case let .profileNavigation(viewModel):
|
||||
self.show(ProfileViewController(viewModel: viewModel), sender: self)
|
||||
case let .urlNavigation(url):
|
||||
self.present(SFSafariViewController(url: url), animated: true)
|
||||
case .webfingerStart:
|
||||
self.webfingerIndicatorView.startAnimating()
|
||||
case .webfingerEnd:
|
||||
self.webfingerIndicatorView.stopAnimating()
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
viewModel.events.receive(on: DispatchQueue.main)
|
||||
.sink { [weak self] in self?.handle(event: $0) }
|
||||
.store(in: &cancellables)
|
||||
|
||||
viewModel.loading.receive(on: RunLoop.main).sink { [weak self] in
|
||||
guard let self = self else { return }
|
||||
|
@ -246,6 +232,38 @@ private extension TableViewController {
|
|||
}
|
||||
}
|
||||
|
||||
func handle(event: CollectionItemEvent) {
|
||||
switch event {
|
||||
case .ignorableOutput:
|
||||
break
|
||||
case let .share(url):
|
||||
share(url: url)
|
||||
case let.navigation(navigation):
|
||||
switch navigation {
|
||||
case let .collection(collectionService):
|
||||
show(TableViewController(
|
||||
viewModel: CollectionItemsViewModel(
|
||||
collectionService: collectionService,
|
||||
identification: identification),
|
||||
identification: identification),
|
||||
sender: self)
|
||||
case let .profile(profileService):
|
||||
show(ProfileViewController(
|
||||
viewModel: ProfileViewModel(
|
||||
profileService: profileService,
|
||||
identification: identification),
|
||||
identification: identification),
|
||||
sender: self)
|
||||
case let .url(url):
|
||||
present(SFSafariViewController(url: url), animated: true)
|
||||
case .webfingerStart:
|
||||
webfingerIndicatorView.startAnimating()
|
||||
case .webfingerEnd:
|
||||
webfingerIndicatorView.stopAnimating()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func share(url: URL) {
|
||||
let activityViewController = UIActivityViewController(activityItems: [url], applicationActivities: nil)
|
||||
|
||||
|
|
|
@ -12,15 +12,17 @@ final public class CollectionItemsViewModel: ObservableObject {
|
|||
|
||||
private let items = CurrentValueSubject<[[CollectionItem]], Never>([])
|
||||
private let collectionService: CollectionService
|
||||
private let identification: Identification
|
||||
private var viewModelCache = [CollectionItem: (viewModel: CollectionItemViewModel, events: AnyCancellable)]()
|
||||
private let navigationEventsSubject = PassthroughSubject<NavigationEvent, Never>()
|
||||
private let eventsSubject = PassthroughSubject<CollectionItemEvent, Never>()
|
||||
private let loadingSubject = PassthroughSubject<Bool, Never>()
|
||||
private var topVisibleIndexPath = IndexPath(item: 0, section: 0)
|
||||
private var lastSelectedLoadMore: LoadMore?
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
init(collectionService: CollectionService) {
|
||||
public init(collectionService: CollectionService, identification: Identification) {
|
||||
self.collectionService = collectionService
|
||||
self.identification = identification
|
||||
|
||||
collectionService.sections
|
||||
.handleEvents(receiveOutput: { [weak self] in self?.process(items: $0) })
|
||||
|
@ -46,7 +48,7 @@ extension CollectionItemsViewModel: CollectionViewModel {
|
|||
|
||||
public var loading: AnyPublisher<Bool, Never> { loadingSubject.eraseToAnyPublisher() }
|
||||
|
||||
public var navigationEvents: AnyPublisher<NavigationEvent, Never> { navigationEventsSubject.eraseToAnyPublisher() }
|
||||
public var events: AnyPublisher<CollectionItemEvent, Never> { eventsSubject.eraseToAnyPublisher() }
|
||||
|
||||
public func request(maxId: String? = nil, minId: String? = nil) {
|
||||
collectionService.request(maxId: maxId, minId: minId)
|
||||
|
@ -64,20 +66,18 @@ extension CollectionItemsViewModel: CollectionViewModel {
|
|||
|
||||
switch item {
|
||||
case let .status(status, _):
|
||||
navigationEventsSubject.send(
|
||||
.collectionNavigation(
|
||||
CollectionItemsViewModel(
|
||||
collectionService: collectionService
|
||||
.navigationService
|
||||
.contextService(id: status.displayStatus.id))))
|
||||
eventsSubject.send(
|
||||
.navigation(.collection(collectionService
|
||||
.navigationService
|
||||
.contextService(id: status.displayStatus.id))))
|
||||
case let .loadMore(loadMore):
|
||||
lastSelectedLoadMore = loadMore
|
||||
(viewModel(indexPath: indexPath) as? LoadMoreViewModel)?.loadMore()
|
||||
case let .account(account):
|
||||
navigationEventsSubject.send(
|
||||
.profileNavigation(
|
||||
ProfileViewModel(
|
||||
profileService: collectionService.navigationService.profileService(account: account))))
|
||||
eventsSubject.send(
|
||||
.navigation(.profile(collectionService
|
||||
.navigationService
|
||||
.profileService(account: account))))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -142,9 +142,9 @@ extension CollectionItemsViewModel: CollectionViewModel {
|
|||
|
||||
private extension CollectionItemsViewModel {
|
||||
func cache(viewModel: CollectionItemViewModel, forItem item: CollectionItem) {
|
||||
viewModelCache[item] = (viewModel, viewModel.events.flatMap { $0.compactMap(NavigationEvent.init) }
|
||||
viewModelCache[item] = (viewModel, viewModel.events.flatMap { $0 }
|
||||
.assignErrorsToAlertItem(to: \.alertItem, on: self)
|
||||
.sink { [weak self] in self?.navigationEventsSubject.send($0) })
|
||||
.sink { [weak self] in self?.eventsSubject.send($0) })
|
||||
}
|
||||
|
||||
func process(items: [[CollectionItem]]) {
|
||||
|
|
|
@ -8,7 +8,7 @@ public protocol CollectionViewModel {
|
|||
var title: AnyPublisher<String, Never> { get }
|
||||
var alertItems: AnyPublisher<AlertItem, Never> { get }
|
||||
var loading: AnyPublisher<Bool, Never> { get }
|
||||
var navigationEvents: AnyPublisher<NavigationEvent, Never> { get }
|
||||
var events: AnyPublisher<CollectionItemEvent, Never> { get }
|
||||
var nextPageMaxId: String? { get }
|
||||
var maintainScrollPositionOfItem: CollectionItemIdentifier? { get }
|
||||
func request(maxId: String?, minId: String?)
|
||||
|
|
|
@ -1,36 +0,0 @@
|
|||
// Copyright © 2020 Metabolist. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
|
||||
public enum NavigationEvent {
|
||||
case collectionNavigation(CollectionViewModel)
|
||||
case profileNavigation(ProfileViewModel)
|
||||
case urlNavigation(URL)
|
||||
case share(URL)
|
||||
case webfingerStart
|
||||
case webfingerEnd
|
||||
}
|
||||
|
||||
extension NavigationEvent {
|
||||
public init?(_ event: CollectionItemEvent) {
|
||||
switch event {
|
||||
case .ignorableOutput:
|
||||
return nil
|
||||
case let .navigation(item):
|
||||
switch item {
|
||||
case let .url(url):
|
||||
self = .urlNavigation(url)
|
||||
case let .collection(statusListService):
|
||||
self = .collectionNavigation(CollectionItemsViewModel(collectionService: statusListService))
|
||||
case let .profile(profileService):
|
||||
self = .profileNavigation(ProfileViewModel(profileService: profileService))
|
||||
case .webfingerStart:
|
||||
self = .webfingerStart
|
||||
case .webfingerEnd:
|
||||
self = .webfingerEnd
|
||||
}
|
||||
case let .share(url):
|
||||
self = .share(url)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -91,7 +91,9 @@ public extension NavigationViewModel {
|
|||
}
|
||||
|
||||
func viewModel(timeline: Timeline) -> CollectionItemsViewModel {
|
||||
CollectionItemsViewModel(collectionService: identification.service.service(timeline: timeline))
|
||||
CollectionItemsViewModel(
|
||||
collectionService: identification.service.service(timeline: timeline),
|
||||
identification: identification)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -14,11 +14,13 @@ final public class ProfileViewModel {
|
|||
private let collectionViewModel: CurrentValueSubject<CollectionItemsViewModel, Never>
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
init(profileService: ProfileService) {
|
||||
public init(profileService: ProfileService, identification: Identification) {
|
||||
self.profileService = profileService
|
||||
|
||||
collectionViewModel = CurrentValueSubject(
|
||||
CollectionItemsViewModel(collectionService: profileService.timelineService(profileCollection: .statuses)))
|
||||
CollectionItemsViewModel(
|
||||
collectionService: profileService.timelineService(profileCollection: .statuses),
|
||||
identification: identification))
|
||||
|
||||
profileService.accountServicePublisher
|
||||
.map(AccountViewModel.init(accountService:))
|
||||
|
@ -27,7 +29,7 @@ final public class ProfileViewModel {
|
|||
|
||||
$collection.dropFirst()
|
||||
.map(profileService.timelineService(profileCollection:))
|
||||
.map(CollectionItemsViewModel.init(collectionService:))
|
||||
.map { CollectionItemsViewModel(collectionService: $0, identification: identification) }
|
||||
.sink { [weak self] in
|
||||
guard let self = self else { return }
|
||||
|
||||
|
@ -55,14 +57,12 @@ extension ProfileViewModel: CollectionViewModel {
|
|||
collectionViewModel.flatMap(\.loading).eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
public var navigationEvents: AnyPublisher<NavigationEvent, Never> {
|
||||
public var events: AnyPublisher<CollectionItemEvent, Never> {
|
||||
$accountViewModel.compactMap { $0 }
|
||||
.flatMap(\.events)
|
||||
.flatMap { $0 }
|
||||
.map(NavigationEvent.init)
|
||||
.compactMap { $0 }
|
||||
.assignErrorsToAlertItem(to: \.alertItem, on: self)
|
||||
.merge(with: collectionViewModel.flatMap(\.navigationEvents))
|
||||
.merge(with: collectionViewModel.flatMap(\.events))
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
|
|
|
@ -4,10 +4,11 @@ import SwiftUI
|
|||
import ViewModels
|
||||
|
||||
struct TableView: UIViewControllerRepresentable {
|
||||
@EnvironmentObject var identification: Identification
|
||||
let viewModel: CollectionViewModel
|
||||
|
||||
func makeUIViewController(context: Context) -> TableViewController {
|
||||
TableViewController(viewModel: viewModel)
|
||||
TableViewController(viewModel: viewModel, identification: identification)
|
||||
}
|
||||
|
||||
func updateUIViewController(_ uiViewController: TableViewController, context: Context) {
|
||||
|
|
Loading…
Reference in a new issue