feat: Implement `translatedContent` which can be used to replace the StatusView content
This commit is contained in:
parent
5c1dea6942
commit
9affb0f637
|
@ -31,6 +31,7 @@
|
||||||
2A506CF6292D040100059C37 /* HashtagTimelineHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A506CF5292D040100059C37 /* HashtagTimelineHeaderView.swift */; };
|
2A506CF6292D040100059C37 /* HashtagTimelineHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A506CF5292D040100059C37 /* HashtagTimelineHeaderView.swift */; };
|
||||||
2A76F75C2930D94700B3388D /* HashtagTimelineHeaderViewActionButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A76F75B2930D94700B3388D /* HashtagTimelineHeaderViewActionButton.swift */; };
|
2A76F75C2930D94700B3388D /* HashtagTimelineHeaderViewActionButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A76F75B2930D94700B3388D /* HashtagTimelineHeaderViewActionButton.swift */; };
|
||||||
2A82294F29262EE000D2A1F7 /* AppContext+NextAccount.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A82294E29262EE000D2A1F7 /* AppContext+NextAccount.swift */; };
|
2A82294F29262EE000D2A1F7 /* AppContext+NextAccount.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A82294E29262EE000D2A1F7 /* AppContext+NextAccount.swift */; };
|
||||||
|
2AB12E4629362F27006BC925 /* DataSourceFacade+Translate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AB12E4529362F27006BC925 /* DataSourceFacade+Translate.swift */; };
|
||||||
2AE244482927831100BDBF7C /* UIImage+SFSymbols.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AE244472927831100BDBF7C /* UIImage+SFSymbols.swift */; };
|
2AE244482927831100BDBF7C /* UIImage+SFSymbols.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AE244472927831100BDBF7C /* UIImage+SFSymbols.swift */; };
|
||||||
2D198643261BF09500F0B013 /* SearchResultItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D198642261BF09500F0B013 /* SearchResultItem.swift */; };
|
2D198643261BF09500F0B013 /* SearchResultItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D198642261BF09500F0B013 /* SearchResultItem.swift */; };
|
||||||
2D198649261C0B8500F0B013 /* SearchResultSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D198648261C0B8500F0B013 /* SearchResultSection.swift */; };
|
2D198649261C0B8500F0B013 /* SearchResultSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D198648261C0B8500F0B013 /* SearchResultSection.swift */; };
|
||||||
|
@ -535,6 +536,7 @@
|
||||||
2A506CF5292D040100059C37 /* HashtagTimelineHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HashtagTimelineHeaderView.swift; sourceTree = "<group>"; };
|
2A506CF5292D040100059C37 /* HashtagTimelineHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HashtagTimelineHeaderView.swift; sourceTree = "<group>"; };
|
||||||
2A76F75B2930D94700B3388D /* HashtagTimelineHeaderViewActionButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HashtagTimelineHeaderViewActionButton.swift; sourceTree = "<group>"; };
|
2A76F75B2930D94700B3388D /* HashtagTimelineHeaderViewActionButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HashtagTimelineHeaderViewActionButton.swift; sourceTree = "<group>"; };
|
||||||
2A82294E29262EE000D2A1F7 /* AppContext+NextAccount.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppContext+NextAccount.swift"; sourceTree = "<group>"; };
|
2A82294E29262EE000D2A1F7 /* AppContext+NextAccount.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppContext+NextAccount.swift"; sourceTree = "<group>"; };
|
||||||
|
2AB12E4529362F27006BC925 /* DataSourceFacade+Translate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DataSourceFacade+Translate.swift"; sourceTree = "<group>"; };
|
||||||
2AE244472927831100BDBF7C /* UIImage+SFSymbols.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImage+SFSymbols.swift"; sourceTree = "<group>"; };
|
2AE244472927831100BDBF7C /* UIImage+SFSymbols.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImage+SFSymbols.swift"; sourceTree = "<group>"; };
|
||||||
2D198642261BF09500F0B013 /* SearchResultItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchResultItem.swift; sourceTree = "<group>"; };
|
2D198642261BF09500F0B013 /* SearchResultItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchResultItem.swift; sourceTree = "<group>"; };
|
||||||
2D198648261C0B8500F0B013 /* SearchResultSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchResultSection.swift; sourceTree = "<group>"; };
|
2D198648261C0B8500F0B013 /* SearchResultSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchResultSection.swift; sourceTree = "<group>"; };
|
||||||
|
@ -2120,6 +2122,7 @@
|
||||||
DB63F74627990B0600455B82 /* DataSourceFacade+Hashtag.swift */,
|
DB63F74627990B0600455B82 /* DataSourceFacade+Hashtag.swift */,
|
||||||
DB63F7532799491600455B82 /* DataSourceFacade+SearchHistory.swift */,
|
DB63F7532799491600455B82 /* DataSourceFacade+SearchHistory.swift */,
|
||||||
DB159C2A27A17BAC0068DC77 /* DataSourceFacade+Media.swift */,
|
DB159C2A27A17BAC0068DC77 /* DataSourceFacade+Media.swift */,
|
||||||
|
2AB12E4529362F27006BC925 /* DataSourceFacade+Translate.swift */,
|
||||||
DB697DD5278F4C29004EF2F7 /* DataSourceProvider.swift */,
|
DB697DD5278F4C29004EF2F7 /* DataSourceProvider.swift */,
|
||||||
DB697DDA278F4DE3004EF2F7 /* DataSourceProvider+StatusTableViewCellDelegate.swift */,
|
DB697DDA278F4DE3004EF2F7 /* DataSourceProvider+StatusTableViewCellDelegate.swift */,
|
||||||
DB023D2927A0FE5C005AC798 /* DataSourceProvider+NotificationTableViewCellDelegate.swift */,
|
DB023D2927A0FE5C005AC798 /* DataSourceProvider+NotificationTableViewCellDelegate.swift */,
|
||||||
|
@ -3286,6 +3289,7 @@
|
||||||
DBF1572F27046F1A00EC00B7 /* SecondaryPlaceholderViewController.swift in Sources */,
|
DBF1572F27046F1A00EC00B7 /* SecondaryPlaceholderViewController.swift in Sources */,
|
||||||
2D4AD8A826316D3500613EFC /* SelectedAccountItem.swift in Sources */,
|
2D4AD8A826316D3500613EFC /* SelectedAccountItem.swift in Sources */,
|
||||||
DBE3CDFB261C6CA500430CC6 /* FavoriteViewModel.swift in Sources */,
|
DBE3CDFB261C6CA500430CC6 /* FavoriteViewModel.swift in Sources */,
|
||||||
|
2AB12E4629362F27006BC925 /* DataSourceFacade+Translate.swift in Sources */,
|
||||||
DBE3CE01261D623D00430CC6 /* FavoriteViewModel+State.swift in Sources */,
|
DBE3CE01261D623D00430CC6 /* FavoriteViewModel+State.swift in Sources */,
|
||||||
2D82BA0525E7897700E36F0F /* MastodonResendEmailViewModelNavigationDelegateShim.swift in Sources */,
|
2D82BA0525E7897700E36F0F /* MastodonResendEmailViewModelNavigationDelegateShim.swift in Sources */,
|
||||||
2D38F1EB25CD477000561493 /* HomeTimelineViewModel+LoadLatestState.swift in Sources */,
|
2D38F1EB25CD477000561493 /* HomeTimelineViewModel+LoadLatestState.swift in Sources */,
|
||||||
|
|
|
@ -393,6 +393,12 @@ extension DataSourceFacade {
|
||||||
alertController.addAction(cancelAction)
|
alertController.addAction(cancelAction)
|
||||||
dependency.present(alertController, animated: true)
|
dependency.present(alertController, animated: true)
|
||||||
|
|
||||||
|
case let .translateStatus(translationContext):
|
||||||
|
guard let status = menuContext.status else { return }
|
||||||
|
try await DataSourceFacade.translateStatus(
|
||||||
|
provider: dependency,
|
||||||
|
status: status
|
||||||
|
)
|
||||||
}
|
}
|
||||||
} // end func
|
} // end func
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
//
|
||||||
|
// DataSourceFacade+Translate.swift
|
||||||
|
// Mastodon
|
||||||
|
//
|
||||||
|
// Created by Marcus Kida on 29.11.22.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
import CoreData
|
||||||
|
import CoreDataStack
|
||||||
|
import MastodonCore
|
||||||
|
|
||||||
|
extension DataSourceFacade {
|
||||||
|
public static func translateStatus(
|
||||||
|
provider: UIViewController & NeedsDependency & AuthContextProvider,
|
||||||
|
status: ManagedObjectRecord<Status>
|
||||||
|
) async throws {
|
||||||
|
let selectionFeedbackGenerator = await UISelectionFeedbackGenerator()
|
||||||
|
await selectionFeedbackGenerator.selectionChanged()
|
||||||
|
|
||||||
|
let status = status.object(in: provider.context.managedObjectContext)
|
||||||
|
status?.translatedContent = "LOREM IPSUM TRANSLATED TEXT"
|
||||||
|
}
|
||||||
|
}
|
|
@ -86,6 +86,14 @@ extension StatusTableViewCell {
|
||||||
self.accessibilityLabel = accessibilityLabel
|
self.accessibilityLabel = accessibilityLabel
|
||||||
}
|
}
|
||||||
.store(in: &_disposeBag)
|
.store(in: &_disposeBag)
|
||||||
|
|
||||||
|
statusView.viewModel
|
||||||
|
.$isTranslated
|
||||||
|
.receive(on: DispatchQueue.main)
|
||||||
|
.sink(receiveValue: { [weak self] _ in
|
||||||
|
self?.invalidateIntrinsicContentSize()
|
||||||
|
})
|
||||||
|
.store(in: &_disposeBag)
|
||||||
}
|
}
|
||||||
|
|
||||||
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
|
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
|
||||||
|
|
|
@ -81,6 +81,14 @@ extension StatusThreadRootTableViewCell {
|
||||||
// a11y
|
// a11y
|
||||||
statusView.contentMetaText.textView.isAccessibilityElement = true
|
statusView.contentMetaText.textView.isAccessibilityElement = true
|
||||||
statusView.contentMetaText.textView.isSelectable = true
|
statusView.contentMetaText.textView.isSelectable = true
|
||||||
|
|
||||||
|
statusView.viewModel
|
||||||
|
.$isTranslated
|
||||||
|
.receive(on: DispatchQueue.main)
|
||||||
|
.sink(receiveValue: { [weak self] _ in
|
||||||
|
self?.invalidateIntrinsicContentSize()
|
||||||
|
})
|
||||||
|
.store(in: &disposeBag)
|
||||||
}
|
}
|
||||||
|
|
||||||
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
|
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
|
||||||
|
|
|
@ -99,6 +99,8 @@ public final class Status: NSManagedObject {
|
||||||
@NSManaged public private(set) var deletedAt: Date?
|
@NSManaged public private(set) var deletedAt: Date?
|
||||||
// sourcery: autoUpdatableObject
|
// sourcery: autoUpdatableObject
|
||||||
@NSManaged public private(set) var revealedAt: Date?
|
@NSManaged public private(set) var revealedAt: Date?
|
||||||
|
|
||||||
|
@Published public var translatedContent: String?
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Status {
|
extension Status {
|
||||||
|
|
|
@ -37,6 +37,7 @@ extension NotificationView {
|
||||||
@Published public var isMyself = false
|
@Published public var isMyself = false
|
||||||
@Published public var isMuting = false
|
@Published public var isMuting = false
|
||||||
@Published public var isBlocking = false
|
@Published public var isBlocking = false
|
||||||
|
@Published public var isTranslated = false
|
||||||
|
|
||||||
@Published public var timestamp: Date?
|
@Published public var timestamp: Date?
|
||||||
|
|
||||||
|
@ -203,20 +204,27 @@ extension NotificationView.ViewModel {
|
||||||
$authorName,
|
$authorName,
|
||||||
$isMuting,
|
$isMuting,
|
||||||
$isBlocking,
|
$isBlocking,
|
||||||
$isMyself
|
Publishers.CombineLatest(
|
||||||
|
$isMyself,
|
||||||
|
$isTranslated
|
||||||
|
)
|
||||||
)
|
)
|
||||||
.sink { authorName, isMuting, isBlocking, isMyself in
|
.sink { authorName, isMuting, isBlocking, comb2 in
|
||||||
guard let name = authorName?.string else {
|
guard let name = authorName?.string else {
|
||||||
notificationView.menuButton.menu = nil
|
notificationView.menuButton.menu = nil
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let (isMyself, isTranslated) = comb2
|
||||||
|
|
||||||
let menuContext = NotificationView.AuthorMenuContext(
|
let menuContext = NotificationView.AuthorMenuContext(
|
||||||
name: name,
|
name: name,
|
||||||
isMuting: isMuting,
|
isMuting: isMuting,
|
||||||
isBlocking: isBlocking,
|
isBlocking: isBlocking,
|
||||||
isMyself: isMyself,
|
isMyself: isMyself,
|
||||||
isBookmarking: false // no bookmark action display for notification item
|
isBookmarking: false, // no bookmark action display for notification item
|
||||||
|
isTranslated: isTranslated,
|
||||||
|
statusLanguage: ""
|
||||||
)
|
)
|
||||||
let (menu, actions) = notificationView.setupAuthorMenu(menuContext: menuContext)
|
let (menu, actions) = notificationView.setupAuthorMenu(menuContext: menuContext)
|
||||||
notificationView.menuButton.menu = menu
|
notificationView.menuButton.menu = menu
|
||||||
|
|
|
@ -149,12 +149,21 @@ extension StatusAuthorView {
|
||||||
public let isBlocking: Bool
|
public let isBlocking: Bool
|
||||||
public let isMyself: Bool
|
public let isMyself: Bool
|
||||||
public let isBookmarking: Bool
|
public let isBookmarking: Bool
|
||||||
|
|
||||||
|
public let isTranslated: Bool
|
||||||
|
public let statusLanguage: String?
|
||||||
}
|
}
|
||||||
|
|
||||||
public func setupAuthorMenu(menuContext: AuthorMenuContext) -> (UIMenu, [UIAccessibilityCustomAction]) {
|
public func setupAuthorMenu(menuContext: AuthorMenuContext) -> (UIMenu, [UIAccessibilityCustomAction]) {
|
||||||
var actions = [MastodonMenu.Action]()
|
var actions = [MastodonMenu.Action]()
|
||||||
|
|
||||||
if !menuContext.isMyself {
|
if !menuContext.isMyself {
|
||||||
|
if let statusLanguage = menuContext.statusLanguage, !menuContext.isTranslated {
|
||||||
|
actions.append(
|
||||||
|
.translateStatus(.init(language: statusLanguage))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
actions.append(contentsOf: [
|
actions.append(contentsOf: [
|
||||||
.muteUser(.init(
|
.muteUser(.init(
|
||||||
name: menuContext.name,
|
name: menuContext.name,
|
||||||
|
|
|
@ -55,6 +55,14 @@ extension StatusView {
|
||||||
configurePoll(status: status)
|
configurePoll(status: status)
|
||||||
configureToolbar(status: status)
|
configureToolbar(status: status)
|
||||||
configureFilter(status: status)
|
configureFilter(status: status)
|
||||||
|
|
||||||
|
status.$translatedContent
|
||||||
|
.receive(on: DispatchQueue.main)
|
||||||
|
.compactMap { $0 }
|
||||||
|
.sink { [weak self] _ in
|
||||||
|
self?.configureTranslated(status: status)
|
||||||
|
}
|
||||||
|
.store(in: &disposeBag)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -231,7 +239,28 @@ extension StatusView {
|
||||||
.store(in: &disposeBag)
|
.store(in: &disposeBag)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func configureTranslated(status: Status) {
|
||||||
|
guard
|
||||||
|
let translatedContent = status.translatedContent
|
||||||
|
else { return }
|
||||||
|
|
||||||
|
// content
|
||||||
|
do {
|
||||||
|
let content = MastodonContent(content: translatedContent, emojis: status.emojis.asDictionary)
|
||||||
|
let metaContent = try MastodonMetaContent.convert(document: content)
|
||||||
|
viewModel.content = metaContent
|
||||||
|
viewModel.isTranslated = true
|
||||||
|
} catch {
|
||||||
|
assertionFailure(error.localizedDescription)
|
||||||
|
viewModel.content = PlaintextMetaContent(string: "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private func configureContent(status: Status) {
|
private func configureContent(status: Status) {
|
||||||
|
guard status.translatedContent == nil else {
|
||||||
|
return configureTranslated(status: status)
|
||||||
|
}
|
||||||
|
|
||||||
let status = status.reblog ?? status
|
let status = status.reblog ?? status
|
||||||
|
|
||||||
// spoilerText
|
// spoilerText
|
||||||
|
@ -254,6 +283,7 @@ extension StatusView {
|
||||||
let content = MastodonContent(content: status.content, emojis: status.emojis.asDictionary)
|
let content = MastodonContent(content: status.content, emojis: status.emojis.asDictionary)
|
||||||
let metaContent = try MastodonMetaContent.convert(document: content)
|
let metaContent = try MastodonMetaContent.convert(document: content)
|
||||||
viewModel.content = metaContent
|
viewModel.content = metaContent
|
||||||
|
viewModel.isTranslated = false
|
||||||
} catch {
|
} catch {
|
||||||
assertionFailure(error.localizedDescription)
|
assertionFailure(error.localizedDescription)
|
||||||
viewModel.content = PlaintextMetaContent(string: "")
|
viewModel.content = PlaintextMetaContent(string: "")
|
||||||
|
|
|
@ -17,6 +17,7 @@ import MastodonCommon
|
||||||
import MastodonExtension
|
import MastodonExtension
|
||||||
import MastodonLocalization
|
import MastodonLocalization
|
||||||
import MastodonSDK
|
import MastodonSDK
|
||||||
|
import MastodonMeta
|
||||||
|
|
||||||
extension StatusView {
|
extension StatusView {
|
||||||
public final class ViewModel: ObservableObject {
|
public final class ViewModel: ObservableObject {
|
||||||
|
@ -27,6 +28,7 @@ extension StatusView {
|
||||||
let logger = Logger(subsystem: "StatusView", category: "ViewModel")
|
let logger = Logger(subsystem: "StatusView", category: "ViewModel")
|
||||||
|
|
||||||
public var authContext: AuthContext?
|
public var authContext: AuthContext?
|
||||||
|
public var originalStatus: Status?
|
||||||
|
|
||||||
// Header
|
// Header
|
||||||
@Published public var header: Header = .none
|
@Published public var header: Header = .none
|
||||||
|
@ -42,6 +44,7 @@ extension StatusView {
|
||||||
@Published public var isMyself = false
|
@Published public var isMyself = false
|
||||||
@Published public var isMuting = false
|
@Published public var isMuting = false
|
||||||
@Published public var isBlocking = false
|
@Published public var isBlocking = false
|
||||||
|
@Published public var isTranslated = false
|
||||||
|
|
||||||
@Published public var timestamp: Date?
|
@Published public var timestamp: Date?
|
||||||
public var timestampFormatter: ((_ date: Date) -> String)?
|
public var timestampFormatter: ((_ date: Date) -> String)?
|
||||||
|
@ -134,6 +137,7 @@ extension StatusView {
|
||||||
isContentSensitive = false
|
isContentSensitive = false
|
||||||
isMediaSensitive = false
|
isMediaSensitive = false
|
||||||
isSensitiveToggled = false
|
isSensitiveToggled = false
|
||||||
|
isTranslated = false
|
||||||
|
|
||||||
activeFilters = []
|
activeFilters = []
|
||||||
filterContext = nil
|
filterContext = nil
|
||||||
|
@ -581,14 +585,20 @@ extension StatusView.ViewModel {
|
||||||
$isBlocking,
|
$isBlocking,
|
||||||
$isBookmark
|
$isBookmark
|
||||||
)
|
)
|
||||||
|
let publishersThree = Publishers.CombineLatest(
|
||||||
|
$isTranslated,
|
||||||
|
$language
|
||||||
|
)
|
||||||
|
|
||||||
Publishers.CombineLatest(
|
Publishers.CombineLatest3(
|
||||||
publisherOne.eraseToAnyPublisher(),
|
publisherOne.eraseToAnyPublisher(),
|
||||||
publishersTwo.eraseToAnyPublisher()
|
publishersTwo.eraseToAnyPublisher(),
|
||||||
|
publishersThree.eraseToAnyPublisher()
|
||||||
).eraseToAnyPublisher()
|
).eraseToAnyPublisher()
|
||||||
.sink { tupleOne, tupleTwo in
|
.sink { tupleOne, tupleTwo, tupleThree in
|
||||||
let (authorName, isMyself) = tupleOne
|
let (authorName, isMyself) = tupleOne
|
||||||
let (isMuting, isBlocking, isBookmark) = tupleTwo
|
let (isMuting, isBlocking, isBookmark) = tupleTwo
|
||||||
|
let (isTranslated, language) = tupleThree
|
||||||
|
|
||||||
guard let name = authorName?.string else {
|
guard let name = authorName?.string else {
|
||||||
statusView.authorView.menuButton.menu = nil
|
statusView.authorView.menuButton.menu = nil
|
||||||
|
@ -600,7 +610,9 @@ extension StatusView.ViewModel {
|
||||||
isMuting: isMuting,
|
isMuting: isMuting,
|
||||||
isBlocking: isBlocking,
|
isBlocking: isBlocking,
|
||||||
isMyself: isMyself,
|
isMyself: isMyself,
|
||||||
isBookmarking: isBookmark
|
isBookmarking: isBookmark,
|
||||||
|
isTranslated: isTranslated,
|
||||||
|
statusLanguage: language
|
||||||
)
|
)
|
||||||
let (menu, actions) = authorView.setupAuthorMenu(menuContext: menuContext)
|
let (menu, actions) = authorView.setupAuthorMenu(menuContext: menuContext)
|
||||||
authorView.menuButton.menu = menu
|
authorView.menuButton.menu = menu
|
||||||
|
|
|
@ -275,6 +275,16 @@ extension StatusView {
|
||||||
|
|
||||||
// statusMetricView
|
// statusMetricView
|
||||||
statusMetricView.delegate = self
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -40,6 +40,7 @@ public enum MastodonMenu {
|
||||||
|
|
||||||
extension MastodonMenu {
|
extension MastodonMenu {
|
||||||
public enum Action {
|
public enum Action {
|
||||||
|
case translateStatus(TranslateStatusActionContext)
|
||||||
case muteUser(MuteUserActionContext)
|
case muteUser(MuteUserActionContext)
|
||||||
case blockUser(BlockUserActionContext)
|
case blockUser(BlockUserActionContext)
|
||||||
case reportUser(ReportUserActionContext)
|
case reportUser(ReportUserActionContext)
|
||||||
|
@ -126,6 +127,15 @@ extension MastodonMenu {
|
||||||
delegate.menuAction(self)
|
delegate.menuAction(self)
|
||||||
}
|
}
|
||||||
return deleteAction
|
return deleteAction
|
||||||
|
case let .translateStatus(context):
|
||||||
|
let translateAction = BuiltAction(
|
||||||
|
title: String(format: "Translate from %@", context.language),
|
||||||
|
image: UIImage(systemName: "character.book.closed")
|
||||||
|
) { [weak delegate] in
|
||||||
|
guard let delegate = delegate else { return }
|
||||||
|
delegate.menuAction(self)
|
||||||
|
}
|
||||||
|
return translateAction
|
||||||
} // end switch
|
} // end switch
|
||||||
} // end func build
|
} // end func build
|
||||||
} // end enum Action
|
} // end enum Action
|
||||||
|
@ -225,4 +235,12 @@ extension MastodonMenu {
|
||||||
self.showReblogs = showReblogs
|
self.showReblogs = showReblogs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public struct TranslateStatusActionContext {
|
||||||
|
public let language: String
|
||||||
|
|
||||||
|
public init(language: String) {
|
||||||
|
self.language = language
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue