Skip to content

Commit

Permalink
refactor: improve Defaults.Key (#599)
Browse files Browse the repository at this point in the history
* refactor: replace const key with enum StoredKey

* perf: remove unused code

* refactor: improve Defaults.Key

* refactor: improve TextEditorCell, use ServiceConfigurationPickerCell to show model

* perf: disable user to edit built-in supported models

* refactor: improve StreamConfigurationView, remove viewModel

* fix: setup subscribers when init, post update notification if model changes

* perf: set defaultModels for OpenAI, Gemini and Built-in service

* refactor: rename enum OpenAIModel and GeminiModel, update gpt3_5_turbo
to gpt_3_5_turbo

* fix: due to service memory leaks, multiple notifications are posted

* fix: if main window dealloc, we need to setup subscribers again

* fix: improve Gemini error message for empty model

* fix: replace validation viewModel @StateObject with @ObservedObject

* refactor: improve Gemini translate()

* fix: Gemini and Built-in service cannot validate

* fix: show different api key placeholders

* fix: remove unused ObservableObject

* style: format code

* chore: update SwiftFormat to 0.54

* style: replace override public with public override

* fix: validModels is empty even if defaultModels is set
  • Loading branch information
tisfeng authored Jul 8, 2024
1 parent 273cb16 commit 6d4cf39
Show file tree
Hide file tree
Showing 52 changed files with 888 additions and 1,314 deletions.
7 changes: 5 additions & 2 deletions .swiftformat
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ strongifiedSelf
--indent 4
--maxwidth 120
--typeattributes prev-line
--varattributes same-line
--storedvarattrs same-line
--voidtype tuple
--wraparguments before-first
--wrapparameters before-first
Expand All @@ -105,4 +105,7 @@ strongifiedSelf

# Following is by Lava

--ifdef no-indent
--ifdef no-indent

# Why doesn't this work?
--modifierorder public,override,
38 changes: 13 additions & 25 deletions Easydict.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

34 changes: 14 additions & 20 deletions Easydict/App/Localizable.xcstrings
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,16 @@
}
}
},
"%@ API Key" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "%@ API Key"
}
}
}
},
"about" : {
"comment" : "about",
"localizations" : {
Expand Down Expand Up @@ -2405,7 +2415,7 @@
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "完整接口地址"
"value" : "API 请求地址"
}
}
}
Expand Down Expand Up @@ -2442,22 +2452,6 @@
}
}
},
"service.configuration.gemini.api_key.placeholder" : {
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "xxxxxxxxxxxxx"
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "xxxxxxxxxxxxx"
}
}
}
},
"service.configuration.input.placeholder" : {
"localizations" : {
"en" : {
Expand Down Expand Up @@ -2543,13 +2537,13 @@
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "https://api.openai.com/v1/chat/completions"
"value" : "The full request URL, for example https://api.openai.com/v1/chat/completions"
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "https://api.openai.com/v1/chat/completions"
"value" : "完整请求 URL,例如 https://api.openai.com/v1/chat/completions"
}
}
}
Expand All @@ -2565,7 +2559,7 @@
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "完整接口地址"
"value" : "API 请求地址"
}
}
}
Expand Down
149 changes: 20 additions & 129 deletions Easydict/Swift/Feature/Configuration/Configuration+Defaults.swift
Original file line number Diff line number Diff line change
Expand Up @@ -205,153 +205,44 @@ class ShortcutWrapper<T: KeyCombo> {
}
}

