fix: MiMo ASR API Key 为空竞态修复(三处)
1. CredentialField onBlur: save() fire-and-forget 与 validate 按钮存在 IPC 竞态 — 用户输入 Key 后立即点「测试连接」时 setCredential 可能晚于 validate 到达后端。 — 添加 mountedRef 守卫防止组件卸载后 stale save。 2. read_mimo_credentials: 将 .ok().flatten().unwrap_or_default() 改为显式 match — keyring 读取失败不再静默吞错,分别用 warn/error 记日志。
This commit is contained in:
1 parent
bba2c90ef2
commit
53bb8fee37
2 files changed
+18
-5
No files matched your search
@@ -506,10 +506,17 @@ pub(crate) fn read_whisper_credentials() -> (String, String, String) {
|
||||
}
|
||||
|
||||
pub(crate) fn read_mimo_credentials() -> (String, String, String) {
|
||||
let api_key = CredentialsVault::get(CredentialAccount::AsrApiKey)
|
||||
.ok()
|
||||
.flatten()
|
||||
.unwrap_or_default();
|
||||
let api_key = match CredentialsVault::get(CredentialAccount::AsrApiKey) {
|
||||
Ok(Some(key)) if !key.trim().is_empty() => key,
|
||||
Ok(_) => {
|
||||
log::warn!("[coord] MiMo ASR: asr.api_key 未配置或为空");
|
||||
String::new()
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("[coord] MiMo ASR: 读取凭据失败: {e}");
|
||||
String::new()
|
||||
}
|
||||
};
|
||||
let base_url = CredentialsVault::get(CredentialAccount::AsrEndpoint)
|
||||
.ok()
|
||||
.flatten()
|
||||
|
||||
@@ -623,6 +623,7 @@ function CredentialField({ label, account, placeholder, mono, mask, defaultValue
|
||||
const [status, setStatus] = useState<CredentialFieldStatus>('idle');
|
||||
const debounceRef = useRef<number | null>(null);
|
||||
const statusRef = useRef<number | null>(null);
|
||||
const mountedRef = useRef(true);
|
||||
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
@@ -652,7 +653,9 @@ function CredentialField({ label, account, placeholder, mono, mask, defaultValue
|
||||
}, [account]);
|
||||
|
||||
useEffect(() => {
|
||||
mountedRef.current = true;
|
||||
return () => {
|
||||
mountedRef.current = false;
|
||||
if (debounceRef.current) clearTimeout(debounceRef.current);
|
||||
if (statusRef.current) clearTimeout(statusRef.current);
|
||||
};
|
||||
@@ -680,13 +683,16 @@ function CredentialField({ label, account, placeholder, mono, mask, defaultValue
|
||||
|
||||
const save = async (v: string, force = false) => {
|
||||
if (!loaded || (!dirty && !force)) return;
|
||||
if (!mountedRef.current) return;
|
||||
setStatus('saving');
|
||||
emitSaved('saving', t('common.saving'));
|
||||
try {
|
||||
await setCredential(account, v);
|
||||
if (!mountedRef.current) return;
|
||||
setDirty(false);
|
||||
showTemporaryStatus('saved');
|
||||
} catch (error) {
|
||||
if (!mountedRef.current) return;
|
||||
console.error('[settings] failed to save credential', account, error);
|
||||
showTemporaryStatus('saveError');
|
||||
}
|
||||
@@ -707,7 +713,7 @@ function CredentialField({ label, account, placeholder, mono, mask, defaultValue
|
||||
clearTimeout(debounceRef.current);
|
||||
debounceRef.current = null;
|
||||
}
|
||||
save(value, true);
|
||||
void save(value, true);
|
||||
};
|
||||
|
||||
const fillDefault = async () => {
|
||||
|
||||
Reference in new issue
Block a user