mirror of
https://github.com/metabolist/metatext.git
synced 2024-11-25 01:31:02 +00:00
Status actions pipeline
This commit is contained in:
parent
56297a643c
commit
a595133d70
4 changed files with 47 additions and 9 deletions
|
@ -78,6 +78,15 @@ final class StatusListViewController: UITableViewController {
|
||||||
}
|
}
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
|
|
||||||
|
viewModel.statusEvents.sink { [weak self] in
|
||||||
|
switch $0 {
|
||||||
|
case .ignorableOutput, .statusListNavigation, .urlNavigation: break
|
||||||
|
case let .share(url):
|
||||||
|
self?.share(url: url)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.store(in: &cancellables)
|
||||||
|
|
||||||
viewModel.$loading
|
viewModel.$loading
|
||||||
.receive(on: RunLoop.main)
|
.receive(on: RunLoop.main)
|
||||||
.sink { [weak self] in
|
.sink { [weak self] in
|
||||||
|
@ -146,6 +155,12 @@ extension StatusListViewController: UITableViewDataSourcePrefetching {
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension StatusListViewController {
|
private extension StatusListViewController {
|
||||||
|
func share(url: URL) {
|
||||||
|
let activityViewController = UIActivityViewController(activityItems: [url], applicationActivities: nil)
|
||||||
|
|
||||||
|
present(activityViewController, animated: true, completion: nil)
|
||||||
|
}
|
||||||
|
|
||||||
func sizeTableHeaderFooterViews() {
|
func sizeTableHeaderFooterViews() {
|
||||||
// https://useyourloaf.com/blog/variable-height-table-view-header/
|
// https://useyourloaf.com/blog/variable-height-table-view-header/
|
||||||
if let headerView = tableView.tableHeaderView {
|
if let headerView = tableView.tableHeaderView {
|
||||||
|
|
|
@ -9,15 +9,18 @@ public final class StatusListViewModel: ObservableObject {
|
||||||
@Published public private(set) var statusIDs = [[String]]()
|
@Published public private(set) var statusIDs = [[String]]()
|
||||||
@Published public var alertItem: AlertItem?
|
@Published public var alertItem: AlertItem?
|
||||||
@Published public private(set) var loading = false
|
@Published public private(set) var loading = false
|
||||||
|
public let statusEvents: AnyPublisher<StatusViewModel.Event, Never>
|
||||||
public private(set) var maintainScrollPositionOfStatusID: String?
|
public private(set) var maintainScrollPositionOfStatusID: String?
|
||||||
|
|
||||||
private var statuses = [String: Status]()
|
private var statuses = [String: Status]()
|
||||||
private let statusListService: StatusListService
|
private let statusListService: StatusListService
|
||||||
private var statusViewModelCache = [Status: (StatusViewModel, AnyCancellable)]()
|
private var statusViewModelCache = [Status: (StatusViewModel, AnyCancellable)]()
|
||||||
|
private let statusEventsSubject = PassthroughSubject<StatusViewModel.Event, Never>()
|
||||||
private var cancellables = Set<AnyCancellable>()
|
private var cancellables = Set<AnyCancellable>()
|
||||||
|
|
||||||
init(statusListService: StatusListService) {
|
init(statusListService: StatusListService) {
|
||||||
self.statusListService = statusListService
|
self.statusListService = statusListService
|
||||||
|
statusEvents = statusEventsSubject.eraseToAnyPublisher()
|
||||||
|
|
||||||
statusListService.statusSections
|
statusListService.statusSections
|
||||||
.combineLatest(statusListService.filters.map { $0.regularExpression() })
|
.combineLatest(statusListService.filters.map { $0.regularExpression() })
|
||||||
|
@ -54,15 +57,16 @@ public extension StatusListViewModel {
|
||||||
guard let status = statuses[id] else { return nil }
|
guard let status = statuses[id] else { return nil }
|
||||||
|
|
||||||
var statusViewModel: StatusViewModel
|
var statusViewModel: StatusViewModel
|
||||||
|
|
||||||
if let cachedViewModel = statusViewModelCache[status]?.0 {
|
if let cachedViewModel = statusViewModelCache[status]?.0 {
|
||||||
statusViewModel = cachedViewModel
|
statusViewModel = cachedViewModel
|
||||||
} else {
|
} else {
|
||||||
statusViewModel = StatusViewModel(statusService: statusListService.statusService(status: status))
|
statusViewModel = StatusViewModel(statusService: statusListService.statusService(status: status))
|
||||||
statusViewModelCache[status] = (statusViewModel, statusViewModel.events
|
statusViewModelCache[status] = (statusViewModel,
|
||||||
.flatMap { $0 }
|
statusViewModel.events
|
||||||
.assignErrorsToAlertItem(to: \.alertItem, on: self)
|
.flatMap { $0 }
|
||||||
.sink { _ in })
|
.assignErrorsToAlertItem(to: \.alertItem, on: self)
|
||||||
|
.sink { [weak self] in self?.statusEventsSubject.send($0) })
|
||||||
}
|
}
|
||||||
|
|
||||||
statusViewModel.isContextParent = status.id == statusListService.contextParentID
|
statusViewModel.isContextParent = status.id == statusListService.contextParentID
|
||||||
|
|
|
@ -22,10 +22,10 @@ public struct StatusViewModel {
|
||||||
public var isReplyInContext = false
|
public var isReplyInContext = false
|
||||||
public var hasReplyFollowing = false
|
public var hasReplyFollowing = false
|
||||||
public var sensitiveContentToggled = false
|
public var sensitiveContentToggled = false
|
||||||
public let events: AnyPublisher<AnyPublisher<Never, Error>, Never>
|
public let events: AnyPublisher<AnyPublisher<Event, Error>, Never>
|
||||||
|
|
||||||
private let statusService: StatusService
|
private let statusService: StatusService
|
||||||
private let eventsInput = PassthroughSubject<AnyPublisher<Never, Error>, Never>()
|
private let eventsSubject = PassthroughSubject<AnyPublisher<Event, Error>, Never>()
|
||||||
|
|
||||||
init(statusService: StatusService) {
|
init(statusService: StatusService) {
|
||||||
self.statusService = statusService
|
self.statusService = statusService
|
||||||
|
@ -45,7 +45,16 @@ public struct StatusViewModel {
|
||||||
.map(AttachmentViewModel.init(attachment:))
|
.map(AttachmentViewModel.init(attachment:))
|
||||||
pollOptionTitles = statusService.status.displayStatus.poll?.options.map { $0.title } ?? []
|
pollOptionTitles = statusService.status.displayStatus.poll?.options.map { $0.title } ?? []
|
||||||
pollEmoji = statusService.status.displayStatus.poll?.emojis ?? []
|
pollEmoji = statusService.status.displayStatus.poll?.emojis ?? []
|
||||||
events = eventsInput.eraseToAnyPublisher()
|
events = eventsSubject.eraseToAnyPublisher()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public extension StatusViewModel {
|
||||||
|
enum Event {
|
||||||
|
case ignorableOutput
|
||||||
|
case statusListNavigation(StatusListViewModel)
|
||||||
|
case urlNavigation(URL)
|
||||||
|
case share(URL)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,7 +117,13 @@ public extension StatusViewModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
func toggleFavorited() {
|
func toggleFavorited() {
|
||||||
eventsInput.send(statusService.toggleFavorited())
|
eventsSubject.send(statusService.toggleFavorited().map { _ in Event.ignorableOutput }.eraseToAnyPublisher())
|
||||||
|
}
|
||||||
|
|
||||||
|
func shareStatus() {
|
||||||
|
guard let url = statusService.status.displayStatus.url else { return }
|
||||||
|
|
||||||
|
eventsSubject.send(Just(Event.share(url)).setFailureType(to: Error.self).eraseToAnyPublisher())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -148,6 +148,10 @@ private extension StatusView {
|
||||||
favoriteButton.addAction(favoriteAction, for: .touchUpInside)
|
favoriteButton.addAction(favoriteAction, for: .touchUpInside)
|
||||||
contextParentFavoriteButton.addAction(favoriteAction, for: .touchUpInside)
|
contextParentFavoriteButton.addAction(favoriteAction, for: .touchUpInside)
|
||||||
|
|
||||||
|
let shareAction = UIAction { [weak self] _ in self?.statusConfiguration.viewModel.shareStatus() }
|
||||||
|
|
||||||
|
shareButton.addAction(shareAction, for: .touchUpInside)
|
||||||
|
|
||||||
applyStatusConfiguration()
|
applyStatusConfiguration()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue