damus

nostr ios client
git clone git://jb55.com/damus
Log | Files | Refs | README | LICENSE

commit 2a19d5d831abaf6a9e6e45cfde837bbb3b7d877c
parent 51ee4046a0471369485c4f9838e1958d56245b7d
Author: Terry Yiu <git@tyiu.xyz>
Date:   Fri, 21 Jun 2024 00:11:18 -0400

Add Apple translation popovers for notes for iOS 17.4+ and macOS 14.4+

Changelog-Added: Add Apple translation popovers for notes for iOS 17.4+ and macOS 14.4+

Diffstat:
Mdamus/Components/TranslateView.swift | 31++++++++++++++++++++++++-------
Mdamus/Models/TranslationService.swift | 16+++++++++++++++-
Mdamus/Util/EventCache.swift | 26+++++++++++++++-----------
Mdamus/Views/NoteContentView.swift | 14+++++++++++---
4 files changed, 65 insertions(+), 22 deletions(-)

diff --git a/damus/Components/TranslateView.swift b/damus/Components/TranslateView.swift @@ -27,19 +27,26 @@ struct TranslateView: View { let damus_state: DamusState let event: NostrEvent let size: EventViewKind - + + @Binding var isAppleTranslationPopoverPresented: Bool + @ObservedObject var translations_model: TranslationModel - init(damus_state: DamusState, event: NostrEvent, size: EventViewKind) { + init(damus_state: DamusState, event: NostrEvent, size: EventViewKind, isAppleTranslationPopoverPresented: Binding<Bool>) { self.damus_state = damus_state self.event = event self.size = size + self._isAppleTranslationPopoverPresented = isAppleTranslationPopoverPresented self._translations_model = ObservedObject(wrappedValue: damus_state.events.get_cache_data(event.id).translations_model) } var TranslateButton: some View { Button(NSLocalizedString("Translate Note", comment: "Button to translate note from different language.")) { - translate() + if damus_state.settings.translation_service == .none { + isAppleTranslationPopoverPresented = true + } else { + translate() + } } .translate_button_style() } @@ -74,17 +81,25 @@ struct TranslateView: View { } func should_transl(_ note_lang: String) -> Bool { - should_translate(event: event, our_keypair: damus_state.keypair, settings: damus_state.settings, note_lang: note_lang) + guard should_translate(event: event, our_keypair: damus_state.keypair, note_lang: note_lang) else { + return false + } + + if TranslationService.isAppleTranslationPopoverSupported { + return damus_state.settings.translation_service == .none || damus_state.settings.can_translate + } else { + return damus_state.settings.can_translate + } } var body: some View { Group { switch self.translations_model.state { case .havent_tried: - if damus_state.settings.auto_translate { + if damus_state.settings.auto_translate && damus_state.settings.translation_service != .none { Text("") } else if let note_lang = translations_model.note_language, should_transl(note_lang) { - TranslateButton + TranslateButton } else { Text("") } @@ -114,9 +129,11 @@ extension View { } struct TranslateView_Previews: PreviewProvider { + @State static var isAppleTranslationPopoverPresented: Bool = false + static var previews: some View { let ds = test_damus_state - TranslateView(damus_state: ds, event: test_note, size: .normal) + TranslateView(damus_state: ds, event: test_note, size: .normal, isAppleTranslationPopoverPresented: $isAppleTranslationPopoverPresented) } } diff --git a/damus/Models/TranslationService.swift b/damus/Models/TranslationService.swift @@ -38,7 +38,13 @@ enum TranslationService: String, CaseIterable, Identifiable, StringCodable { var model: Model { switch self { case .none: - return .init(tag: self.rawValue, displayName: NSLocalizedString("none_translation_service", value: "None", comment: "Dropdown option for selecting no translation service.")) + let displayName: String + if TranslationService.isAppleTranslationPopoverSupported { + displayName = NSLocalizedString("apple_translation_service", value: "Apple", comment: "Dropdown option for selecting Apple as a translation service.") + } else { + displayName = NSLocalizedString("none_translation_service", value: "None", comment: "Dropdown option for selecting no translation service.") + } + return .init(tag: self.rawValue, displayName: displayName) case .purple: return .init(tag: self.rawValue, displayName: NSLocalizedString("Damus Purple", comment: "Dropdown option for selecting Damus Purple as a translation service.")) case .libretranslate: @@ -51,4 +57,12 @@ enum TranslationService: String, CaseIterable, Identifiable, StringCodable { return .init(tag: self.rawValue, displayName: NSLocalizedString("translate.nostr.wine (DeepL, Pay with BTC)", comment: "Dropdown option for selecting translate.nostr.wine as the translation service.")) } } + + static var isAppleTranslationPopoverSupported: Bool { + if #available(iOS 17.4, macOS 14.4, *) { + return true + } else { + return false + } + } } diff --git a/damus/Util/EventCache.swift b/damus/Util/EventCache.swift @@ -244,16 +244,12 @@ class EventCache { } } -func should_translate(event: NostrEvent, our_keypair: Keypair, settings: UserSettingsStore, note_lang: String?) -> Bool { - guard settings.can_translate else { - return false - } - +func should_translate(event: NostrEvent, our_keypair: Keypair, note_lang: String?) -> Bool { // don't translate reposts, longform, etc if event.kind != 1 { return false; } - + // Do not translate self-authored notes if logged in with a private key // as we can assume the user can understand their own notes. // The detected language prediction could be incorrect and not in the list of preferred languages. @@ -261,25 +257,33 @@ func should_translate(event: NostrEvent, our_keypair: Keypair, settings: UserSet if our_keypair.privkey != nil && our_keypair.pubkey == event.pubkey { return false } - + if let note_lang { let preferredLanguages = Set(Locale.preferredLanguages.map { localeToLanguage($0) }) - + // Don't translate if its in our preferred languages guard !preferredLanguages.contains(note_lang) else { // if its the same, give up and don't retry return false } } - + // we should start translating if we have auto_translate on return true } +func can_and_should_translate(event: NostrEvent, our_keypair: Keypair, settings: UserSettingsStore, note_lang: String?) -> Bool { + guard settings.can_translate else { + return false + } + + return should_translate(event: event, our_keypair: our_keypair, note_lang: note_lang) +} + func should_preload_translation(event: NostrEvent, our_keypair: Keypair, current_status: TranslateStatus, settings: UserSettingsStore, note_lang: String?) -> Bool { switch current_status { case .havent_tried: - return should_translate(event: event, our_keypair: our_keypair, settings: settings, note_lang: note_lang) && settings.auto_translate + return can_and_should_translate(event: event, our_keypair: our_keypair, settings: settings, note_lang: note_lang) && settings.auto_translate case .translating: return false case .translated: return false case .not_needed: return false @@ -413,7 +417,7 @@ func preload_event(plan: PreloadPlan, state: DamusState) async { var translations: TranslateStatus? = nil // We have to recheck should_translate here now that we have note_language - if plan.load_translations && should_translate(event: plan.event, our_keypair: our_keypair, settings: settings, note_lang: note_language) && settings.auto_translate + if plan.load_translations && can_and_should_translate(event: plan.event, our_keypair: our_keypair, settings: settings, note_lang: note_language) && settings.auto_translate { translations = await translate_note(profiles: profiles, keypair: our_keypair, event: plan.event, settings: settings, note_lang: note_language, purple: state.purple) } diff --git a/damus/Views/NoteContentView.swift b/damus/Views/NoteContentView.swift @@ -9,6 +9,7 @@ import SwiftUI import LinkPresentation import NaturalLanguage import MarkdownUI +import Translation struct Blur: UIViewRepresentable { var style: UIBlurEffect.Style = .systemUltraThinMaterial @@ -32,6 +33,8 @@ struct NoteContentView: View { let preview_height: CGFloat? let options: EventViewOptions + @State var isAppleTranslationPopoverPresented: Bool = false + @ObservedObject var artifacts_model: NoteArtifactsModel @ObservedObject var preview_model: PreviewModel @ObservedObject var settings: UserSettingsStore @@ -96,7 +99,7 @@ struct NoteContentView: View { } var translateView: some View { - TranslateView(damus_state: damus_state, event: event, size: self.size) + TranslateView(damus_state: damus_state, event: event, size: self.size, isAppleTranslationPopoverPresented: $isAppleTranslationPopoverPresented) } func previewView(links: [URL]) -> some View { @@ -145,7 +148,7 @@ struct NoteContentView: View { } } - if !options.contains(.no_translate) && (size == .selected || damus_state.settings.auto_translate) { + if !options.contains(.no_translate) && (size == .selected || TranslationService.isAppleTranslationPopoverSupported || damus_state.settings.auto_translate) { if with_padding { translateView .padding(.horizontal) @@ -298,7 +301,12 @@ struct NoteContentView: View { Markdown(md.markdown) .padding([.leading, .trailing, .top]) case .separated(let separated): - MainContent(artifacts: separated) + if #available(iOS 17.4, macOS 14.4, *) { + MainContent(artifacts: separated) + .translationPresentation(isPresented: $isAppleTranslationPopoverPresented, text: event.get_content(damus_state.keypair)) + } else { + MainContent(artifacts: separated) + } } } .fixedSize(horizontal: false, vertical: true)