Skip to content

Commit

Permalink
IOS-271: Fix translate functionality leads to empty status content (#…
Browse files Browse the repository at this point in the history
…1320)

This PR solves the following things:

1. Fixes the post body being set to an empty string in case translation
fails
2. Fetches `/api/v1/instance/translation_languages` (only if the
`/api/v2/instance` is available) and checks if the transaction between
the desired languages is possible
3. Refactors `Instance` usage to now use
`MastodonAuthentication.InstanceConfiguration` if feasible with low
effort
4. On the home timeline, the reblog status (the wrapper) is taken into
account for translation which leads to the translation failing
  • Loading branch information
kimar authored Jun 26, 2024
2 parents ca68a6c + 7342b92 commit 1a8fbe7
Show file tree
Hide file tree
Showing 18 changed files with 207 additions and 422 deletions.
3 changes: 2 additions & 1 deletion Mastodon/Protocol/Provider/DataSourceFacade+Status.swift
Original file line number Diff line number Diff line change
Expand Up @@ -317,13 +317,14 @@ extension DataSourceFacade {
performDeletion(of: status, with: dependency)
}
case .translateStatus:
guard let status = menuContext.statusViewModel?.originalStatus else { return }
guard let status = menuContext.statusViewModel?.originalStatus?.reblog ?? menuContext.statusViewModel?.originalStatus else { return }

