diff --git a/src-tauri/src/cmds.rs b/src-tauri/src/cmds.rs index 8f54fb3..8cf1b54 100644 --- a/src-tauri/src/cmds.rs +++ b/src-tauri/src/cmds.rs @@ -43,6 +43,42 @@ pub async fn import_profile( } } +/// new a profile +/// append a temp profile item file to the `profiles` dir +/// view the temp profile file by using vscode or other editor +#[tauri::command] +pub async fn new_profile( + name: String, + desc: String, + profiles_state: State<'_, ProfilesState>, +) -> Result<(), String> { + let mut profiles = profiles_state.0.lock().unwrap(); + + let (_, path) = profiles.append_item(name, desc)?; + + if !path.exists() { + return Err("the file not found".into()); + } + + // use vscode first + if let Ok(code) = which::which("code") { + return match Command::new(code).arg(path).status() { + Ok(_) => Ok(()), + Err(_) => Err("failed to open file by VScode".into()), + }; + } + + // use `open` command + if let Ok(open) = which::which("open") { + return match Command::new(open).arg(path).status() { + Ok(_) => Ok(()), + Err(_) => Err("failed to open file by `open`".into()), + }; + } + + return Err("failed to open the file, please edit the file manually".into()); +} + /// Update the profile #[tauri::command] pub async fn update_profile( diff --git a/src-tauri/src/core/profiles.rs b/src-tauri/src/core/profiles.rs index 96093c7..3f30ffa 100644 --- a/src-tauri/src/core/profiles.rs +++ b/src-tauri/src/core/profiles.rs @@ -7,6 +7,7 @@ use std::collections::HashMap; use std::env::temp_dir; use std::fs::File; use std::io::Write; +use std::path::PathBuf; use std::time::{SystemTime, UNIX_EPOCH}; /// Define the `profiles.yaml` schema @@ -23,6 +24,8 @@ pub struct Profiles { pub struct ProfileItem { /// profile name pub name: Option, + /// profile description + pub desc: Option, /// profile file pub file: Option, /// current mode @@ -109,6 +112,7 @@ impl Profiles { items.push(ProfileItem { name: Some(result.name), + desc: Some("imported url".into()), file: Some(result.file), mode: Some(format!("rule")), url: Some(url), @@ -138,6 +142,49 @@ impl Profiles { self.save_file() } + /// append new item + /// return the new item's index + pub fn append_item(&mut self, name: String, desc: String) -> Result<(usize, PathBuf), String> { + let mut items = self.items.take().unwrap_or(vec![]); + + // create a new profile file + let now = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_secs(); + let file = format!("{}.yaml", now); + let path = dirs::app_home_dir().join("profiles").join(&file); + + let file_data = b"# Profile Template for clash verge\n +# proxies defination (optional, the same as clash) +proxies:\n +# proxy-groups (optional, the same as clash) +proxy-groups:\n +# rules (optional, the same as clash) +rules:\n\n +"; + + match File::create(&path).unwrap().write(file_data) { + Ok(_) => { + items.push(ProfileItem { + name: Some(name), + desc: Some(desc), + file: Some(file), + mode: None, + url: None, + selected: Some(vec![]), + extra: None, + updated: Some(now as usize), + }); + + let index = items.len(); + self.items = Some(items); + Ok((index, path)) + } + Err(_) => Err("failed to create file".into()), + } + } + /// update the target profile /// and save to config file /// only support the url item diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index ba743a3..3184c87 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -81,6 +81,7 @@ fn main() -> std::io::Result<()> { cmds::get_verge_config, cmds::patch_verge_config, // profile + cmds::new_profile, cmds::view_profile, cmds::patch_profile, cmds::import_profile, diff --git a/src/pages/profiles.tsx b/src/pages/profiles.tsx index 75a2639..299658a 100644 --- a/src/pages/profiles.tsx +++ b/src/pages/profiles.tsx @@ -6,6 +6,7 @@ import { selectProfile, patchProfile, importProfile, + newProfile, } from "../services/cmds"; import { getProxies, updateProxy } from "../services/api"; import noop from "../utils/noop"; @@ -94,6 +95,21 @@ const ProfilePage = () => { } }; + const lockNewRef = useRef(false); + const onNew = async () => { + if (lockNewRef.current) return; + lockNewRef.current = true; + + try { + await newProfile("New Profile", "no desc"); + mutate("getProfiles"); + } catch (err: any) { + err && Notice.error(err.toString()); + } finally { + lockNewRef.current = false; + } + }; + return ( @@ -105,15 +121,19 @@ const ProfilePage = () => { fullWidth value={url} onChange={(e) => setUrl(e.target.value)} - sx={{ mr: 2 }} + sx={{ mr: 1 }} /> + diff --git a/src/services/cmds.ts b/src/services/cmds.ts index 6f5703f..5f9cf08 100644 --- a/src/services/cmds.ts +++ b/src/services/cmds.ts @@ -9,6 +9,10 @@ export async function syncProfiles() { return invoke("sync_profiles"); } +export async function newProfile(name: string, desc: string) { + return invoke("new_profile", { name, desc }); +} + export async function viewProfile(index: number) { return invoke("view_profile", { index }); }