重构(应用状态存储): 调整savePaths返回结构化结果并更新所有调用处

- 新增SaveResult类型统一标准化保存操作的结果状态
- 修改savePaths函数返回结构化结果而非布尔值,完善长路径超限、部分失败等场景的处理逻辑,部分失败时重新加载路径避免状态偏移
- 更新useAppActions与ProfileDialog的保存逻辑,适配新API并添加长路径确认弹窗
- 补充相关测试用例,修正导入导出测试的版本号预期
This commit is contained in:
2026-05-31 14:48:03 +08:00
parent 78439a6ac6
commit a9b36a6f47
6 changed files with 73 additions and 24 deletions
+13 -2
View File
@@ -73,8 +73,19 @@ export function ProfileDialog({ open, onClose }: Props) {
system: selectedData.sys.filter(e => !e.enabled).map(e => e.path),
user: selectedData.user.filter(e => !e.enabled).map(e => e.path),
});
await useAppStore.getState().savePaths();
onClose();
const result = await useAppStore.getState().savePaths();
if (result.kind === 'success') {
onClose();
} else if (result.kind === 'warning') {
const { ask } = await import('@tauri-apps/plugin-dialog');
const confirmed = await ask(t('status.saveWarningLongPaths'), { title: t('dialog.backupTitle'), kind: 'warning' });
if (confirmed) {
const forceResult = await useAppStore.getState().savePaths(true);
if (forceResult.kind === 'success') {
onClose();
}
}
}
};
const handleDelete = async (name: string) => {
+2 -2
View File
@@ -118,8 +118,8 @@ export function useAppActions(activeTab: TabId, dialogs: DialogState) {
}, []);
const handleSave = useCallback(async () => {
const saved = await useAppStore.getState().savePaths();
if (!saved && !useAppStore.getState().isSaving) {
const result = await useAppStore.getState().savePaths();
if (result.kind === 'warning') {
// 长度超限,需要用户确认
const { ask } = await import('@tauri-apps/plugin-dialog');
const confirmed = await ask(i18n.t('status.saveWarningLongPaths'), { title: i18n.t('dialog.backupTitle'), kind: 'warning' });
+24 -7
View File
@@ -8,6 +8,13 @@ import appConfig from '@/config/default.json';
export type TabId = 'system' | 'user' | 'merged';
export type SaveResult =
| { kind: 'success' }
| { kind: 'warning'; reason: 'lengthExceeded' }
| { kind: 'failure'; message: string }
| { kind: 'partial'; message: string }
| { kind: 'blocked' };
interface AppState {
sysPaths: PathEntry[];
userPaths: PathEntry[];
@@ -45,7 +52,7 @@ interface AppState {
redo: () => void;
loadPaths: () => Promise<void>;
savePaths: (force?: boolean) => Promise<boolean>;
savePaths: (force?: boolean) => Promise<SaveResult>;
initialize: () => Promise<void>;
}
@@ -324,7 +331,7 @@ export const useAppStore = create<AppState>((set, get) => {
savePaths: async (force?: boolean) => {
const state = get();
if (state.isSaving) return false;
if (state.isSaving) return { kind: 'blocked' };
set({ isSaving: true, statusMessage: i18n.t('status.saving') });
// 只保存 enabled 的路径到注册表
@@ -337,7 +344,7 @@ export const useAppStore = create<AppState>((set, get) => {
const { maxSystemLength, maxUserLength, maxCombinedLength } = appConfig.path;
if (!force && (sysJoined.length > maxSystemLength || userJoined.length > maxUserLength || (sysJoined + userJoined).length > maxCombinedLength)) {
set({ isSaving: false, statusMessage: i18n.t('status.saveWarningLongPaths') });
return false;
return { kind: 'warning', reason: 'lengthExceeded' };
}
// 备份当前注册表(保存前备份旧值,失败仅警告不中断)
@@ -359,14 +366,24 @@ export const useAppStore = create<AppState>((set, get) => {
set({ isModified: false, isSaving: false,
statusMessage: backupFailed ? i18n.t('status.saved_without_backup') : i18n.t('status.saved'),
_savedSys: savedSys, _savedUser: savedUser });
return true;
return { kind: 'success' };
} else {
const sysErr = (!sysOk && sysResult.status === 'rejected') ? String(sysResult.reason) : '';
const usrErr = (!userOk && userResult.status === 'rejected') ? String(userResult.reason) : '';
const parts = [sysErr, usrErr].filter(Boolean);
const msg = sysOk ? '用户 PATH 保存失败' : userOk ? '系统 PATH 保存失败' : `保存失败: ${parts.join('; ')}`;
set({ isSaving: false, statusMessage: msg });
return false;
const msg = sysOk ? `用户 PATH 保存失败: ${usrErr}` : userOk ? `系统 PATH 保存失败: ${sysErr}` : `保存失败: ${parts.join('; ')}`;
if (sysOk || userOk) {
// partial success
set({ isSaving: false });
await get().loadPaths(); // reload to avoid state drift
set({ statusMessage: msg }); // restore the error message overwritten by loadPaths
return { kind: 'partial', message: msg };
} else {
set({ isSaving: false, statusMessage: msg });
return { kind: 'failure', message: msg };
}
}
},