From 1020ca531a6766a73d19bb88c1a57074469a6e4a Mon Sep 17 00:00:00 2001 From: Marcus Kida Date: Wed, 7 Dec 2022 15:41:33 +0100 Subject: [PATCH] feat: Implement status translation info footer and reversion --- .../TableviewCell/StatusTableViewCell.swift | 2 +- .../StatusThreadRootTableViewCell.swift | 2 +- .../Content/StatusView+Configuration.swift | 29 +++++---- .../View/Content/StatusView+ViewModel.swift | 10 +-- .../MastodonUI/View/Content/StatusView.swift | 62 ++++++++++++++++--- .../MastodonUI/View/Menu/MastodonMenu.swift | 2 +- 6 files changed, 76 insertions(+), 31 deletions(-) diff --git a/Mastodon/Scene/Share/View/TableviewCell/StatusTableViewCell.swift b/Mastodon/Scene/Share/View/TableviewCell/StatusTableViewCell.swift index 3bb57302a..b8482c564 100644 --- a/Mastodon/Scene/Share/View/TableviewCell/StatusTableViewCell.swift +++ b/Mastodon/Scene/Share/View/TableviewCell/StatusTableViewCell.swift @@ -88,7 +88,7 @@ extension StatusTableViewCell { .store(in: &_disposeBag) statusView.viewModel - .$isTranslated + .$translatedFromLanguage .receive(on: DispatchQueue.main) .sink(receiveValue: { [weak self] _ in self?.invalidateIntrinsicContentSize() diff --git a/Mastodon/Scene/Share/View/TableviewCell/StatusThreadRootTableViewCell.swift b/Mastodon/Scene/Share/View/TableviewCell/StatusThreadRootTableViewCell.swift index 5cc6f6596..73b700e27 100644 --- a/Mastodon/Scene/Share/View/TableviewCell/StatusThreadRootTableViewCell.swift +++ b/Mastodon/Scene/Share/View/TableviewCell/StatusThreadRootTableViewCell.swift @@ -83,7 +83,7 @@ extension StatusThreadRootTableViewCell { statusView.contentMetaText.textView.isSelectable = true statusView.viewModel - .$isTranslated + .$translatedFromLanguage .receive(on: DispatchQueue.main) .sink(receiveValue: { [weak self] _ in self?.invalidateIntrinsicContentSize() diff --git a/MastodonSDK/Sources/MastodonUI/View/Content/StatusView+Configuration.swift b/MastodonSDK/Sources/MastodonUI/View/Content/StatusView+Configuration.swift index 498b77a5f..7b40b9609 100644 --- a/MastodonSDK/Sources/MastodonUI/View/Content/StatusView+Configuration.swift +++ b/MastodonSDK/Sources/MastodonUI/View/Content/StatusView+Configuration.swift @@ -55,18 +55,13 @@ extension StatusView { configurePoll(status: status) configureToolbar(status: status) configureFilter(status: status) - - status.$translatedContent + viewModel.originalStatus = status + [ + status.$translatedContent, + status.reblog?.$translatedContent + ].compactMap { $0 } + .last? .receive(on: DispatchQueue.main) - .compactMap { $0 } - .sink { [weak self] _ in - self?.configureTranslated(status: status) - } - .store(in: &disposeBag) - - status.reblog?.$translatedContent - .receive(on: DispatchQueue.main) - .compactMap { $0 } .sink { [weak self] _ in self?.configureTranslated(status: status) } @@ -247,6 +242,14 @@ extension StatusView { .store(in: &disposeBag) } + func revertTranslation() { + guard let originalStatus = viewModel.originalStatus else { return } + viewModel.translatedFromLanguage = nil + originalStatus.reblog?.translatedContent = nil + originalStatus.translatedContent = nil + configure(status: originalStatus) + } + func configureTranslated(status: Status) { let translatedContent: String? = { if let translatedContent = status.reblog?.translatedContent { @@ -267,7 +270,7 @@ extension StatusView { let content = MastodonContent(content: translatedContent, emojis: status.emojis.asDictionary) let metaContent = try MastodonMetaContent.convert(document: content) viewModel.content = metaContent - viewModel.isTranslated = true + viewModel.translatedFromLanguage = status.reblog?.language ?? status.language } catch { assertionFailure(error.localizedDescription) viewModel.content = PlaintextMetaContent(string: "") @@ -301,7 +304,7 @@ extension StatusView { let content = MastodonContent(content: status.content, emojis: status.emojis.asDictionary) let metaContent = try MastodonMetaContent.convert(document: content) viewModel.content = metaContent - viewModel.isTranslated = false + viewModel.translatedFromLanguage = nil } catch { assertionFailure(error.localizedDescription) viewModel.content = PlaintextMetaContent(string: "") diff --git a/MastodonSDK/Sources/MastodonUI/View/Content/StatusView+ViewModel.swift b/MastodonSDK/Sources/MastodonUI/View/Content/StatusView+ViewModel.swift index b56bba2e1..15919120c 100644 --- a/MastodonSDK/Sources/MastodonUI/View/Content/StatusView+ViewModel.swift +++ b/MastodonSDK/Sources/MastodonUI/View/Content/StatusView+ViewModel.swift @@ -44,7 +44,7 @@ extension StatusView { @Published public var isMyself = false @Published public var isMuting = false @Published public var isBlocking = false - @Published public var isTranslated = false + @Published public var translatedFromLanguage: String? @Published public var timestamp: Date? public var timestampFormatter: ((_ date: Date) -> String)? @@ -137,7 +137,7 @@ extension StatusView { isContentSensitive = false isMediaSensitive = false isSensitiveToggled = false - isTranslated = false + translatedFromLanguage = nil activeFilters = [] filterContext = nil @@ -586,7 +586,7 @@ extension StatusView.ViewModel { $isBookmark ) let publishersThree = Publishers.CombineLatest( - $isTranslated, + $translatedFromLanguage, $language ) @@ -598,7 +598,7 @@ extension StatusView.ViewModel { .sink { tupleOne, tupleTwo, tupleThree in let (authorName, isMyself) = tupleOne let (isMuting, isBlocking, isBookmark) = tupleTwo - let (isTranslated, language) = tupleThree + let (translatedFromLanguage, language) = tupleThree guard let name = authorName?.string else { statusView.authorView.menuButton.menu = nil @@ -611,7 +611,7 @@ extension StatusView.ViewModel { isBlocking: isBlocking, isMyself: isMyself, isBookmarking: isBookmark, - isTranslated: isTranslated, + isTranslated: translatedFromLanguage != nil, statusLanguage: language ) let (menu, actions) = authorView.setupAuthorMenu(menuContext: menuContext) diff --git a/MastodonSDK/Sources/MastodonUI/View/Content/StatusView.swift b/MastodonSDK/Sources/MastodonUI/View/Content/StatusView.swift index bd901b08a..2838bbd85 100644 --- a/MastodonSDK/Sources/MastodonUI/View/Content/StatusView.swift +++ b/MastodonSDK/Sources/MastodonUI/View/Content/StatusView.swift @@ -176,6 +176,37 @@ public final class StatusView: UIView { indicatorView.stopAnimating() return indicatorView }() + private let translatedInfoLabel = UILabel() + lazy var translatedInfoView: UIView = { + let containerView = UIView() + + let revertButton = UIButton() + revertButton.setTitle("Show Original", for: .normal) + revertButton.setTitleColor(Asset.Colors.brand.color, for: .normal) + revertButton.addAction(UIAction { [weak self] _ in + self?.revertTranslation() + }, for: .touchUpInside) + + [containerView, translatedInfoLabel, revertButton].forEach { + $0.translatesAutoresizingMaskIntoConstraints = false + } + + [translatedInfoLabel, revertButton].forEach { + containerView.addSubview($0) + } + + NSLayoutConstraint.activate([ + containerView.heightAnchor.constraint(equalToConstant: 20), + translatedInfoLabel.centerYAnchor.constraint(equalTo: containerView.centerYAnchor), + translatedInfoLabel.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: 16), + revertButton.centerYAnchor.constraint(equalTo: containerView.centerYAnchor), + revertButton.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: -16) + ]) + + containerView.isHidden = true + + return containerView + }() // toolbar let actionToolbarAdaptiveMarginContainerView = AdaptiveMarginContainerView() @@ -217,6 +248,7 @@ public final class StatusView: UIView { setMediaDisplay(isDisplay: false) setPollDisplay(isDisplay: false) setFilterHintLabelDisplay(isDisplay: false) + setupTranslationIndicator() } public override init(frame: CGRect) { @@ -275,16 +307,6 @@ extension StatusView { // statusMetricView statusMetricView.delegate = self - - // status translation - viewModel.$isTranslated.sink { [weak self] isTranslated in - guard - let self = self, - let status = self.viewModel.originalStatus - else { return } - self.configureTranslated(status: status) - } - .store(in: &disposeBag) } } @@ -448,6 +470,9 @@ extension StatusView.Style { statusView.filterHintLabel.centerXAnchor.constraint(equalTo: statusView.containerStackView.centerXAnchor), statusView.filterHintLabel.centerYAnchor.constraint(equalTo: statusView.containerStackView.centerYAnchor), ]) + + // translated info + statusView.containerStackView.addArrangedSubview(statusView.translatedInfoView) } func inline(statusView: StatusView) { @@ -660,6 +685,23 @@ extension StatusView: MastodonMenuDelegate { } } +extension StatusView { + func setupTranslationIndicator() { + viewModel.$translatedFromLanguage + .receive(on: DispatchQueue.main) + .sink { [weak self] translatedFromLanguage in + guard let self = self else { return } + if let translatedFromLanguage = translatedFromLanguage { + self.translatedInfoLabel.text = String(format: "Translated from %@", Locale.current.localizedString(forIdentifier: translatedFromLanguage) ?? "Unknown") + self.translatedInfoView.isHidden = false + } else { + self.translatedInfoView.isHidden = true + } + } + .store(in: &disposeBag) + } +} + #if DEBUG import SwiftUI diff --git a/MastodonSDK/Sources/MastodonUI/View/Menu/MastodonMenu.swift b/MastodonSDK/Sources/MastodonUI/View/Menu/MastodonMenu.swift index e2e887db3..a09067adf 100644 --- a/MastodonSDK/Sources/MastodonUI/View/Menu/MastodonMenu.swift +++ b/MastodonSDK/Sources/MastodonUI/View/Menu/MastodonMenu.swift @@ -129,7 +129,7 @@ extension MastodonMenu { return deleteAction case let .translateStatus(context): let translateAction = BuiltAction( - title: String(format: "Translate from %@", context.language), + title: String(format: "Translate from %@", Locale.current.localizedString(forIdentifier: context.language) ?? "Unknown"), image: UIImage(systemName: "character.book.closed") ) { [weak delegate] in guard let delegate = delegate else { return }