// // StatusProvider+StatusTableViewCellDelegate.swift // Mastodon // // Created by sxiaojian on 2021/2/8. // import os.log import UIKit import Combine import CoreData import CoreDataStack import MastodonSDK import ActiveLabel import Meta import MetaTextView // MARK: - StatusViewDelegate extension StatusTableViewCellDelegate where Self: StatusProvider { func statusTableViewCell(_ cell: StatusTableViewCell, statusView: StatusView, headerInfoLabelDidPressed label: UILabel) { StatusProviderFacade.coordinateToStatusAuthorProfileScene(for: .secondary, provider: self, cell: cell) } func statusTableViewCell(_ cell: StatusTableViewCell, statusView: StatusView, avatarImageViewDidPressed imageView: UIImageView) { StatusProviderFacade.coordinateToStatusAuthorProfileScene(for: .primary, provider: self, cell: cell) } func statusTableViewCell(_ cell: StatusTableViewCell, statusView: StatusView, activeLabel: ActiveLabel, didSelectActiveEntity entity: ActiveEntity) { StatusProviderFacade.responseToStatusActiveLabelAction(provider: self, cell: cell, activeLabel: activeLabel, didTapEntity: entity) } func statusTableViewCell(_ cell: StatusTableViewCell, statusView: StatusView, metaText: MetaText, didSelectMeta meta: Meta) { StatusProviderFacade.responseToStatusMetaTextAction(provider: self, cell: cell, metaText: metaText, didSelectMeta: meta) } func statusTableViewCell(_ cell: StatusTableViewCell, statusView: StatusView, revealContentWarningButtonDidPressed button: UIButton) { StatusProviderFacade.responseToStatusContentWarningRevealAction(provider: self, cell: cell) } func statusTableViewCell(_ cell: StatusTableViewCell, statusView: StatusView, contentWarningOverlayViewDidPressed contentWarningOverlayView: ContentWarningOverlayView) { StatusProviderFacade.responseToStatusContentWarningRevealAction(provider: self, cell: cell) } } // MARK: - ActionToolbarContainerDelegate extension StatusTableViewCellDelegate where Self: StatusProvider { func statusTableViewCell(_ cell: StatusTableViewCell, actionToolbarContainer: ActionToolbarContainer, replyButtonDidPressed sender: UIButton) { StatusProviderFacade.responseToStatusReplyAction(provider: self, cell: cell) } func statusTableViewCell(_ cell: StatusTableViewCell, actionToolbarContainer: ActionToolbarContainer, reblogButtonDidPressed sender: UIButton) { StatusProviderFacade.responseToStatusReblogAction(provider: self, cell: cell) } func statusTableViewCell(_ cell: StatusTableViewCell, actionToolbarContainer: ActionToolbarContainer, likeButtonDidPressed sender: UIButton) { StatusProviderFacade.responseToStatusLikeAction(provider: self, cell: cell) } } // MARK: - MosciaImageViewContainerDelegate extension StatusTableViewCellDelegate where Self: StatusProvider { func statusTableViewCell(_ cell: StatusTableViewCell, mosaicImageViewContainer: MosaicImageViewContainer, contentWarningOverlayViewDidPressed contentWarningOverlayView: ContentWarningOverlayView) { StatusProviderFacade.responseToStatusContentWarningRevealAction(provider: self, cell: cell) } func statusTableViewCell(_ cell: StatusTableViewCell, playerContainerView: PlayerContainerView, contentWarningOverlayViewDidPressed contentWarningOverlayView: ContentWarningOverlayView) { StatusProviderFacade.responseToStatusContentWarningRevealAction(provider: self, cell: cell) } func statusTableViewCell(_ cell: StatusTableViewCell, contentWarningOverlayViewDidPressed contentWarningOverlayView: ContentWarningOverlayView) { StatusProviderFacade.responseToStatusContentWarningRevealAction(provider: self, cell: cell) } } extension StatusTableViewCellDelegate where Self: StatusProvider & MediaPreviewableViewController { func statusTableViewCell(_ cell: StatusTableViewCell, mosaicImageViewContainer: MosaicImageViewContainer, didTapImageView imageView: UIImageView, atIndex index: Int) { if UIAccessibility.isVoiceOverRunning, !(self is ThreadViewController) { StatusProviderFacade.coordinateToStatusThreadScene(for: .primary, provider: self, cell: cell) } else { StatusProviderFacade.coordinateToStatusMediaPreviewScene(provider: self, cell: cell, mosaicImageView: mosaicImageViewContainer, didTapImageView: imageView, atIndex: index) } } } // MARK: - PollTableView extension StatusTableViewCellDelegate where Self: StatusProvider { func statusTableViewCell(_ cell: StatusTableViewCell, statusView: StatusView, pollVoteButtonPressed button: UIButton) { guard let activeMastodonAuthenticationBox = context.authenticationService.activeMastodonAuthenticationBox.value else { return } status(for: cell, indexPath: nil) .receive(on: DispatchQueue.main) .setFailureType(to: Error.self) .compactMap { status -> AnyPublisher, Error>? in guard let status = (status?.reblog ?? status) else { return nil } guard let poll = status.poll else { return nil } let votedOptions = poll.options.filter { ($0.votedBy ?? Set()).contains(where: { $0.id == activeMastodonAuthenticationBox.userID }) } let choices = votedOptions.map { $0.index.intValue } let domain = poll.status.domain button.isEnabled = false return self.context.apiService.vote( domain: domain, pollID: poll.id, pollObjectID: poll.objectID, choices: choices, mastodonAuthenticationBox: activeMastodonAuthenticationBox ) } .switchToLatest() .sink(receiveCompletion: { completion in switch completion { case .failure(let error): // TODO: handle error os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: multiple vote fail: %s", ((#file as NSString).lastPathComponent), #line, #function, error.localizedDescription) button.isEnabled = true case .finished: break } }, receiveValue: { response in // do nothing }) .store(in: &context.disposeBag) } func statusTableViewCell(_ cell: StatusTableViewCell, pollTableView: PollTableView, didSelectRowAt indexPath: IndexPath) { guard let activeMastodonAuthenticationBox = context.authenticationService.activeMastodonAuthenticationBox.value else { return } guard let activeMastodonAuthentication = context.authenticationService.activeMastodonAuthentication.value else { return } guard let diffableDataSource = cell.statusView.pollTableViewDataSource else { return } let item = diffableDataSource.itemIdentifier(for: indexPath) guard case let .option(objectID, _) = item else { return } guard let option = managedObjectContext.object(with: objectID) as? PollOption else { return } let poll = option.poll let pollObjectID = option.poll.objectID let domain = poll.status.domain if poll.multiple { var votedOptions = poll.options.filter { ($0.votedBy ?? Set()).contains(where: { $0.id == activeMastodonAuthenticationBox.userID }) } if votedOptions.contains(option) { votedOptions.remove(option) } else { votedOptions.insert(option) } let choices = votedOptions.map { $0.index.intValue } context.apiService.vote( pollObjectID: option.poll.objectID, mastodonUserObjectID: activeMastodonAuthentication.user.objectID, choices: choices ) .handleEvents(receiveOutput: { _ in // TODO: add haptic }) .receive(on: DispatchQueue.main) .sink { completion in // Do nothing } receiveValue: { _ in // Do nothing } .store(in: &context.disposeBag) } else { let choices = [option.index.intValue] context.apiService.vote( pollObjectID: pollObjectID, mastodonUserObjectID: activeMastodonAuthentication.user.objectID, choices: [option.index.intValue] ) .handleEvents(receiveOutput: { _ in // TODO: add haptic }) .flatMap { pollID -> AnyPublisher, Error> in return self.context.apiService.vote( domain: domain, pollID: pollID, pollObjectID: pollObjectID, choices: choices, mastodonAuthenticationBox: activeMastodonAuthenticationBox ) } .receive(on: DispatchQueue.main) .sink { completion in } receiveValue: { response in print(response.value) } .store(in: &context.disposeBag) } } }