do {
let translation = try await DataSourceFacade.translateStatus(provider: dependency,status: status)

menuContext.statusViewModel?.translation = translation
} catch TranslationFailure.emptyOrInvalidResponse {
menuContext.statusViewModel?.isCurrentlyTranslating = false
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))
dependency.present(alertController, animated: true)
Expand Down
6 changes: 3 additions & 3 deletions Mastodon/Protocol/Provider/DataSourceFacade+Translate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ extension DataSourceFacade {
public static func translateStatus(
provider: Provider,
status: MastodonStatus
) async throws -> Mastodon.Entity.Translation? {
) async throws -> Mastodon.Entity.Translation {
FeedbackGenerator.shared.generate(.selectionChanged)

do {
Expand All @@ -32,10 +32,10 @@ extension DataSourceFacade {
authenticationBox: provider.authContext.mastodonAuthenticationBox
).value

if value.content != nil {
if let content = value.content, content.isNotEmpty {
return value
} else {
return nil
throw TranslationFailure.emptyOrInvalidResponse
}

} catch {
Expand Down
4 changes: 2 additions & 2 deletions Mastodon/Scene/Profile/ProfileViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -995,8 +995,8 @@ extension ProfileViewController: PagerTabStripNavigateable {
}

private extension ProfileViewController {
var currentInstance: Instance? {
authContext.mastodonAuthenticationBox.authentication.instance(in: context.managedObjectContext)
var currentInstance: MastodonAuthentication.InstanceConfiguration? {
authContext.mastodonAuthenticationBox.authentication.instanceConfiguration
}
}

Expand Down
79 changes: 0 additions & 79 deletions MastodonSDK/Sources/CoreDataStack/Entity/Mastodon/Instance.swift

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,7 @@ final public class MastodonAuthenticationLegacy: NSManagedObject {
@NSManaged public private(set) var activedAt: Date

// one-to-one relationship
@NSManaged public private(set) var user: MastodonUser

// many-to-one relationship
@NSManaged public private(set) var instance: Instance?

@NSManaged public private(set) var user: MastodonUser
}

extension MastodonAuthenticationLegacy {
Expand Down Expand Up @@ -101,12 +97,6 @@ extension MastodonAuthenticationLegacy {
}
}

public func update(instance: Instance) {
if self.instance != instance {
self.instance = instance
}
}

public func didUpdate(at networkDate: Date) {
self.updatedAt = networkDate
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,35 @@ public class AuthenticationServiceProvider: ObservableObject {
persist() // todo: Is this too heavy and too often here???
}
}

func update(instance: Instance, where domain: String) {

@MainActor
@discardableResult
func updating(instanceV1 instance: Mastodon.Entity.Instance, for domain: String) -> Self {
authentications = authentications.map { authentication in
guard authentication.domain == domain else { return authentication }
return authentication.updating(instanceV1: instance)
}
return self
}

@MainActor
@discardableResult
func updating(instanceV2 instance: Mastodon.Entity.V2.Instance, for domain: String) -> Self {
authentications = authentications.map { authentication in
guard authentication.domain == domain else { return authentication }
return authentication.updating(instanceV2: instance)
}
return self
}

@MainActor
@discardableResult
func updating(translationLanguages: TranslationLanguages, for domain: String) -> Self {
authentications = authentications.map { authentication in
guard authentication.domain == domain else { return authentication }
return authentication.updating(instance: instance)
return authentication.updating(translationLanguages: translationLanguages)
}
return self
}

func delete(authentication: MastodonAuthentication) throws {
Expand Down

This file was deleted.

88 changes: 66 additions & 22 deletions MastodonSDK/Sources/MastodonCore/MastodonAuthentication.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,41 @@ import CoreDataStack
import MastodonSDK

public struct MastodonAuthentication: Codable, Hashable, UserIdentifier {
public enum InstanceConfiguration: Codable, Hashable {
case v1(Mastodon.Entity.Instance)
case v2(Mastodon.Entity.V2.Instance, TranslationLanguages)

public func canTranslateFrom(_ sourceLocale: String, to targetLanguage: String) -> Bool {
switch self {
case .v1:
return false
case let .v2(instance, translationLanguages):
guard instance.configuration?.translation?.enabled == true else { return false }
return translationLanguages[sourceLocale]?.contains(targetLanguage) == true
}
}

public var instanceConfigLimitingProperties: InstanceConfigLimitingPropertyContaining? {
switch self {
case let .v1(instance):
return instance.configuration
case let .v2(instance, _):
return instance.configuration
}
}

public var canFollowTags: Bool {
let version: String?
switch self {
case let .v1(instance):
version = instance.version
case let .v2(instance, _):
version = instance.version
}
return version?.majorServerVersion(greaterThanOrEquals: 4) ?? false // following Tags is support beginning with Mastodon v4.0.0
}
}

public typealias ID = UUID

public private(set) var identifier: ID
Expand All @@ -21,7 +56,8 @@ public struct MastodonAuthentication: Codable, Hashable, UserIdentifier {
public private(set) var activedAt: Date

public private(set) var userID: String
public private(set) var instanceObjectIdURI: URL?

public private(set) var instanceConfiguration: InstanceConfiguration?

public var persistenceIdentifier: String {
"\(username)@\(domain)"
Expand Down Expand Up @@ -49,7 +85,7 @@ public struct MastodonAuthentication: Codable, Hashable, UserIdentifier {
updatedAt: now,
activedAt: now,
userID: userID,
instanceObjectIdURI: nil
instanceConfiguration: nil
)
}

Expand All @@ -65,7 +101,7 @@ public struct MastodonAuthentication: Codable, Hashable, UserIdentifier {
updatedAt: Date? = nil,
activedAt: Date? = nil,
userID: String? = nil,
instanceObjectIdURI: URL? = nil
instanceConfiguration: InstanceConfiguration? = nil
) -> Self {
MastodonAuthentication(
identifier: identifier ?? self.identifier,
Expand All @@ -79,25 +115,9 @@ public struct MastodonAuthentication: Codable, Hashable, UserIdentifier {
updatedAt: updatedAt ?? self.updatedAt,
activedAt: activedAt ?? self.activedAt,
userID: userID ?? self.userID,
instanceObjectIdURI: instanceObjectIdURI ?? self.instanceObjectIdURI
instanceConfiguration: instanceConfiguration ?? self.instanceConfiguration
)
}

public func instance(in context: NSManagedObjectContext) -> Instance? {
guard let instanceObjectIdURI,
let objectID = context.persistentStoreCoordinator?.managedObjectID(forURIRepresentation: instanceObjectIdURI)
else {
return nil
}

let instance = try? context.existingObject(with: objectID) as? Instance
return instance
}

public func user(in context: NSManagedObjectContext) -> MastodonUser? {
let userPredicate = MastodonUser.predicate(domain: domain, id: userID)
return MastodonUser.findOrFetch(in: context, matching: userPredicate)
}

public func account() -> Mastodon.Entity.Account? {

Expand All @@ -113,8 +133,32 @@ public struct MastodonAuthentication: Codable, Hashable, UserIdentifier {
MastodonUserIdentifier(domain: domain, userID: userID)
}

func updating(instance: Instance) -> Self {
copy(instanceObjectIdURI: instance.objectID.uriRepresentation())
@MainActor
func updating(instanceV1 instance: Mastodon.Entity.Instance) -> Self {
return copy(instanceConfiguration: .v1(instance))
}

@MainActor
func updating(instanceV2 instance: Mastodon.Entity.V2.Instance) -> Self {
guard
let instanceConfiguration,
case let InstanceConfiguration.v2(_, translationLanguages) = instanceConfiguration
else {
return copy(instanceConfiguration: .v2(instance, [:]))
}
return copy(instanceConfiguration: .v2(instance, translationLanguages))
}

@MainActor
func updating(translationLanguages: TranslationLanguages) -> Self {
switch self.instanceConfiguration {
case .v1(let instance):
return copy(instanceConfiguration: .v1(instance))
case .v2(let instance, _):
return copy(instanceConfiguration: .v2(instance, translationLanguages))
case .none:
return self
}
}

func updating(activatedAt: Date) -> Self {
Expand Down
Loading

0 comments on commit 1a8fbe7

Please sign in to comment.