From a5b43ea0465a67ded250d1c0b70ee158d99a0374 Mon Sep 17 00:00:00 2001 From: Max Goedjen Date: Tue, 9 Sep 2025 23:00:26 -0700 Subject: [PATCH] Cleanup and string fixes (#684) * Setup UI tweaks. * Protection level string * Toolbar cleanup * More strings. --- .../Packages/Resources/Localizable.xcstrings | 551 +----------------- .../PersistentAuthenticationHandler.swift | 8 +- .../Views/Configuration/SetupView.swift | 26 +- .../Views/Secrets/CreateSecretView.swift | 2 +- .../Secretive/Views/Views/ContentView.swift | 23 +- 5 files changed, 36 insertions(+), 574 deletions(-) diff --git a/Sources/Packages/Resources/Localizable.xcstrings b/Sources/Packages/Resources/Localizable.xcstrings index 89f8b34..38e0bbd 100644 --- a/Sources/Packages/Resources/Localizable.xcstrings +++ b/Sources/Packages/Resources/Localizable.xcstrings @@ -411,83 +411,6 @@ } } }, - "agent_setup_notice_title" : { - "extractionState" : "manual", - "localizations" : { - "ca" : { - "stringUnit" : { - "state" : "translated", - "value" : "Inicialitza Secretive" - } - }, - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Secretive Einrichten" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Setup Secretive" - } - }, - "fi" : { - "stringUnit" : { - "state" : "translated", - "value" : "Asenna Secretive" - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Configurer Secretive" - } - }, - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "Imposta Secretive" - } - }, - "ja" : { - "stringUnit" : { - "state" : "translated", - "value" : "Secretiveをセットアップ" - } - }, - "ko" : { - "stringUnit" : { - "state" : "translated", - "value" : "Secretive 설치" - } - }, - "pl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Konfiguracja Secretive" - } - }, - "pt-BR" : { - "stringUnit" : { - "state" : "translated", - "value" : "Configurar Secretive" - } - }, - "ru" : { - "stringUnit" : { - "state" : "translated", - "value" : "Настроить Secretive" - } - }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "设置 Secretive" - } - } - } - }, "agentDetailsLocationTitle" : { "extractionState" : "manual", "localizations" : { @@ -653,83 +576,6 @@ } } }, - "app_menu_setup_button" : { - "extractionState" : "manual", - "localizations" : { - "ca" : { - "stringUnit" : { - "state" : "translated", - "value" : "Inicialitza Secretive" - } - }, - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Secretive Einrichten" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Setup Secretive" - } - }, - "fi" : { - "stringUnit" : { - "state" : "translated", - "value" : "Asenna Secretive" - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Configurer Secretive" - } - }, - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "Imposta Secretive" - } - }, - "ja" : { - "stringUnit" : { - "state" : "translated", - "value" : "セットアップ" - } - }, - "ko" : { - "stringUnit" : { - "state" : "translated", - "value" : "Secretive 설치" - } - }, - "pl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Skonfiguruj Scretive" - } - }, - "pt-BR" : { - "stringUnit" : { - "state" : "translated", - "value" : "Configurar Secretive" - } - }, - "ru" : { - "stringUnit" : { - "state" : "translated", - "value" : "Настроить Secretive" - } - }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "设置 Secretive" - } - } - } - }, "app_not_in_applications_notice_detail_description" : { "extractionState" : "manual", "localizations" : { @@ -962,84 +808,6 @@ } } }, - "auth_context_persist_for_duration_unknown" : { - "comment" : "When the user clicks the notification to leave a secret unlocked, they are shown a prompt to approve the action. This is the description, showing which secret will used. The placeholder is the name of the secret. This is a fallback used when a duration is unable to be specified.", - "extractionState" : "manual", - "localizations" : { - "ca" : { - "stringUnit" : { - "state" : "translated", - "value" : "desbloqueja el secret \"%1$(secretName)@\"" - } - }, - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Secret \"%1$(secretName)@\" entsperren" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "unlock secret “%1$(secretName)@\"" - } - }, - "fi" : { - "stringUnit" : { - "state" : "translated", - "value" : "avaa salaisuuden \"%1$(secretName)@\" lukitus" - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "déverrouiller le secret \"%1$(secretName)@\"" - } - }, - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "sblocca il Segreto \"%1$(secretName)@\"" - } - }, - "ja" : { - "stringUnit" : { - "state" : "translated", - "value" : "シークレット“%1$(secretName)@”のロックを解除します" - } - }, - "ko" : { - "stringUnit" : { - "state" : "translated", - "value" : "비밀 \"%1$(secretName)@\" 잠금 해제" - } - }, - "pl" : { - "stringUnit" : { - "state" : "translated", - "value" : "odblokuj sekret “%1$(secretName)@”" - } - }, - "pt-BR" : { - "stringUnit" : { - "state" : "translated", - "value" : "destravar secreto \"%1$(secretName)@\"" - } - }, - "ru" : { - "stringUnit" : { - "state" : "translated", - "value" : "разблокировать секрет \"%1$(secretName)@\"" - } - }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "解锁密钥串 \"%1$(secretName)@\"" - } - } - } - }, "auth_context_request_deny_button" : { "comment" : "When the user chooses to perform an action that requires Touch ID/password authentication, they are shown a prompt to approve the action. This is the deny button for that prompt.", "extractionState" : "manual", @@ -1196,95 +964,6 @@ } } }, - "auth_context_request_verify_description" : { - "comment" : "When the user performs a signature verification action using a secret, they are shown a prompt to approve the action. This is the description, showing which secret will be used. The placeholder is the name of the secret. NOTE: This is currently not exposed in UI.", - "extractionState" : "manual", - "localizations" : { - "ca" : { - "stringUnit" : { - "state" : "translated", - "value" : "verifica una signatura usant el secret \"%1$(secretName)@\"" - } - }, - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "eine Signatur mit dem Secret \"%1$(secretName)@\" verifizieren" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "verify a signature using secret \"%1$(secretName)@“" - } - }, - "fi" : { - "stringUnit" : { - "state" : "translated", - "value" : "varmista allekirjoitus käyttäen salaisuutta \"%1$(secretName)@\"" - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "vérifier une signature en utilisant le secret \"%1$(secretName)@\"" - } - }, - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "verifica una firma usando il segreto \"%1$(secretName)@\"" - } - }, - "ja" : { - "stringUnit" : { - "state" : "translated", - "value" : "シークレット“%1$(secretName)@”を使って署名を検証します" - } - }, - "ko" : { - "stringUnit" : { - "state" : "translated", - "value" : "비밀 \"%1$(secretName)@\"를 사용하여 서명 검증" - } - }, - "pl" : { - "stringUnit" : { - "state" : "translated", - "value" : "zweryfikuj sygnaturę za pomocą sekretu “%1$(secretName)@”" - } - }, - "pt-BR" : { - "stringUnit" : { - "state" : "translated", - "value" : "verificar a assinatura utilizando o segredo \"%1$(secretName)@\"" - } - }, - "ru" : { - "stringUnit" : { - "state" : "translated", - "value" : "верифицировать подпись используя секрет \"%1$(secretName)@\"" - } - }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "使用密钥串 \"%1$(secretName)@\" 认证" - } - } - } - }, - "copy_button" : { - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Copy" - } - } - } - }, "copyable_click_to_copy_button" : { "extractionState" : "manual", "localizations" : { @@ -1967,6 +1646,17 @@ } } }, + "create_secret_protection_level_title" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Protection Level" + } + } + } + }, "create_secret_require_authentication_biometric_current_description" : { "extractionState" : "manual", "localizations" : { @@ -2297,83 +1987,6 @@ } } }, - "delete_confirmation_confirm_name_label" : { - "extractionState" : "manual", - "localizations" : { - "ca" : { - "stringUnit" : { - "state" : "translated", - "value" : "Confirma el nom" - } - }, - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Name bestätigen" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Confirm Name" - } - }, - "fi" : { - "stringUnit" : { - "state" : "translated", - "value" : "Vahvista nimi" - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Confirmer le nom" - } - }, - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "Conferma nome" - } - }, - "ja" : { - "stringUnit" : { - "state" : "translated", - "value" : "名前の確認" - } - }, - "ko" : { - "stringUnit" : { - "state" : "translated", - "value" : "확인 이름" - } - }, - "pl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Powtórz nazwę" - } - }, - "pt-BR" : { - "stringUnit" : { - "state" : "translated", - "value" : "Confirmar Nome" - } - }, - "ru" : { - "stringUnit" : { - "state" : "translated", - "value" : "Подтвердить название" - } - }, - "zh-Hans" : { - "stringUnit" : { - "state" : "needs_review", - "value" : "确认名称" - } - } - } - }, "delete_confirmation_delete_button" : { "extractionState" : "manual", "localizations" : { @@ -4776,77 +4389,6 @@ } } }, - "setup_ssh_added_manually_button" : { - "extractionState" : "manual", - "localizations" : { - "ca" : { - "stringUnit" : { - "state" : "translated", - "value" : "L'he afegida manualment" - } - }, - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Ich habe es Manuell Eingefügt" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "I Added it Manually" - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Je l'ai ajouté manuellement" - } - }, - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "L’ho aggiunta manualmente" - } - }, - "ja" : { - "stringUnit" : { - "state" : "translated", - "value" : "手動で追加する" - } - }, - "ko" : { - "stringUnit" : { - "state" : "translated", - "value" : "내가 수동으로 추가했습니다" - } - }, - "pl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Dodałem to samodzielnie" - } - }, - "pt-BR" : { - "stringUnit" : { - "state" : "translated", - "value" : "Eu adicionei manualmente" - } - }, - "ru" : { - "stringUnit" : { - "state" : "translated", - "value" : "Добавлено мною вручную" - } - }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "我自行手动添加" - } - } - } - }, "setup_updates_description" : { "extractionState" : "manual", "localizations" : { @@ -4989,77 +4531,6 @@ } } }, - "setup_updates_readmore" : { - "extractionState" : "manual", - "localizations" : { - "ca" : { - "stringUnit" : { - "state" : "translated", - "value" : "Llegiu més ací." - } - }, - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Lies hier mehr darüber." - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Read more about this here." - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Pour en savoir plus, cliquez ici." - } - }, - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "Leggi di più a riguardo qui." - } - }, - "ja" : { - "stringUnit" : { - "state" : "translated", - "value" : "詳細はこちら" - } - }, - "ko" : { - "stringUnit" : { - "state" : "translated", - "value" : "이에 대한 자세한 내용은 여기를 참조하세요." - } - }, - "pl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Przeczytaj więcej tutaj." - } - }, - "pt-BR" : { - "stringUnit" : { - "state" : "translated", - "value" : "Leia mais sobre isto aqui." - } - }, - "ru" : { - "stringUnit" : { - "state" : "translated", - "value" : "Больше подробностей здесь." - } - }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "在此处查看详情。" - } - } - } - }, "setup_updates_title" : { "extractionState" : "manual", "localizations" : { diff --git a/Sources/Packages/Sources/SecureEnclaveSecretKit/PersistentAuthenticationHandler.swift b/Sources/Packages/Sources/SecureEnclaveSecretKit/PersistentAuthenticationHandler.swift index 79d4a30..4934c77 100644 --- a/Sources/Packages/Sources/SecureEnclaveSecretKit/PersistentAuthenticationHandler.swift +++ b/Sources/Packages/Sources/SecureEnclaveSecretKit/PersistentAuthenticationHandler.swift @@ -56,11 +56,9 @@ extension SecureEnclave { formatter.unitsStyle = .spellOut formatter.allowedUnits = [.hour, .minute, .day] - if let durationString = formatter.string(from: duration) { - newContext.localizedReason = String(localized: .authContextPersistForDuration(secretName: secret.name, duration: durationString)) - } else { - newContext.localizedReason = String(localized: .authContextPersistForDurationUnknown(secretName: secret.name)) - } + + let durationString = formatter.string(from: duration)! + newContext.localizedReason = String(localized: .authContextPersistForDuration(secretName: secret.name, duration: durationString)) let success = try await newContext.evaluatePolicy(.deviceOwnerAuthentication, localizedReason: newContext.localizedReason) guard success else { return } let context = PersistentAuthenticationContext(secret: secret, context: newContext, duration: duration) diff --git a/Sources/Secretive/Views/Configuration/SetupView.swift b/Sources/Secretive/Views/Configuration/SetupView.swift index 2c2d66e..2578c28 100644 --- a/Sources/Secretive/Views/Configuration/SetupView.swift +++ b/Sources/Secretive/Views/Configuration/SetupView.swift @@ -21,9 +21,10 @@ struct SetupView: View { StepView( title: .setupAgentTitle, description: .setupAgentDescription, + detail: .setupAgentActivityMonitorDescription, systemImage: "lock.laptopcomputer", ) { - setupButton( + SetupButton( .setupAgentInstallButton, complete: installed, width: buttonWidth @@ -40,7 +41,7 @@ struct SetupView: View { description: .setupUpdatesDescription, systemImage: "network.badge.shield.half.filled", ) { - setupButton( + SetupButton( .setupUpdatesOkButton, complete: updates, width: buttonWidth @@ -54,7 +55,7 @@ struct SetupView: View { description: .setupIntegrationsDescription, systemImage: "firewall", ) { - setupButton( + SetupButton( .setupIntegrationsButton, complete: integrations, width: buttonWidth @@ -63,7 +64,7 @@ struct SetupView: View { } } } - .onPreferenceChange(setupButton.WidthKey.self) { width in + .onPreferenceChange(SetupButton.WidthKey.self) { width in buttonWidth = width } .background(.white.opacity(0.1), in: RoundedRectangle(cornerRadius: 10)) @@ -88,7 +89,7 @@ struct SetupView: View { } } -struct setupButton: View { +struct SetupButton: View { struct WidthKey: @MainActor PreferenceKey { @MainActor static var defaultValue: CGFloat? = nil @@ -144,12 +145,20 @@ struct StepView: View { let title: LocalizedStringResource let icon: Image let description: LocalizedStringResource + let detail: LocalizedStringResource? let actions: Content - init(title: LocalizedStringResource, description: LocalizedStringResource, systemImage: String, actions: () -> Content) { + init( + title: LocalizedStringResource, + description: LocalizedStringResource, + detail: LocalizedStringResource? = nil, + systemImage: String, + actions: () -> Content + ) { self.title = title self.icon = Image(systemName: systemImage) self.description = description + self.detail = detail self.actions = actions() } @@ -165,6 +174,11 @@ struct StepView: View { Text(title) .bold() Text(description) + if let detail { + Text(detail) + .font(.callout) + .italic() + } } Spacer(minLength: 20) actions diff --git a/Sources/Secretive/Views/Secrets/CreateSecretView.swift b/Sources/Secretive/Views/Secrets/CreateSecretView.swift index 78fd4e6..192c3dc 100644 --- a/Sources/Secretive/Views/Secrets/CreateSecretView.swift +++ b/Sources/Secretive/Views/Secrets/CreateSecretView.swift @@ -28,7 +28,7 @@ struct CreateSecretView: View { Section { TextField(String(localized: .createSecretNameLabel), text: $name, prompt: Text(.createSecretNamePlaceholder)) VStack(alignment: .leading, spacing: 10) { - Picker(.createSecretRequireAuthenticationTitle, selection: $authenticationRequirement) { + Picker(.createSecretProtectionLevelTitle, selection: $authenticationRequirement) { ForEach(authenticationOptions) { option in HStack { switch option { diff --git a/Sources/Secretive/Views/Views/ContentView.swift b/Sources/Secretive/Views/Views/ContentView.swift index 54b0fe5..117d0d8 100644 --- a/Sources/Secretive/Views/Views/ContentView.swift +++ b/Sources/Secretive/Views/Views/ContentView.swift @@ -54,20 +54,12 @@ extension ContentView { ToolbarItem(id: id) { view } } } - - var needsSetup: Bool { - runningSetup || !hasRunSetup - } /// Item either showing a "everything's good, here's more info" or "something's wrong, re-run setup" message /// These two are mutually exclusive @ViewBuilder var runningOrRunSetupView: some View { - if needsSetup { - setupNoticeView - } else { - agentStatusToolbarView - } + agentStatusToolbarView } var updateNoticeContent: (LocalizedStringResource, Color)? { @@ -134,19 +126,6 @@ extension ContentView { } } - @ViewBuilder - var setupNoticeView: some View { - Button(action: { - runningSetup = true - }, label: { - if !hasRunSetup { - Text(.agentSetupNoticeTitle) - .font(.headline) - } - }) - .buttonStyle(ToolbarButtonStyle(color: .orange)) - } - @ViewBuilder var agentStatusToolbarView: some View { Button(action: {