Skip to content

Commit

Permalink
feat: profile enhanced mode
Browse files Browse the repository at this point in the history
  • Loading branch information
zzzgydi committed Mar 6, 2022
1 parent a43dab8 commit ef47a74
Show file tree
Hide file tree
Showing 10 changed files with 293 additions and 64 deletions.
39 changes: 38 additions & 1 deletion src-tauri/src/cmds.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use crate::{
use anyhow::Result;
use serde_yaml::Mapping;
use std::{path::PathBuf, process::Command};
use tauri::{api, State};
use tauri::{api, Manager, State};

/// get all profiles from `profiles.yaml`
#[tauri::command]
Expand Down Expand Up @@ -100,6 +100,43 @@ pub fn select_profile(
wrap_err!(clash.activate(&profiles))
}

/// change the profile chain
#[tauri::command]
pub fn change_profile_chain(
chain: Option<Vec<String>>,
app_handle: tauri::AppHandle,
clash_state: State<'_, ClashState>,
profiles_state: State<'_, ProfilesState>,
) -> Result<(), String> {
let clash = clash_state.0.lock().unwrap();
let mut profiles = profiles_state.0.lock().unwrap();

profiles.put_chain(chain);

app_handle
.get_window("main")
.map(|win| wrap_err!(clash.activate_enhanced(&profiles, win, false)));

Ok(())
}

/// manually exec enhanced profile
#[tauri::command]
pub fn enhance_profiles(
app_handle: tauri::AppHandle,
clash_state: State<'_, ClashState>,
profiles_state: State<'_, ProfilesState>,
) -> Result<(), String> {
let clash = clash_state.0.lock().unwrap();
let profiles = profiles_state.0.lock().unwrap();

app_handle
.get_window("main")
.map(|win| wrap_err!(clash.activate_enhanced(&profiles, win, false)));

Ok(())
}

/// delete profile item
#[tauri::command]
pub fn delete_profile(
Expand Down
42 changes: 32 additions & 10 deletions src-tauri/src/core/clash.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use super::{Profiles, Verge};
use super::{PrfEnhancedResult, Profiles, Verge};
use crate::utils::{config, dirs, help};
use anyhow::{bail, Result};
use reqwest::header::HeaderMap;
Expand Down Expand Up @@ -260,6 +260,7 @@ impl Clash {
let mut data = HashMap::new();
data.insert("path", temp_path.as_os_str().to_str().unwrap());

// retry 5 times
for _ in 0..5 {
match reqwest::ClientBuilder::new().no_proxy().build() {
Ok(client) => match client
Expand All @@ -269,11 +270,18 @@ impl Clash {
.send()
.await
{
Ok(_) => break,
Ok(resp) => {
if resp.status() != 204 {
log::error!("failed to activate clash for status \"{}\"", resp.status());
}
// do not retry
break;
}
Err(err) => log::error!("failed to activate for `{err}`"),
},
Err(err) => log::error!("failed to activate for `{err}`"),
}
sleep(Duration::from_millis(500)).await;
}
});

Expand All @@ -294,29 +302,43 @@ impl Clash {
}

/// enhanced profiles mode
pub fn activate_enhanced(&self, profiles: &Profiles, win: tauri::Window) -> Result<()> {
pub fn activate_enhanced(
&self,
profiles: &Profiles,
win: tauri::Window,
delay: bool,
) -> Result<()> {
let event_name = help::get_uid("e");
let event_name = format!("script-cb-{event_name}");
let event_name = format!("enhanced-cb-{event_name}");

let info = self.info.clone();
let mut config = self.config.clone();

// generate the payload
let payload = profiles.gen_enhanced()?;
let payload = profiles.gen_enhanced(event_name.clone())?;

win.once(&event_name, move |event| {
if let Some(result) = event.payload() {
let gen_map: Mapping = serde_json::from_str(result).unwrap();
let result: PrfEnhancedResult = serde_json::from_str(result).unwrap();

for (key, value) in gen_map.into_iter() {
config.insert(key, value);
if let Some(data) = result.data {
for (key, value) in data.into_iter() {
config.insert(key, value);
}
Self::_activate(info, config).unwrap();
}
Self::_activate(info, config).unwrap();

log::info!("profile enhanced status {}", result.status);

result.error.map(|error| log::error!("{error}"));
}
});

tauri::async_runtime::spawn(async move {
sleep(Duration::from_secs(5)).await;
// wait the window setup during resolve app
if delay {
sleep(Duration::from_secs(2)).await;
}
win.emit("script-handler", payload).unwrap();
});

Expand Down
24 changes: 22 additions & 2 deletions src-tauri/src/core/profiles.rs
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,11 @@ impl Profiles {
bail!("invalid uid \"{uid}\"");
}

/// just change the `chain`
pub fn put_chain(&mut self, chain: Option<Vec<String>>) {
self.chain = chain;
}

/// find the item by the uid
pub fn get_item(&self, uid: &String) -> Result<&PrfItem> {
if self.items.is_some() {
Expand Down Expand Up @@ -519,7 +524,7 @@ impl Profiles {
}

/// gen the enhanced profiles
pub fn gen_enhanced(&self) -> Result<PrfEnhanced> {
pub fn gen_enhanced(&self, callback: String) -> Result<PrfEnhanced> {
let current = self.gen_activate()?;

let chain = match self.chain.as_ref() {
Expand All @@ -535,7 +540,11 @@ impl Profiles {
None => vec![],
};

Ok(PrfEnhanced { current, chain })
Ok(PrfEnhanced {
current,
chain,
callback,
})
}
}

Expand All @@ -544,6 +553,17 @@ pub struct PrfEnhanced {
current: Mapping,

chain: Vec<PrfData>,

callback: String,
}

#[derive(Default, Debug, Clone, Serialize, Deserialize)]
pub struct PrfEnhancedResult {
pub data: Option<Mapping>,

pub status: String,

pub error: Option<String>,
}

#[derive(Default, Debug, Clone, Serialize, Deserialize)]
Expand Down
2 changes: 2 additions & 0 deletions src-tauri/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ fn main() -> std::io::Result<()> {
cmds::select_profile,
cmds::get_profiles,
cmds::sync_profiles,
cmds::enhance_profiles,
cmds::change_profile_chain
]);

#[cfg(target_os = "macos")]
Expand Down
7 changes: 4 additions & 3 deletions src-tauri/src/utils/resolve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,10 @@ pub fn resolve_setup(app: &App) {
*profiles = Profiles::read_file();
log_if_err!(clash.activate(&profiles));

app
.get_window("main")
.map(|win| log_if_err!(clash.activate_enhanced(&profiles, win)));
match app.get_window("main") {
Some(win) => log_if_err!(clash.activate_enhanced(&profiles, win, true)),
None => log::error!("failed to get window for enhanced profiles"),
};

verge.init_sysproxy(clash.info.port.clone());
// enable tun mode
Expand Down
30 changes: 12 additions & 18 deletions src/components/profile/profile-more.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import dayjs from "dayjs";
import { useLockFn } from "ahooks";
import { useSWRConfig } from "swr";
import { useState } from "react";
import {
alpha,
Expand All @@ -12,7 +10,7 @@ import {
Menu,
} from "@mui/material";
import { CmdType } from "../../services/types";
import { deleteProfile, viewProfile } from "../../services/cmds";
import { viewProfile } from "../../services/cmds";
import relativeTime from "dayjs/plugin/relativeTime";
import ProfileEdit from "./profile-edit";
import Notice from "../base/base-notice";
Expand All @@ -37,6 +35,7 @@ interface Props {
onDisable: () => void;
onMoveTop: () => void;
onMoveEnd: () => void;
onDelete: () => void;
}

// profile enhanced item
Expand All @@ -48,10 +47,10 @@ const ProfileMore = (props: Props) => {
onDisable,
onMoveTop,
onMoveEnd,
onDelete,
} = props;

const { type } = itemData;
const { mutate } = useSWRConfig();
const [anchorEl, setAnchorEl] = useState<any>(null);
const [position, setPosition] = useState({ left: 0, top: 0 });
const [editOpen, setEditOpen] = useState(false);
Expand All @@ -70,30 +69,25 @@ const ProfileMore = (props: Props) => {
}
};

const onDelete = useLockFn(async () => {
const closeWrapper = (fn: () => void) => () => {
setAnchorEl(null);
try {
await deleteProfile(itemData.uid);
mutate("getProfiles");
} catch (err: any) {
Notice.error(err?.message || err.toString());
}
});
return fn();
};

const enableMenu = [
{ label: "Disable", handler: onDisable },
{ label: "Disable", handler: closeWrapper(onDisable) },
{ label: "Edit", handler: onEdit },
{ label: "View File", handler: onView },
{ label: "To Top", handler: onMoveTop },
{ label: "To End", handler: onMoveEnd },
{ label: "Delete", handler: onDelete },
{ label: "To Top", handler: closeWrapper(onMoveTop) },
{ label: "To End", handler: closeWrapper(onMoveEnd) },
{ label: "Delete", handler: closeWrapper(onDelete) },
];

const disableMenu = [
{ label: "Enable", handler: onEnable },
{ label: "Enable", handler: closeWrapper(onEnable) },
{ label: "Edit", handler: onEdit },
{ label: "View File", handler: onView },
{ label: "Delete", handler: onDelete },
{ label: "Delete", handler: closeWrapper(onDelete) },
];

const boxStyle = {
Expand Down
77 changes: 64 additions & 13 deletions src/pages/profiles.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import useSWR, { useSWRConfig } from "swr";
import { useEffect, useMemo, useState } from "react";
import { useLockFn } from "ahooks";
import { useEffect, useMemo, useState } from "react";
import { Box, Button, Grid, TextField } from "@mui/material";
import {
getProfiles,
selectProfile,
patchProfile,
deleteProfile,
selectProfile,
importProfile,
changeProfileChain,
} from "../services/cmds";
import { getProxies, updateProxy } from "../services/api";
import Notice from "../components/base/base-notice";
Expand All @@ -25,13 +27,20 @@ const ProfilePage = () => {
const { data: profiles = {} } = useSWR("getProfiles", getProfiles);

const { regularItems, enhanceItems } = useMemo(() => {
const { items = [] } = profiles;
const regularItems = items.filter((i) =>
["local", "remote"].includes(i.type!)
);
const enhanceItems = items.filter((i) =>
["merge", "script"].includes(i.type!)
);
const items = profiles.items || [];
const chain = profiles.chain || [];

const type1 = ["local", "remote"];
const type2 = ["merge", "script"];

const regularItems = items.filter((i) => type1.includes(i.type!));
const restItems = items.filter((i) => type2.includes(i.type!));

const restMap = Object.fromEntries(restItems.map((i) => [i.uid, i]));

const enhanceItems = chain
.map((i) => restMap[i]!)
.concat(restItems.filter((i) => !chain.includes(i.uid)));

return { regularItems, enhanceItems };
}, [profiles]);
Expand Down Expand Up @@ -113,10 +122,51 @@ const ProfilePage = () => {
}
});

const onEnhanceEnable = useLockFn(async (uid: string) => {});
const onEnhanceDisable = useLockFn(async (uid: string) => {});
const onMoveTop = useLockFn(async (uid: string) => {});
const onMoveEnd = useLockFn(async (uid: string) => {});
/** enhanced profile mode */

const chain = profiles.chain || [];

const onEnhanceEnable = useLockFn(async (uid: string) => {
if (chain.includes(uid)) return;

const newChain = [...chain, uid];
await changeProfileChain(newChain);
mutate("getProfiles", { ...profiles, chain: newChain }, true);
});

const onEnhanceDisable = useLockFn(async (uid: string) => {
if (!chain.includes(uid)) return;

const newChain = chain.filter((i) => i !== uid);
await changeProfileChain(newChain);
mutate("getProfiles", { ...profiles, chain: newChain }, true);
});

const onEnhanceDelete = useLockFn(async (uid: string) => {
try {
await onEnhanceDisable(uid);
await deleteProfile(uid);
mutate("getProfiles");
} catch (err: any) {
Notice.error(err?.message || err.toString());
}
});

const onMoveTop = useLockFn(async (uid: string) => {
if (!chain.includes(uid)) return;

const newChain = [uid].concat(chain.filter((i) => i !== uid));
await changeProfileChain(newChain);
mutate("getProfiles", { ...profiles, chain: newChain }, true);
});

const onMoveEnd = useLockFn(async (uid: string) => {
if (!chain.includes(uid)) return;

const newChain = chain.filter((i) => i !== uid).concat([uid]);
await changeProfileChain(newChain);
mutate("getProfiles", { ...profiles, chain: newChain }, true);
});

return (
<BasePage title="Profiles">
Expand Down Expand Up @@ -164,6 +214,7 @@ const ProfilePage = () => {
itemData={item}
onEnable={() => onEnhanceEnable(item.uid)}
onDisable={() => onEnhanceDisable(item.uid)}
onDelete={() => onEnhanceDelete(item.uid)}
onMoveTop={() => onMoveTop(item.uid)}
onMoveEnd={() => onMoveEnd(item.uid)}
/>
Expand Down
Loading

0 comments on commit ef47a74

Please sign in to comment.