// Service Configuration
extension Defaults.Keys {
// OpenAI
static let openAIAPIKey = Key<String?>(apiStoredKey(.openAI)) // EZOpenAIAPIKey
static let openAITranslation = Key<String>(
translationStoredKey(.openAI),
default: "1"
)
static let openAIDictionary = Key<String>(
dictionaryStoredKey(.openAI),
default: "1"
)
static let openAISentence = Key<String>(
sentenceStoredKey(.openAI),
default: "1"
)
static let openAIServiceUsageStatus = Key<ServiceUsageStatus>(
serviceUsageStatusStoredKey(.openAI),
default: .default
)
static let openAIEndPoint = Key<String?>(endpointStoredKey(.openAI))
static let openAIModel = Key<String>(
modelStoredKey(.openAI),
default: OpenAIModel.gpt3_5_turbo.rawValue
)
static let openAIAvailableModels = Key<String?>(
availableModelsStoredKey(.openAI),
default: OpenAIModel.allCases.map { $0.rawValue }.joined(separator: ",")
)
static let openAIVaildModels = Key<Array>(
validModelsStoredKey(.openAI),
default: OpenAIModel.allCases.map { $0.rawValue }
)

// Custom OpenAI
static let customOpenAINameKey = Key<String?>(
nameStoredKey(.customOpenAI),
default: NSLocalizedString("custom_openai", comment: "")
)
static let customOpenAIAPIKey = Key<String?>(apiStoredKey(.customOpenAI))
static let customOpenAITranslation = Key<String>(
translationStoredKey(.customOpenAI),
default: "1"
)
static let customOpenAIDictionary = Key<String>(
dictionaryStoredKey(.customOpenAI),
default: "0"
)
static let customOpenAISentence = Key<String>(
sentenceStoredKey(.customOpenAI),
default: "0"
)
static let customOpenAIServiceUsageStatus = Key<ServiceUsageStatus>(
serviceUsageStatusStoredKey(.builtInAI),
default: .default
)
static let customOpenAIEndPoint = Key<String?>(endpointStoredKey(.customOpenAI))
static let customOpenAIModel = Key<String>(
modelStoredKey(.customOpenAI),
default: ""
)
static let customOpenAIAvailableModels = Key<String?>(
availableModelsStoredKey(.customOpenAI),
default: ""
)
static let customOpenAIVaildModels = Key<Array>(
validModelsStoredKey(.customOpenAI),
default: [""]
)

// Built-in AI
static let builtInAIModel = Key<String>(
modelStoredKey(.builtInAI),
default: ""
) // EZBuiltInAIModelKey
static let builtInAITranslation = Key<String>(
translationStoredKey(.builtInAI),
default: "1"
)
static let builtInAIDictionary = Key<String>(
dictionaryStoredKey(.builtInAI),
default: "0"
)
static let builtInAISentence = Key<String>(
sentenceStoredKey(.builtInAI),
default: "0"
)
static let builtInAIServiceUsageStatus = Key<ServiceUsageStatus>(
serviceUsageStatusStoredKey(.builtInAI),
default: .default
)
func defaultsKey<T>(_ key: StoredKey, serviceType: ServiceType) -> Defaults.Key<T?> {
defaultsKey(key, serviceType: serviceType, defaultValue: nil)
}

// Gemni
static let geminiAPIKey = Key<String?>(apiStoredKey(.gemini)) // EZGeminiAPIKey
static let geminiTranslation = Key<String>(
translationStoredKey(.gemini),
default: "1"
)
static let geminiDictionary = Key<String>(
dictionaryStoredKey(.gemini),
default: "1"
)
static let geminiSentence = Key<String>(
sentenceStoredKey(.gemini),
default: "1"
)
static let geminiServiceUsageStatus = Key<ServiceUsageStatus>(
serviceUsageStatusStoredKey(.gemini),
default: .default
)
static let geminiModel = Key<String>(
modelStoredKey(.gemini),
default: GeminiModel.gemini1_5_flash.rawValue
)
static let geminiAvailableModels = Key<String?>(
availableModelsStoredKey(.gemini),
default: GeminiModel.allCases.map { $0.rawValue }.joined(separator: ",")
)
static let geminiValidModels = Key<Array>(
validModelsStoredKey(.gemini),
default: GeminiModel.allCases.map { $0.rawValue }
func defaultsKey<T: _DefaultsSerializable>(_ key: StoredKey, serviceType: ServiceType, defaultValue: T) -> Defaults
.Key<T> {
Defaults.Key<T>(
storedKey(key, serviceType: serviceType),
default: defaultValue
)
}

// Service Configuration
extension Defaults.Keys {
// DeepL
static let deepLAuth = Key<String?>(EZDeepLAuthKey)
static let deepLAuth = Key<String>(EZDeepLAuthKey, default: "")
static let deepLTranslation = Key<DeepLAPIUsagePriority>(
EZDeepLTranslationAPIKey,
default: DeepLAPIUsagePriority.webFirst
)
static let deepLTranslateEndPointKey = Key<String?>(EZDeepLTranslateEndPointKey)
static let deepLTranslateEndPointKey = Key<String>(EZDeepLTranslateEndPointKey, default: "")

// Bing
static let bingCookieKey = Key<String?>(EZBingCookieKey)
static let bingCookieKey = Key<String>(EZBingCookieKey, default: "")

// niu
static let niuTransAPIKey = Key<String?>(EZNiuTransAPIKey)
static let niuTransAPIKey = Key<String>(EZNiuTransAPIKey, default: "")

// Caiyun
static let caiyunToken = Key<String?>(EZCaiyunToken)
static let caiyunToken = Key<String>(EZCaiyunToken, default: "")

// tencent
static let tencentSecretId = Key<String?>(EZTencentSecretId)
static let tencentSecretKey = Key<String?>(EZTencentSecretKey)
static let tencentSecretId = Key<String>(EZTencentSecretId, default: "")
static let tencentSecretKey = Key<String>(EZTencentSecretKey, default: "")

// Ali
static let aliAccessKeyId = Key<String?>(EZAliAccessKeyId)
static let aliAccessKeySecret = Key<String?>(EZAliAccessKeySecret)
static let aliAccessKeyId = Key<String>(EZAliAccessKeyId, default: "")
static let aliAccessKeySecret = Key<String>(EZAliAccessKeySecret, default: "")
}

/// shortcut
Expand Down
70 changes: 25 additions & 45 deletions Easydict/Swift/Feature/Configuration/DefaultsStoredKey.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,64 +8,44 @@

import Foundation

// TODO: refactor key with enum key type.
func storedKey(_ key: String, serviceType: ServiceType) -> String {
func storedKey(_ key: StoredKey, serviceType: ServiceType) -> String {
// This key should be compatible with existing OpenAI config keys
// EZOpenAIServiceUsageStatusKey
// EZOpenAIDictionaryKey
"EZ" + serviceType.rawValue + key + "Key"
}

func serviceUsageStatusStoredKey(_ serviceType: ServiceType) -> String {
storedKey(EZServiceUsageStatusKey, serviceType: serviceType)
}

func translationStoredKey(_ serviceType: ServiceType) -> String {
storedKey(EZTranslationKey, serviceType: serviceType)
}

func sentenceStoredKey(_ serviceType: ServiceType) -> String {
storedKey(EZSentenceKey, serviceType: serviceType)
}

func dictionaryStoredKey(_ serviceType: ServiceType) -> String {
storedKey(EZDictionaryKey, serviceType: serviceType)
}

func availableModelsStoredKey(_ serviceType: ServiceType) -> String {
storedKey(EZAvailableModelsKey, serviceType: serviceType)
}

func validModelsStoredKey(_ serviceType: ServiceType) -> String {
storedKey(EZValidModelsKey, serviceType: serviceType)
}

func modelStoredKey(_ serviceType: ServiceType) -> String {
storedKey(EZModelKey, serviceType: serviceType)
}

func apiStoredKey(_ serviceType: ServiceType) -> String {
storedKey(EZAPIKey, serviceType: serviceType)
}

func endpointStoredKey(_ serviceType: ServiceType) -> String {
storedKey(EZEndpointKey, serviceType: serviceType)
}

func nameStoredKey(_ serviceType: ServiceType) -> String {
storedKey(EZNameKey, serviceType: serviceType)
"EZ" + serviceType.rawValue + key.rawValue.capitalizeFirstLetter() + "Key"
}

extension UserDefaults {
static func bool(forKey key: String, serviceType: ServiceType) -> Bool {
static func bool(forKey key: StoredKey, serviceType: ServiceType) -> Bool {
let key = storedKey(key, serviceType: serviceType)
let value = standard.bool(forKey: key)
return value
}

static func string(forKey key: String, serviceType: ServiceType) -> String? {
static func string(forKey key: StoredKey, serviceType: ServiceType) -> String? {
let key = storedKey(key, serviceType: serviceType)
let value = standard.string(forKey: key)
return value
}
}

// MARK: - StoredKey

enum StoredKey: String {
case serviceUsageStatus
case translation
case dictionary
case sentence
case supportedModels = "AvailableModels" // save in String: "gpt-3.5, gpt-4"
case validModels // save in [String]
case model
case apiKey = "API"
case endpoint = "EndPoint"
case name
}

extension String {
func capitalizeFirstLetter() -> String {
prefix(1).uppercased() + dropFirst()
}
}
10 changes: 9 additions & 1 deletion Easydict/Swift/Model/ServiceUsageStatus.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,14 @@ extension ServiceUsageStatus: EnumLocalizedStringConvertible {
}
}

// MARK: Defaults.Serializable
// MARK: - String + EnumLocalizedStringConvertible

extension String: EnumLocalizedStringConvertible {
var title: LocalizedStringKey {
LocalizedStringKey(self)
}
}

// MARK: - ServiceUsageStatus + Defaults.Serializable

extension ServiceUsageStatus: Defaults.Serializable {}
Loading

0 comments on commit 6d4cf39

Please sign in to comment.