From 87cb71da5dab2c91d759c2c2f3665fcf984aafe2 Mon Sep 17 00:00:00 2001 From: Nathan Mattes Date: Mon, 16 Oct 2023 13:28:12 +0200 Subject: [PATCH] Get rid of translation-persistence --- .../Provider/DataSourceFacade+Status.swift | 22 +++++------ .../Provider/DataSourceFacade+Translate.swift | 25 ++++--------- ...er+NotificationTableViewCellDelegate.swift | 2 +- ...Provider+StatusTableViewCellDelegate.swift | 14 ++++++- .../Scene/Profile/ProfileViewController.swift | 2 +- .../Entity/Mastodon/Status.swift | 20 +--------- .../Content/StatusView+Configuration.swift | 37 ++++++------------- .../View/Content/StatusView+ViewModel.swift | 6 ++- 8 files changed, 50 insertions(+), 78 deletions(-) diff --git a/Mastodon/Protocol/Provider/DataSourceFacade+Status.swift b/Mastodon/Protocol/Provider/DataSourceFacade+Status.swift index da5e97820..d304816d0 100644 --- a/Mastodon/Protocol/Provider/DataSourceFacade+Status.swift +++ b/Mastodon/Protocol/Provider/DataSourceFacade+Status.swift @@ -151,7 +151,7 @@ extension DataSourceFacade { struct MenuContext { let author: ManagedObjectRecord? - let status: ManagedObjectRecord? + let statusViewModel: StatusView.ViewModel? let button: UIButton? let barButtonItem: UIBarButtonItem? } @@ -266,7 +266,7 @@ extension DataSourceFacade { context: dependency.context, authContext: dependency.authContext, user: user, - status: menuContext.status + status: menuContext.statusViewModel?.originalStatus?.asRecord ) _ = dependency.coordinator.present( @@ -297,7 +297,7 @@ extension DataSourceFacade { ) case .bookmarkStatus: Task { - guard let status = menuContext.status else { + guard let status = menuContext.statusViewModel?.originalStatus?.asRecord else { assertionFailure() return } @@ -310,7 +310,7 @@ extension DataSourceFacade { Task { let managedObjectContext = dependency.context.managedObjectContext guard let status: ManagedObjectRecord = try? await managedObjectContext.perform(block: { - guard let object = menuContext.status?.object(in: managedObjectContext) else { return nil } + guard let object = menuContext.statusViewModel?.originalStatus?.asRecord.object(in: managedObjectContext) else { return nil } let objectID = (object.reblog ?? object).objectID return .init(objectID: objectID) }) else { @@ -344,7 +344,7 @@ extension DataSourceFacade { style: .destructive ) { [weak dependency] _ in guard let dependency = dependency else { return } - guard let status = menuContext.status else { return } + guard let status = menuContext.statusViewModel?.originalStatus?.asRecord else { return } Task { try await DataSourceFacade.responseToDeleteStatus( dependency: dependency, @@ -358,12 +358,12 @@ extension DataSourceFacade { dependency.present(alertController, animated: true) case .translateStatus: - guard let status = menuContext.status else { return } + guard let status = menuContext.statusViewModel?.originalStatus?.asRecord else { return } + do { - try await DataSourceFacade.translateStatus( - provider: dependency, - status: status - ) + let translation = try await DataSourceFacade.translateStatus(provider: dependency,status: status) + + menuContext.statusViewModel?.translation = translation } catch TranslationFailure.emptyOrInvalidResponse { let alertController = UIAlertController(title: L10n.Common.Alerts.TranslationFailed.title, message: L10n.Common.Alerts.TranslationFailed.message, preferredStyle: .alert) alertController.addAction(UIAlertAction(title: L10n.Common.Alerts.TranslationFailed.button, style: .default)) @@ -371,7 +371,7 @@ extension DataSourceFacade { } case .editStatus: - guard let status = menuContext.status?.object(in: dependency.context.managedObjectContext) else { return } + guard let status = menuContext.statusViewModel?.originalStatus?.asRecord.object(in: dependency.context.managedObjectContext) else { return } let statusSource = try await dependency.context.apiService.getStatusSource( forStatusID: status.id, diff --git a/Mastodon/Protocol/Provider/DataSourceFacade+Translate.swift b/Mastodon/Protocol/Provider/DataSourceFacade+Translate.swift index 8ce9c2447..912540f69 100644 --- a/Mastodon/Protocol/Provider/DataSourceFacade+Translate.swift +++ b/Mastodon/Protocol/Provider/DataSourceFacade+Translate.swift @@ -9,6 +9,7 @@ import UIKit import CoreData import CoreDataStack import MastodonCore +import MastodonSDK typealias Provider = UIViewController & NeedsDependency & AuthContextProvider @@ -20,26 +21,26 @@ extension DataSourceFacade { public static func translateStatus( provider: Provider, status: ManagedObjectRecord - ) async throws { + ) async throws -> Mastodon.Entity.Translation? { let selectionFeedbackGenerator = await UISelectionFeedbackGenerator() await selectionFeedbackGenerator.selectionChanged() guard let status = status.object(in: provider.context.managedObjectContext) else { - return + return nil } if let reblog = status.reblog { - try await translateAndApply(provider: provider, status: reblog) + return try await translateStatus(provider: provider, status: reblog) } else { - try await translateAndApply(provider: provider, status: status) + return try await translateStatus(provider: provider, status: status) } } } private extension DataSourceFacade { - static func translateStatus(provider: Provider, status: Status) async throws -> Status.TranslatedContent? { + static func translateStatus(provider: Provider, status: Status) async throws -> Mastodon.Entity.Translation? { do { let value = try await provider.context .apiService @@ -49,22 +50,12 @@ private extension DataSourceFacade { ).value guard let content = value.content else { - throw TranslationFailure.emptyOrInvalidResponse + return nil } - return Status.TranslatedContent(content: content, provider: value.provider) + return value } catch { throw TranslationFailure.emptyOrInvalidResponse } } - - static func translateAndApply(provider: Provider, status: Status) async throws { - do { - let translated = try await translateStatus(provider: provider, status: status) - status.update(translatedContent: translated) - } catch { - status.update(translatedContent: nil) - throw TranslationFailure.emptyOrInvalidResponse - } - } } diff --git a/Mastodon/Protocol/Provider/DataSourceProvider+NotificationTableViewCellDelegate.swift b/Mastodon/Protocol/Provider/DataSourceProvider+NotificationTableViewCellDelegate.swift index 019f780bb..0974510bf 100644 --- a/Mastodon/Protocol/Provider/DataSourceProvider+NotificationTableViewCellDelegate.swift +++ b/Mastodon/Protocol/Provider/DataSourceProvider+NotificationTableViewCellDelegate.swift @@ -44,7 +44,7 @@ extension NotificationTableViewCellDelegate where Self: DataSourceProvider & Aut action: action, menuContext: .init( author: author, - status: nil, + statusViewModel: nil, button: button, barButtonItem: nil ) diff --git a/Mastodon/Protocol/Provider/DataSourceProvider+StatusTableViewCellDelegate.swift b/Mastodon/Protocol/Provider/DataSourceProvider+StatusTableViewCellDelegate.swift index 85718c94a..dafd65fdf 100644 --- a/Mastodon/Protocol/Provider/DataSourceProvider+StatusTableViewCellDelegate.swift +++ b/Mastodon/Protocol/Provider/DataSourceProvider+StatusTableViewCellDelegate.swift @@ -498,13 +498,23 @@ extension StatusTableViewCellDelegate where Self: DataSourceProvider & AuthConte } } } - + + let statusViewModel: StatusView.ViewModel? + + if let cell = cell as? StatusTableViewCell { + statusViewModel = await cell.statusView.viewModel + } else if let cell = cell as? StatusThreadRootTableViewCell { + statusViewModel = await cell.statusView.viewModel + } else { + statusViewModel = nil + } + try await DataSourceFacade.responseToMenuAction( dependency: self, action: action, menuContext: .init( author: author, - status: status, + statusViewModel: statusViewModel, button: button, barButtonItem: nil ) diff --git a/Mastodon/Scene/Profile/ProfileViewController.swift b/Mastodon/Scene/Profile/ProfileViewController.swift index c83401329..1edb7e5f7 100644 --- a/Mastodon/Scene/Profile/ProfileViewController.swift +++ b/Mastodon/Scene/Profile/ProfileViewController.swift @@ -894,7 +894,7 @@ extension ProfileViewController: MastodonMenuDelegate { action: action, menuContext: DataSourceFacade.MenuContext( author: userRecord, - status: nil, + statusViewModel: nil, button: nil, barButtonItem: self.moreMenuBarButtonItem ) diff --git a/MastodonSDK/Sources/CoreDataStack/Entity/Mastodon/Status.swift b/MastodonSDK/Sources/CoreDataStack/Entity/Mastodon/Status.swift index b9fcf3a3c..736d4b7eb 100644 --- a/MastodonSDK/Sources/CoreDataStack/Entity/Mastodon/Status.swift +++ b/MastodonSDK/Sources/CoreDataStack/Entity/Mastodon/Status.swift @@ -10,17 +10,7 @@ import Foundation public final class Status: NSManagedObject { public typealias ID = String - - public class TranslatedContent: NSObject { - public let content: String - public let provider: String? - - public init(content: String, provider: String?) { - self.content = content - self.provider = provider - } - } - + // sourcery: autoGenerateProperty @NSManaged public private(set) var identifier: ID // sourcery: autoGenerateProperty @@ -118,9 +108,6 @@ public final class Status: NSManagedObject { @NSManaged public private(set) var deletedAt: Date? // sourcery: autoUpdatableObject @NSManaged public private(set) var revealedAt: Date? - - // sourcery: autoUpdatableObject - @NSManaged public private(set) var translatedContent: TranslatedContent? } extension Status { @@ -535,11 +522,6 @@ extension Status: AutoUpdatableObject { self.revealedAt = revealedAt } } - public func update(translatedContent: TranslatedContent?) { - if self.translatedContent != translatedContent { - self.translatedContent = translatedContent - } - } public func update(attachments: [MastodonAttachment]) { if self.attachments != attachments { self.attachments = attachments diff --git a/MastodonSDK/Sources/MastodonUI/View/Content/StatusView+Configuration.swift b/MastodonSDK/Sources/MastodonUI/View/Content/StatusView+Configuration.swift index 653854bf4..bc0ebf10a 100644 --- a/MastodonSDK/Sources/MastodonUI/View/Content/StatusView+Configuration.swift +++ b/MastodonSDK/Sources/MastodonUI/View/Content/StatusView+Configuration.swift @@ -85,13 +85,10 @@ extension StatusView { configureToolbar(status: status) configureFilter(status: status) viewModel.originalStatus = status - [ - status.publisher(for: \.translatedContent), - status.reblog?.publisher(for: \.translatedContent) - ].compactMap { $0 } - .last? + + viewModel.$translation .receive(on: DispatchQueue.main) - .sink { [weak self] _ in + .sink { [weak self] translation in self?.configureTranslated(status: status) } .store(in: &disposeBag) @@ -293,36 +290,26 @@ extension StatusView { public func revertTranslation() { guard let originalStatus = viewModel.originalStatus else { return } - viewModel.translatedFromLanguage = nil - viewModel.translatedUsingProvider = nil - originalStatus.reblog?.update(translatedContent: nil) - originalStatus.update(translatedContent: nil) + viewModel.translation = nil configure(status: originalStatus) } func configureTranslated(status: Status) { - let translatedContent: Status.TranslatedContent? = { - if let translatedContent = status.reblog?.translatedContent { - return translatedContent - } - return status.translatedContent - - }() - - guard - let translatedContent = translatedContent - else { + guard let translation = viewModel.translation, + let translatedContent = translation.content, + let sourceLanguage = translation.sourceLanguage, + let provider = translation.provider else { viewModel.isCurrentlyTranslating = false return } // content do { - let content = MastodonContent(content: translatedContent.content, emojis: status.emojis.asDictionary) + let content = MastodonContent(content: translatedContent, emojis: status.emojis.asDictionary) let metaContent = try MastodonMetaContent.convert(document: content) viewModel.content = metaContent - viewModel.translatedFromLanguage = status.reblog?.language ?? status.language - viewModel.translatedUsingProvider = status.reblog?.translatedContent?.provider ?? status.translatedContent?.provider + viewModel.translatedFromLanguage = sourceLanguage + viewModel.translatedUsingProvider = provider viewModel.isCurrentlyTranslating = false } catch { assertionFailure(error.localizedDescription) @@ -351,7 +338,7 @@ extension StatusView { } private func configureContent(status: Status) { - guard status.translatedContent == nil else { + guard viewModel.translation == nil else { return configureTranslated(status: status) } diff --git a/MastodonSDK/Sources/MastodonUI/View/Content/StatusView+ViewModel.swift b/MastodonSDK/Sources/MastodonUI/View/Content/StatusView+ViewModel.swift index ab8b449d4..f2a45ff24 100644 --- a/MastodonSDK/Sources/MastodonUI/View/Content/StatusView+ViewModel.swift +++ b/MastodonSDK/Sources/MastodonUI/View/Content/StatusView+ViewModel.swift @@ -48,6 +48,7 @@ extension StatusView { @Published public var isCurrentlyTranslating = false @Published public var translatedFromLanguage: String? @Published public var translatedUsingProvider: String? + @Published public var translation: Mastodon.Entity.Translation? = nil @Published public var timestamp: Date? public var timestampFormatter: ((_ date: Date, _ isEdited: Bool) -> String)? @@ -151,7 +152,8 @@ extension StatusView { translatedFromLanguage = nil translatedUsingProvider = nil isCurrentlyTranslating = false - + translation = nil + activeFilters = [] filterContext = nil } @@ -657,7 +659,7 @@ extension StatusView.ViewModel { $isFollowed ) let publishersThree = Publishers.CombineLatest( - $translatedFromLanguage, + $translation, $language )