mirror of
https://github.com/metabolist/metatext.git
synced 2024-11-25 09:41: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 let viewModel: ProfileViewModel
|
||||||
private var cancellables = Set<AnyCancellable>()
|
private var cancellables = Set<AnyCancellable>()
|
||||||
|
|
||||||
required init(viewModel: ProfileViewModel) {
|
required init(viewModel: ProfileViewModel, identification: Identification) {
|
||||||
self.viewModel = viewModel
|
self.viewModel = viewModel
|
||||||
|
|
||||||
super.init(viewModel: viewModel)
|
super.init(viewModel: viewModel, identification: identification)
|
||||||
}
|
}
|
||||||
|
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
|
|
|
@ -7,6 +7,7 @@ import ViewModels
|
||||||
|
|
||||||
class TableViewController: UITableViewController {
|
class TableViewController: UITableViewController {
|
||||||
private let viewModel: CollectionViewModel
|
private let viewModel: CollectionViewModel
|
||||||
|
private let identification: Identification
|
||||||
private let loadingTableFooterView = LoadingTableFooterView()
|
private let loadingTableFooterView = LoadingTableFooterView()
|
||||||
private let webfingerIndicatorView = WebfingerIndicatorView()
|
private let webfingerIndicatorView = WebfingerIndicatorView()
|
||||||
private var cancellables = Set<AnyCancellable>()
|
private var cancellables = Set<AnyCancellable>()
|
||||||
|
@ -37,8 +38,9 @@ class TableViewController: UITableViewController {
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
init(viewModel: CollectionViewModel) {
|
init(viewModel: CollectionViewModel, identification: Identification) {
|
||||||
self.viewModel = viewModel
|
self.viewModel = viewModel
|
||||||
|
self.identification = identification
|
||||||
|
|
||||||
super.init(style: .plain)
|
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.sections.sink { [weak self] in self?.update(items: $0) }.store(in: &cancellables)
|
||||||
|
|
||||||
viewModel.navigationEvents.receive(on: DispatchQueue.main).sink { [weak self] in
|
viewModel.events.receive(on: DispatchQueue.main)
|
||||||
guard let self = self else { return }
|
.sink { [weak self] in self?.handle(event: $0) }
|
||||||
|
.store(in: &cancellables)
|
||||||
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.loading.receive(on: RunLoop.main).sink { [weak self] in
|
viewModel.loading.receive(on: RunLoop.main).sink { [weak self] in
|
||||||
guard let self = self else { return }
|
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) {
|
func share(url: URL) {
|
||||||
let activityViewController = UIActivityViewController(activityItems: [url], applicationActivities: nil)
|
let activityViewController = UIActivityViewController(activityItems: [url], applicationActivities: nil)
|
||||||
|
|
||||||
|
|
|
@ -12,15 +12,17 @@ final public class CollectionItemsViewModel: ObservableObject {
|
||||||
|
|
||||||
private let items = CurrentValueSubject<[[CollectionItem]], Never>([])
|
private let items = CurrentValueSubject<[[CollectionItem]], Never>([])
|
||||||
private let collectionService: CollectionService
|
private let collectionService: CollectionService
|
||||||
|
private let identification: Identification
|
||||||
private var viewModelCache = [CollectionItem: (viewModel: CollectionItemViewModel, events: AnyCancellable)]()
|
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 let loadingSubject = PassthroughSubject<Bool, Never>()
|
||||||
private var topVisibleIndexPath = IndexPath(item: 0, section: 0)
|
private var topVisibleIndexPath = IndexPath(item: 0, section: 0)
|
||||||
private var lastSelectedLoadMore: LoadMore?
|
private var lastSelectedLoadMore: LoadMore?
|
||||||
private var cancellables = Set<AnyCancellable>()
|
private var cancellables = Set<AnyCancellable>()
|
||||||
|
|
||||||
init(collectionService: CollectionService) {
|
public init(collectionService: CollectionService, identification: Identification) {
|
||||||
self.collectionService = collectionService
|
self.collectionService = collectionService
|
||||||
|
self.identification = identification
|
||||||
|
|
||||||
collectionService.sections
|
collectionService.sections
|
||||||
.handleEvents(receiveOutput: { [weak self] in self?.process(items: $0) })
|
.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 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) {
|
public func request(maxId: String? = nil, minId: String? = nil) {
|
||||||
collectionService.request(maxId: maxId, minId: minId)
|
collectionService.request(maxId: maxId, minId: minId)
|
||||||
|
@ -64,20 +66,18 @@ extension CollectionItemsViewModel: CollectionViewModel {
|
||||||
|
|
||||||
switch item {
|
switch item {
|
||||||
case let .status(status, _):
|
case let .status(status, _):
|
||||||
navigationEventsSubject.send(
|
eventsSubject.send(
|
||||||
.collectionNavigation(
|
.navigation(.collection(collectionService
|
||||||
CollectionItemsViewModel(
|
.navigationService
|
||||||
collectionService: collectionService
|
.contextService(id: status.displayStatus.id))))
|
||||||
.navigationService
|
|
||||||
.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):
|
||||||
navigationEventsSubject.send(
|
eventsSubject.send(
|
||||||
.profileNavigation(
|
.navigation(.profile(collectionService
|
||||||
ProfileViewModel(
|
.navigationService
|
||||||
profileService: collectionService.navigationService.profileService(account: account))))
|
.profileService(account: account))))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -142,9 +142,9 @@ extension CollectionItemsViewModel: CollectionViewModel {
|
||||||
|
|
||||||
private extension CollectionItemsViewModel {
|
private extension CollectionItemsViewModel {
|
||||||
func cache(viewModel: CollectionItemViewModel, forItem item: CollectionItem) {
|
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)
|
.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]]) {
|
func process(items: [[CollectionItem]]) {
|
||||||
|
|
|
@ -8,7 +8,7 @@ public protocol CollectionViewModel {
|
||||||
var title: AnyPublisher<String, Never> { get }
|
var title: AnyPublisher<String, Never> { get }
|
||||||
var alertItems: AnyPublisher<AlertItem, Never> { get }
|
var alertItems: AnyPublisher<AlertItem, Never> { get }
|
||||||
var loading: AnyPublisher<Bool, 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 nextPageMaxId: String? { get }
|
||||||
var maintainScrollPositionOfItem: CollectionItemIdentifier? { get }
|
var maintainScrollPositionOfItem: CollectionItemIdentifier? { get }
|
||||||
func request(maxId: String?, minId: String?)
|
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 {
|
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 let collectionViewModel: CurrentValueSubject<CollectionItemsViewModel, Never>
|
||||||
private var cancellables = Set<AnyCancellable>()
|
private var cancellables = Set<AnyCancellable>()
|
||||||
|
|
||||||
init(profileService: ProfileService) {
|
public init(profileService: ProfileService, identification: Identification) {
|
||||||
self.profileService = profileService
|
self.profileService = profileService
|
||||||
|
|
||||||
collectionViewModel = CurrentValueSubject(
|
collectionViewModel = CurrentValueSubject(
|
||||||
CollectionItemsViewModel(collectionService: profileService.timelineService(profileCollection: .statuses)))
|
CollectionItemsViewModel(
|
||||||
|
collectionService: profileService.timelineService(profileCollection: .statuses),
|
||||||
|
identification: identification))
|
||||||
|
|
||||||
profileService.accountServicePublisher
|
profileService.accountServicePublisher
|
||||||
.map(AccountViewModel.init(accountService:))
|
.map(AccountViewModel.init(accountService:))
|
||||||
|
@ -27,7 +29,7 @@ final public class ProfileViewModel {
|
||||||
|
|
||||||
$collection.dropFirst()
|
$collection.dropFirst()
|
||||||
.map(profileService.timelineService(profileCollection:))
|
.map(profileService.timelineService(profileCollection:))
|
||||||
.map(CollectionItemsViewModel.init(collectionService:))
|
.map { CollectionItemsViewModel(collectionService: $0, identification: identification) }
|
||||||
.sink { [weak self] in
|
.sink { [weak self] in
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
|
|
||||||
|
@ -55,14 +57,12 @@ extension ProfileViewModel: CollectionViewModel {
|
||||||
collectionViewModel.flatMap(\.loading).eraseToAnyPublisher()
|
collectionViewModel.flatMap(\.loading).eraseToAnyPublisher()
|
||||||
}
|
}
|
||||||
|
|
||||||
public var navigationEvents: AnyPublisher<NavigationEvent, Never> {
|
public var events: AnyPublisher<CollectionItemEvent, Never> {
|
||||||
$accountViewModel.compactMap { $0 }
|
$accountViewModel.compactMap { $0 }
|
||||||
.flatMap(\.events)
|
.flatMap(\.events)
|
||||||
.flatMap { $0 }
|
.flatMap { $0 }
|
||||||
.map(NavigationEvent.init)
|
|
||||||
.compactMap { $0 }
|
|
||||||
.assignErrorsToAlertItem(to: \.alertItem, on: self)
|
.assignErrorsToAlertItem(to: \.alertItem, on: self)
|
||||||
.merge(with: collectionViewModel.flatMap(\.navigationEvents))
|
.merge(with: collectionViewModel.flatMap(\.events))
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,10 +4,11 @@ import SwiftUI
|
||||||
import ViewModels
|
import ViewModels
|
||||||
|
|
||||||
struct TableView: UIViewControllerRepresentable {
|
struct TableView: UIViewControllerRepresentable {
|
||||||
|
@EnvironmentObject var identification: Identification
|
||||||
let viewModel: CollectionViewModel
|
let viewModel: CollectionViewModel
|
||||||
|
|
||||||
func makeUIViewController(context: Context) -> TableViewController {
|
func makeUIViewController(context: Context) -> TableViewController {
|
||||||
TableViewController(viewModel: viewModel)
|
TableViewController(viewModel: viewModel, identification: identification)
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateUIViewController(_ uiViewController: TableViewController, context: Context) {
|
func updateUIViewController(_ uiViewController: TableViewController, context: Context) {
|
||||||
|
|
Loading…
Reference in a new issue