Fixed an issue with adding a profile when making changes in advanced settings.
This commit is contained in:
@@ -164,7 +164,16 @@ pub async fn import_profile(url: String, option: Option<PrfOption>) -> CmdResult
|
|||||||
url
|
url
|
||||||
);
|
);
|
||||||
let item = wrap_err!(PrfItem::from_url(&url, None, None, option).await)?;
|
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]
|
#[tauri::command]
|
||||||
pub async fn create_profile(item: PrfItem, file_data: Option<String>) -> CmdResult {
|
pub async fn create_profile(item: PrfItem, file_data: Option<String>) -> CmdResult {
|
||||||
let item = wrap_err!(PrfItem::from(item, file_data).await)?;
|
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(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 更新配置文件
|
/// 更新配置文件
|
||||||
|
|||||||
@@ -504,13 +504,23 @@ impl PrfItem {
|
|||||||
selected: None,
|
selected: None,
|
||||||
extra,
|
extra,
|
||||||
option: Some(PrfOption {
|
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_interval,
|
||||||
update_always,
|
update_always,
|
||||||
|
timeout_seconds: Some(timeout),
|
||||||
|
danger_accept_invalid_certs: if accept_invalid_certs {
|
||||||
|
Some(true)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
merge,
|
merge,
|
||||||
script,
|
script,
|
||||||
rules,
|
rules,
|
||||||
proxies,
|
proxies,
|
||||||
groups,
|
groups,
|
||||||
|
use_hwid: Some(use_hwid),
|
||||||
..PrfOption::default()
|
..PrfOption::default()
|
||||||
}),
|
}),
|
||||||
home,
|
home,
|
||||||
|
|||||||
@@ -136,10 +136,9 @@ impl IProfiles {
|
|||||||
.with_context(|| format!("failed to write to file \"{file}\""))?;
|
.with_context(|| format!("failed to write to file \"{file}\""))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.current.is_none()
|
if item.itype == Some("remote".to_string()) || item.itype == Some("local".to_string()) {
|
||||||
&& (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();
|
||||||
self.current = uid;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.items.is_none() {
|
if self.items.is_none() {
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import {
|
|||||||
importProfile,
|
importProfile,
|
||||||
enhanceProfiles,
|
enhanceProfiles,
|
||||||
createProfileFromShareLink,
|
createProfileFromShareLink,
|
||||||
|
getProfiles,
|
||||||
} from "@/services/cmds";
|
} from "@/services/cmds";
|
||||||
import { useProfiles } from "@/hooks/use-profiles";
|
import { useProfiles } from "@/hooks/use-profiles";
|
||||||
import { showNotice } from "@/services/noticeService";
|
import { showNotice } from "@/services/noticeService";
|
||||||
@@ -65,7 +66,7 @@ export const ProfileViewer = forwardRef<ProfileViewerRef, Props>(
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const [openType, setOpenType] = useState<"new" | "edit">("new");
|
const [openType, setOpenType] = useState<"new" | "edit">("new");
|
||||||
const { profiles } = useProfiles();
|
const { profiles, patchProfiles } = useProfiles();
|
||||||
const fileDataRef = useRef<string | null>(null);
|
const fileDataRef = useRef<string | null>(null);
|
||||||
|
|
||||||
const [showAdvanced, setShowAdvanced] = useState(false);
|
const [showAdvanced, setShowAdvanced] = useState(false);
|
||||||
@@ -210,33 +211,81 @@ export const ProfileViewer = forwardRef<ProfileViewerRef, Props>(
|
|||||||
|
|
||||||
const handleSaveAdvanced = useLockFn(
|
const handleSaveAdvanced = useLockFn(
|
||||||
handleSubmit(async (formData) => {
|
handleSubmit(async (formData) => {
|
||||||
const form = { ...formData, url: formData.url || importUrl };
|
const form = { ...formData, url: formData.url || importUrl } as Partial<IProfileItem>;
|
||||||
|
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
try {
|
try {
|
||||||
if (!form.type) throw new Error("`Type` should not be null");
|
if (!form.type) throw new Error("`Type` should not be null");
|
||||||
if (form.type === "remote" && !form.url)
|
if (form.type === "remote" && !form.url)
|
||||||
throw new Error("The URL should not be null");
|
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`;
|
// Clean option fields: only send what user actually set
|
||||||
const item = { ...form, name };
|
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<IProfileItem> = {
|
||||||
|
...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 isUpdate = openType === "edit";
|
||||||
const isActivating =
|
const wasCurrent = isUpdate && form.uid === (profiles?.current ?? "");
|
||||||
isUpdate && form.uid === (profiles?.current ?? "");
|
|
||||||
|
|
||||||
if (openType === "new") {
|
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);
|
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 {
|
} else {
|
||||||
if (!form.uid) throw new Error("UID not found");
|
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);
|
setOpen(false);
|
||||||
props.onChange(isActivating);
|
props.onChange(wasCurrent);
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
showNotice("error", err.message || err.toString());
|
showNotice("error", err.message || err.toString());
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@@ -392,6 +392,8 @@
|
|||||||
"Profile Imported Successfully": "Profile Imported Successfully",
|
"Profile Imported Successfully": "Profile Imported Successfully",
|
||||||
"Profile Switched": "Profile Switched",
|
"Profile Switched": "Profile Switched",
|
||||||
"Profile Reactivated": "Profile Reactivated",
|
"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",
|
"Profile switch interrupted by new selection": "Profile switch interrupted by new selection",
|
||||||
"Only YAML Files Supported": "Only YAML Files Supported",
|
"Only YAML Files Supported": "Only YAML Files Supported",
|
||||||
"Settings Applied": "Settings Applied",
|
"Settings Applied": "Settings Applied",
|
||||||
|
|||||||
@@ -392,6 +392,8 @@
|
|||||||
"Profile Imported Successfully": "Профиль успешно импортирован",
|
"Profile Imported Successfully": "Профиль успешно импортирован",
|
||||||
"Profile Switched": "Профиль изменен",
|
"Profile Switched": "Профиль изменен",
|
||||||
"Profile Reactivated": "Профиль перезапущен",
|
"Profile Reactivated": "Профиль перезапущен",
|
||||||
|
"Profile Created Successfully": "Профиль успешно создан",
|
||||||
|
"Profile Updated Successfully": "Профиль успешно обновлён",
|
||||||
"Profile switch interrupted by new selection": "Переключение профилей прервано новым выбором",
|
"Profile switch interrupted by new selection": "Переключение профилей прервано новым выбором",
|
||||||
"Only YAML Files Supported": "Поддерживаются только файлы YAML",
|
"Only YAML Files Supported": "Поддерживаются только файлы YAML",
|
||||||
"Settings Applied": "Настройки применены",
|
"Settings Applied": "Настройки применены",
|
||||||
|
|||||||
Reference in New Issue
Block a user