diff --git a/src-tauri/src/cmd/profile.rs b/src-tauri/src/cmd/profile.rs index 3836cfdc..880fa978 100644 --- a/src-tauri/src/cmd/profile.rs +++ b/src-tauri/src/cmd/profile.rs @@ -164,7 +164,16 @@ pub async fn import_profile(url: String, option: Option) -> CmdResult url ); let item = wrap_err!(PrfItem::from_url(&url, None, None, option).await)?; - wrap_err!(Config::profiles().data().append_item(item)) + let new_uid = item.uid.clone().unwrap_or_default(); + wrap_err!(Config::profiles().data().append_item(item))?; + if !new_uid.is_empty() { + let _ = patch_profiles_config(IProfiles { + current: Some(new_uid), + items: None, + }) + .await?; + } + Ok(()) } } @@ -178,7 +187,17 @@ pub async fn reorder_profile(active_id: String, over_id: String) -> CmdResult { #[tauri::command] pub async fn create_profile(item: PrfItem, file_data: Option) -> CmdResult { let item = wrap_err!(PrfItem::from(item, file_data).await)?; - wrap_err!(Config::profiles().data().append_item(item)) + let new_uid = item.uid.clone().unwrap_or_default(); + wrap_err!(Config::profiles().data().append_item(item))?; + + if !new_uid.is_empty() { + let _ = patch_profiles_config(IProfiles { + current: Some(new_uid), + items: None, + }) + .await?; + } + Ok(()) } /// 更新配置文件 diff --git a/src-tauri/src/config/prfitem.rs b/src-tauri/src/config/prfitem.rs index 0a47d911..6ba6cc86 100644 --- a/src-tauri/src/config/prfitem.rs +++ b/src-tauri/src/config/prfitem.rs @@ -504,13 +504,23 @@ impl PrfItem { selected: None, extra, option: Some(PrfOption { + user_agent: user_agent.clone(), + with_proxy: if with_proxy { Some(true) } else { None }, + self_proxy: if self_proxy { Some(true) } else { None }, update_interval, update_always, + timeout_seconds: Some(timeout), + danger_accept_invalid_certs: if accept_invalid_certs { + Some(true) + } else { + None + }, merge, script, rules, proxies, groups, + use_hwid: Some(use_hwid), ..PrfOption::default() }), home, diff --git a/src-tauri/src/config/profiles.rs b/src-tauri/src/config/profiles.rs index 348ab98c..f10fea45 100644 --- a/src-tauri/src/config/profiles.rs +++ b/src-tauri/src/config/profiles.rs @@ -136,10 +136,9 @@ impl IProfiles { .with_context(|| format!("failed to write to file \"{file}\""))?; } - if self.current.is_none() - && (item.itype == Some("remote".to_string()) || item.itype == Some("local".to_string())) - { - self.current = uid; + if item.itype == Some("remote".to_string()) || item.itype == Some("local".to_string()) { + // Always switch current to the newly created remote/local profile + self.current = uid.clone(); } if self.items.is_none() { diff --git a/src/components/profile/profile-viewer.tsx b/src/components/profile/profile-viewer.tsx index 3c4ed007..d5353fd2 100644 --- a/src/components/profile/profile-viewer.tsx +++ b/src/components/profile/profile-viewer.tsx @@ -14,6 +14,7 @@ import { importProfile, enhanceProfiles, createProfileFromShareLink, + getProfiles, } from "@/services/cmds"; import { useProfiles } from "@/hooks/use-profiles"; import { showNotice } from "@/services/noticeService"; @@ -65,7 +66,7 @@ export const ProfileViewer = forwardRef( const { t } = useTranslation(); const [open, setOpen] = useState(false); const [openType, setOpenType] = useState<"new" | "edit">("new"); - const { profiles } = useProfiles(); + const { profiles, patchProfiles } = useProfiles(); const fileDataRef = useRef(null); const [showAdvanced, setShowAdvanced] = useState(false); @@ -210,33 +211,81 @@ export const ProfileViewer = forwardRef( const handleSaveAdvanced = useLockFn( handleSubmit(async (formData) => { - const form = { ...formData, url: formData.url || importUrl }; + const form = { ...formData, url: formData.url || importUrl } as Partial; setLoading(true); try { if (!form.type) throw new Error("`Type` should not be null"); if (form.type === "remote" && !form.url) throw new Error("The URL should not be null"); - if (form.option?.update_interval) - form.option.update_interval = +form.option.update_interval; - else delete form.option?.update_interval; - if (form.option?.user_agent === "") delete form.option.user_agent; - const name = form.name || `${form.type} file`; - const item = { ...form, name }; + // Clean option fields: only send what user actually set + let option = form.option ? { ...form.option } : undefined; + if (option) { + if ((option as any).update_interval != null && (option as any).update_interval !== "") { + // ensure number + (option as any).update_interval = +((option as any).update_interval as any); + } else { + delete (option as any).update_interval; + } + if (typeof option.user_agent === "string" && option.user_agent.trim() === "") { + delete (option as any).user_agent; + } + } + + const providedName = (form as any).name && String((form as any).name).trim(); + const providedDesc = (form as any).desc && String((form as any).desc).trim(); + + const item: Partial = { + ...form, + // Only include name/desc when user explicitly entered them + name: providedName ? (providedName as string) : undefined, + desc: providedDesc ? (providedDesc as string) : undefined, + option, + }; + const isUpdate = openType === "edit"; - const isActivating = - isUpdate && form.uid === (profiles?.current ?? ""); + const wasCurrent = isUpdate && form.uid === (profiles?.current ?? ""); if (openType === "new") { + // Detect newly created profile and activate it explicitly + const before = await getProfiles().catch(() => null); + const beforeUids = new Set( + (before?.items || []).map((i: any) => i?.uid).filter(Boolean), + ); + await createProfile(item, fileDataRef.current); + + const after = await getProfiles().catch(() => null); + const newRemoteLocal = (after?.items || []).find( + (i: any) => + i && + (i.type === "remote" || i.type === "local") && + i.uid && + !beforeUids.has(i.uid), + ); + const newUid = (newRemoteLocal && newRemoteLocal.uid) as + | string + | undefined; + + if (newUid) { + try { + await patchProfiles({ current: newUid }); + } catch {} + } + + showNotice("success", t("Profile Created Successfully")); + setOpen(false); + props.onChange(true); + return; } else { if (!form.uid) throw new Error("UID not found"); - await patchProfile(form.uid, item); + await patchProfile(form.uid as string, item); + showNotice("success", t("Profile Updated Successfully")); } setOpen(false); - props.onChange(isActivating); + props.onChange(wasCurrent); } catch (err: any) { showNotice("error", err.message || err.toString()); } finally { diff --git a/src/locales/en.json b/src/locales/en.json index ae37ca42..55f1bfc5 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -392,6 +392,8 @@ "Profile Imported Successfully": "Profile Imported Successfully", "Profile Switched": "Profile Switched", "Profile Reactivated": "Profile Reactivated", + "Profile Created Successfully": "Profile created successfully", + "Profile Updated Successfully": "Profile updated successfully", "Profile switch interrupted by new selection": "Profile switch interrupted by new selection", "Only YAML Files Supported": "Only YAML Files Supported", "Settings Applied": "Settings Applied", diff --git a/src/locales/ru.json b/src/locales/ru.json index 2352c4be..5bd11971 100644 --- a/src/locales/ru.json +++ b/src/locales/ru.json @@ -392,6 +392,8 @@ "Profile Imported Successfully": "Профиль успешно импортирован", "Profile Switched": "Профиль изменен", "Profile Reactivated": "Профиль перезапущен", + "Profile Created Successfully": "Профиль успешно создан", + "Profile Updated Successfully": "Профиль успешно обновлён", "Profile switch interrupted by new selection": "Переключение профилей прервано новым выбором", "Only YAML Files Supported": "Поддерживаются только файлы YAML", "Settings Applied": "Настройки применены",