diff --git a/Sources/Packages/Package.swift b/Sources/Packages/Package.swift index 061f7ad..39d9a99 100644 --- a/Sources/Packages/Package.swift +++ b/Sources/Packages/Package.swift @@ -6,7 +6,7 @@ import PackageDescription let package = Package( name: "SecretivePackages", platforms: [ - .macOS(.v11) + .macOS(.v12) ], products: [ .library( diff --git a/Sources/Packages/Sources/SmartCardSecretKit/SmartCardStore.swift b/Sources/Packages/Sources/SmartCardSecretKit/SmartCardStore.swift index dd52d22..6333aab 100644 --- a/Sources/Packages/Sources/SmartCardSecretKit/SmartCardStore.swift +++ b/Sources/Packages/Sources/SmartCardSecretKit/SmartCardStore.swift @@ -139,20 +139,10 @@ extension SmartCard.Store { guard let tokenID = tokenID else { return } let fallbackName = NSLocalizedString("Smart Card", comment: "Smart Card") - if #available(macOS 12.0, *) { - if let driverName = watcher.tokenInfo(forTokenID: tokenID)?.driverName { - name = driverName - } else { - name = fallbackName - } + if let driverName = watcher.tokenInfo(forTokenID: tokenID)?.driverName { + name = driverName } else { - // Hack to read name if there's only one smart card - let slotNames = TKSmartCardSlotManager().slotNames - if watcher.nonSecureEnclaveTokens.count == 1 && slotNames.count == 1 { - name = slotNames.first! - } else { - name = fallbackName - } + name = fallbackName } let attributes = KeychainDictionary([ diff --git a/Sources/SecretAgent/Localizable.xcstrings b/Sources/SecretAgent/Localizable.xcstrings new file mode 100644 index 0000000..f62bcd5 --- /dev/null +++ b/Sources/SecretAgent/Localizable.xcstrings @@ -0,0 +1,105 @@ +{ + "sourceLanguage" : "en", + "strings" : { + "persist_authentication_accept_button" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Leave Unlocked" + } + } + } + }, + "persist_authentication_decline_button" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Do Not Unlock" + } + } + } + }, + "signed_notification_description_%@" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Using secret %1$@" + } + } + } + }, + "signed_notification_title_%@" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Signed Request from %1$@" + } + } + } + }, + "update_notification_ignore_button" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ignore" + } + } + } + }, + "update_notification_update_button" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Update" + } + } + } + }, + "update_notification_update_critical_title_%@" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Critical Security Update - %1$@" + } + } + } + }, + "update_notification_update_description" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Click to Update" + } + } + } + }, + "update_notification_update_normal_title_%@" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Update Available - %1$@" + } + } + } + } + }, + "version" : "1.0" +} \ No newline at end of file diff --git a/Sources/SecretAgent/Notifier.swift b/Sources/SecretAgent/Notifier.swift index 8320784..69b29bb 100644 --- a/Sources/SecretAgent/Notifier.swift +++ b/Sources/SecretAgent/Notifier.swift @@ -10,8 +10,8 @@ class Notifier { private let notificationDelegate = NotificationDelegate() init() { - let updateAction = UNNotificationAction(identifier: Constants.updateActionIdentitifier, title: "Update", options: []) - let ignoreAction = UNNotificationAction(identifier: Constants.ignoreActionIdentitifier, title: "Ignore", options: []) + let updateAction = UNNotificationAction(identifier: Constants.updateActionIdentitifier, title: String(localized: "update_notification_update_button"), options: []) + let ignoreAction = UNNotificationAction(identifier: Constants.ignoreActionIdentitifier, title: String(localized: "update_notification_ignore_button"), options: []) let updateCategory = UNNotificationCategory(identifier: Constants.updateCategoryIdentitifier, actions: [updateAction, ignoreAction], intentIdentifiers: [], options: []) let criticalUpdateCategory = UNNotificationCategory(identifier: Constants.criticalUpdateCategoryIdentitifier, actions: [updateAction], intentIdentifiers: [], options: []) @@ -22,7 +22,7 @@ class Notifier { Measurement(value: 24, unit: UnitDuration.hours) ] - let doNotPersistAction = UNNotificationAction(identifier: Constants.doNotPersistActionIdentitifier, title: "Do Not Unlock", options: []) + let doNotPersistAction = UNNotificationAction(identifier: Constants.doNotPersistActionIdentitifier, title: String(localized: "persist_authentication_decline_button"), options: []) var allPersistenceActions = [doNotPersistAction] let formatter = DateComponentsFormatter() @@ -40,7 +40,7 @@ class Notifier { let persistAuthenticationCategory = UNNotificationCategory(identifier: Constants.persistAuthenticationCategoryIdentitifier, actions: allPersistenceActions, intentIdentifiers: [], options: []) if persistAuthenticationCategory.responds(to: Selector(("actionsMenuTitle"))) { - persistAuthenticationCategory.setValue("Leave Unlocked", forKey: "_actionsMenuTitle") + persistAuthenticationCategory.setValue(String(localized: "persist_authentication_accept_button"), forKey: "_actionsMenuTitle") } UNUserNotificationCenter.current().setNotificationCategories([updateCategory, criticalUpdateCategory, persistAuthenticationCategory]) UNUserNotificationCenter.current().delegate = notificationDelegate @@ -62,13 +62,11 @@ class Notifier { notificationDelegate.pendingPersistableStores[store.id.description] = store let notificationCenter = UNUserNotificationCenter.current() let notificationContent = UNMutableNotificationContent() - notificationContent.title = "Signed Request from \(provenance.origin.displayName)" - notificationContent.subtitle = "Using secret \"\(secret.name)\"" + notificationContent.title = String(localized: "signed_notification_title_\(provenance.origin.displayName)") + notificationContent.subtitle = String(localized: "signed_notification_description_\(secret.name)") notificationContent.userInfo[Constants.persistSecretIDKey] = secret.id.description notificationContent.userInfo[Constants.persistStoreIDKey] = store.id.description - if #available(macOS 12.0, *) { - notificationContent.interruptionLevel = .timeSensitive - } + notificationContent.interruptionLevel = .timeSensitive if secret.requiresAuthentication && store.existingPersistedAuthenticationContext(secret: secret) == nil { notificationContent.categoryIdentifier = Constants.persistAuthenticationCategoryIdentitifier } @@ -85,14 +83,12 @@ class Notifier { let notificationCenter = UNUserNotificationCenter.current() let notificationContent = UNMutableNotificationContent() if update.critical { - if #available(macOS 12.0, *) { - notificationContent.interruptionLevel = .critical - } - notificationContent.title = "Critical Security Update - \(update.name)" + notificationContent.interruptionLevel = .critical + notificationContent.title = String(localized: "update_notification_update_critical_title_\(update.name)") } else { - notificationContent.title = "Update Available - \(update.name)" + notificationContent.title = String(localized: "update_notification_update_normal_title_\(update.name)") } - notificationContent.subtitle = "Click to Update" + notificationContent.subtitle = String(localized: "update_notification_update_description") notificationContent.body = update.body notificationContent.categoryIdentifier = update.critical ? Constants.criticalUpdateCategoryIdentitifier : Constants.updateCategoryIdentitifier let request = UNNotificationRequest(identifier: UUID().uuidString, content: notificationContent, trigger: nil) diff --git a/Sources/Secretive.xcodeproj/project.pbxproj b/Sources/Secretive.xcodeproj/project.pbxproj index d66c1dd..b879f21 100644 --- a/Sources/Secretive.xcodeproj/project.pbxproj +++ b/Sources/Secretive.xcodeproj/project.pbxproj @@ -19,6 +19,7 @@ 5003EF632780081B00DF2006 /* SecureEnclaveSecretKit in Frameworks */ = {isa = PBXBuildFile; productRef = 5003EF622780081B00DF2006 /* SecureEnclaveSecretKit */; }; 5003EF652780081B00DF2006 /* SmartCardSecretKit in Frameworks */ = {isa = PBXBuildFile; productRef = 5003EF642780081B00DF2006 /* SmartCardSecretKit */; }; 500B93C32B478D8400E157DE /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 500B93C22B478D8400E157DE /* Localizable.xcstrings */; }; + 500B93C72B479E2E00E157DE /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 500B93C62B479E2E00E157DE /* Localizable.xcstrings */; }; 501421622781262300BBAA70 /* Brief in Frameworks */ = {isa = PBXBuildFile; productRef = 501421612781262300BBAA70 /* Brief */; }; 501421652781268000BBAA70 /* SecretAgent.app in CopyFiles */ = {isa = PBXBuildFile; fileRef = 50A3B78A24026B7500D209EA /* SecretAgent.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 50153E20250AFCB200525160 /* UpdateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50153E1F250AFCB200525160 /* UpdateView.swift */; }; @@ -110,6 +111,7 @@ 50033AC227813F1700253856 /* BundleIDs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BundleIDs.swift; sourceTree = ""; }; 5003EF39278005C800DF2006 /* Packages */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = Packages; sourceTree = ""; }; 500B93C22B478D8400E157DE /* Localizable.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = Localizable.xcstrings; sourceTree = ""; }; + 500B93C62B479E2E00E157DE /* Localizable.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = Localizable.xcstrings; sourceTree = ""; }; 50153E1F250AFCB200525160 /* UpdateView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateView.swift; sourceTree = ""; }; 50153E21250DECA300525160 /* SecretListItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecretListItemView.swift; sourceTree = ""; }; 5018F54E24064786002EB505 /* Notifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Notifier.swift; sourceTree = ""; }; @@ -314,6 +316,7 @@ 50A3B79824026B7600D209EA /* Info.plist */, 508BF29425B4F140009EFB7E /* InternetAccessPolicy.plist */, 50A3B79924026B7600D209EA /* SecretAgent.entitlements */, + 500B93C62B479E2E00E157DE /* Localizable.xcstrings */, 50A3B79224026B7600D209EA /* Preview Content */, ); path = SecretAgent; @@ -466,6 +469,7 @@ buildActionMask = 2147483647; files = ( 50A3B79724026B7600D209EA /* Main.storyboard in Resources */, + 500B93C72B479E2E00E157DE /* Localizable.xcstrings in Resources */, 50A3B79424026B7600D209EA /* Preview Assets.xcassets in Resources */, 50A3B79124026B7600D209EA /* Assets.xcassets in Resources */, 508BF2AA25B4F1CB009EFB7E /* InternetAccessPolicy.plist in Resources */, @@ -693,6 +697,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); + MACOSX_DEPLOYMENT_TARGET = 12.0; MARKETING_VERSION = 1; PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.Host; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -721,6 +726,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); + MACOSX_DEPLOYMENT_TARGET = 12.0; MARKETING_VERSION = 1; PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.Host; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -854,6 +860,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); + MACOSX_DEPLOYMENT_TARGET = 12.0; MARKETING_VERSION = 1; PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.Host; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -898,6 +905,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); + MACOSX_DEPLOYMENT_TARGET = 12.0; MARKETING_VERSION = 1; PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.SecretAgent; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -922,6 +930,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); + MACOSX_DEPLOYMENT_TARGET = 12.0; MARKETING_VERSION = 1; PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.SecretAgent; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -947,6 +956,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); + MACOSX_DEPLOYMENT_TARGET = 12.0; MARKETING_VERSION = 1; PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.SecretAgent; PRODUCT_NAME = "$(TARGET_NAME)"; diff --git a/Sources/Secretive/Localizable.xcstrings b/Sources/Secretive/Localizable.xcstrings index e98301a..71f5686 100644 --- a/Sources/Secretive/Localizable.xcstrings +++ b/Sources/Secretive/Localizable.xcstrings @@ -1,9 +1,6 @@ { "sourceLanguage" : "en", "strings" : { - "" : { - - }, "\n" : { }, @@ -130,26 +127,6 @@ } } }, - "create_secret_legacy_notify_description" : { - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Authentication not required when Mac is unlocked" - } - } - } - }, - "create_secret_legacy_require_authentication_description" : { - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Requires Authentication (Biometrics or Password) before each use" - } - } - } - }, "create_secret_name_label" : { "localizations" : { "en" : { @@ -614,6 +591,16 @@ } } }, + "update_critical_notice_title" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Critical Security Update Required" + } + } + } + }, "update_ignore_button" : { "localizations" : { "en" : { @@ -624,6 +611,16 @@ } } }, + "update_normal_notice_title" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Update Available" + } + } + } + }, "update_release_notes_title" : { "localizations" : { "en" : { @@ -634,6 +631,16 @@ } } }, + "update_test_notice_title" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Test Build" + } + } + } + }, "update_update_button" : { "localizations" : { "en" : { diff --git a/Sources/Secretive/Views/ContentView.swift b/Sources/Secretive/Views/ContentView.swift index 948ff26..c48991c 100644 --- a/Sources/Secretive/Views/ContentView.swift +++ b/Sources/Secretive/Views/ContentView.swift @@ -64,15 +64,15 @@ extension ContentView { } } - var updateNoticeContent: (String, Color)? { + var updateNoticeContent: (LocalizedStringKey, Color)? { guard let update = updater.update else { return nil } if update.critical { - return ("Critical Security Update Required", .red) + return ("update_critical_notice_title", .red) } else { if updater.testBuild { - return ("Test Build", .blue) + return ("update_test_notice_title", .blue) } else { - return ("Update Available", .orange) + return ("update_normal_notice_title", .orange) } } } diff --git a/Sources/Secretive/Views/CreateSecretView.swift b/Sources/Secretive/Views/CreateSecretView.swift index 15de1c6..accd8be 100644 --- a/Sources/Secretive/Views/CreateSecretView.swift +++ b/Sources/Secretive/Views/CreateSecretView.swift @@ -23,25 +23,12 @@ struct CreateSecretView: View { TextField("create_secret_name_placeholder", text: $name) .focusable() } - if #available(macOS 12.0, *) { - ThumbnailPickerView(items: [ - ThumbnailPickerView.Item(value: true, name: "create_secret_require_authentication_title", description: "create_secret_require_authentication_description", thumbnail: AuthenticationView()), - ThumbnailPickerView.Item(value: false, name: "create_secret_notify_title", - description: "create_secret_notify_description", - thumbnail: NotificationView()) - ], selection: $requiresAuthentication) - } else { - HStack { - VStack(spacing: 20) { - Picker("", selection: $requiresAuthentication) { - Text("create_secret_legacy_require_authentication_description").tag(true) - Text("create_secret_legacy_notify_description").tag(false) - } - .pickerStyle(RadioGroupPickerStyle()) - Spacer(minLength: 10) - } - } - } + ThumbnailPickerView(items: [ + ThumbnailPickerView.Item(value: true, name: "create_secret_require_authentication_title", description: "create_secret_require_authentication_description", thumbnail: AuthenticationView()), + ThumbnailPickerView.Item(value: false, name: "create_secret_notify_title", + description: "create_secret_notify_description", + thumbnail: NotificationView()) + ], selection: $requiresAuthentication) } } HStack { @@ -138,7 +125,6 @@ extension ThumbnailPickerView { } -@available(macOS 12.0, *) struct SystemBackgroundView: View { let anchor: UnitPoint @@ -157,7 +143,6 @@ struct SystemBackgroundView: View { } } -@available(macOS 12.0, *) struct AuthenticationView: View { var body: some View { @@ -203,7 +188,6 @@ struct AuthenticationView: View { } -@available(macOS 12.0, *) struct NotificationView: View { var body: some View { @@ -253,14 +237,10 @@ struct CreateSecretView_Previews: PreviewProvider { static var previews: some View { Group { CreateSecretView(store: Preview.StoreModifiable(), showing: .constant(true)) - if #available(macOS 12.0, *) { AuthenticationView().environment(\.colorScheme, .dark) AuthenticationView().environment(\.colorScheme, .light) NotificationView().environment(\.colorScheme, .dark) NotificationView().environment(\.colorScheme, .light) - } else { - // Fallback on earlier versions - } } } }