Skip to content

Commit

Permalink
feat(config): support custom app dir in windows (#582)
Browse files Browse the repository at this point in the history
* feat(backend): custom app dir in windows

* feat(frontend): support to change app dir
  • Loading branch information
greenhat616 authored Mar 9, 2024
1 parent 68e1ad2 commit 2e48df0
Show file tree
Hide file tree
Showing 11 changed files with 172 additions and 21 deletions.
49 changes: 49 additions & 0 deletions backend/tauri/src/cmds.rs
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,55 @@ pub async fn update_proxy_provider(name: String) -> CmdResult<()> {
Ok(())
}

#[cfg(windows)]
#[tauri::command]
pub fn get_custom_app_dir() -> CmdResult<Option<String>> {
use crate::utils::winreg::get_app_dir;
match get_app_dir() {
Ok(Some(path)) => Ok(Some(path.to_string_lossy().to_string())),
Ok(None) => Ok(None),
Err(err) => Err(err.to_string()),
}
}

#[cfg(not(windows))]
#[tauri::command]
pub fn get_custom_app_dir() -> CmdResult<Option<String>> {
Ok(None)
}

#[cfg(windows)]
#[tauri::command]
pub async fn set_custom_app_dir(path: String) -> CmdResult {
use crate::utils::{dialog::migrate_dialog, init::do_config_migration, winreg::set_app_dir};
use rust_i18n::t;
use std::path::PathBuf;

let path_str = path.clone();
let path = PathBuf::from(path);
wrap_err!(set_app_dir(&path))?;

// show a dialog to ask whether to migrate the data
let res = tauri::async_runtime::spawn_blocking(move || {
let msg = t!("dialog.custom_app_dir_migrate", path = path_str).to_string();
if migrate_dialog(&msg) {
let new_dir = PathBuf::from(path_str);
let old_dir = dirs::old_app_home_dir().unwrap();
do_config_migration(&old_dir, &new_dir)?;
}
Ok::<_, anyhow::Error>(())
})
.await;
wrap_err!(wrap_err!(res)?)?;
Ok(())
}

#[cfg(not(windows))]
#[tauri::command]
pub async fn set_custom_app_dir(_path: String) -> CmdResult {
Ok(())
}

#[cfg(windows)]
pub mod uwp {
use super::{wrap_err, CmdResult};
Expand Down
2 changes: 2 additions & 0 deletions backend/tauri/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,8 @@ fn main() -> std::io::Result<()> {
cmds::read_profile_file,
cmds::save_profile_file,
cmds::save_window_size_state,
cmds::get_custom_app_dir,
cmds::set_custom_app_dir,
// service mode
cmds::service::check_service,
cmds::service::install_service,
Expand Down
6 changes: 2 additions & 4 deletions backend/tauri/src/utils/dialog.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,12 @@ pub fn panic_dialog(msg: &str) {
.show();
}

pub fn migrate_dialog() -> bool {
let msg = format!("{}", t!("dialog.migrate"));

pub fn migrate_dialog(msg: &str) -> bool {
MessageDialog::new()
.set_level(MessageLevel::Warning)
.set_title("Clash Nyanpasu Migration")
.set_buttons(MessageButtons::YesNo)
.set_description(msg.as_str())
.set_description(msg)
.show()
}

Expand Down
24 changes: 14 additions & 10 deletions backend/tauri/src/utils/dirs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,21 +88,25 @@ pub fn old_app_home_dir() -> Result<PathBuf> {
pub fn app_home_dir() -> Result<PathBuf> {
#[cfg(target_os = "windows")]
{
use crate::utils::winreg::get_app_dir;
use tauri::utils::platform::current_exe;

if !PORTABLE_FLAG.get().unwrap_or(&false) {
Ok(home_dir()
let reg_app_dir = get_app_dir()?;
if let Some(reg_app_dir) = reg_app_dir {
return Ok(reg_app_dir);
}
return Ok(home_dir()
.ok_or(anyhow::anyhow!("failed to get app home dir"))?
.join(".config")
.join(APP_NAME))
} else {
let app_exe = current_exe()?;
let app_exe = dunce::canonicalize(app_exe)?;
let app_dir = app_exe
.parent()
.ok_or(anyhow::anyhow!("failed to get the portable app dir"))?;
Ok(PathBuf::from(app_dir).join(".config").join(APP_NAME))
.join(APP_NAME));
}

let app_exe = current_exe()?;
let app_exe = dunce::canonicalize(app_exe)?;
let app_dir = app_exe
.parent()
.ok_or(anyhow::anyhow!("failed to get the portable app dir"))?;
Ok(PathBuf::from(app_dir).join(".config").join(APP_NAME))
}

#[cfg(not(target_os = "windows"))]
Expand Down
6 changes: 4 additions & 2 deletions backend/tauri/src/utils/init/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use crate::{
};
use anyhow::Result;
use runas::Command as RunasCommand;
use rust_i18n::t;
use std::{fs, io::ErrorKind, path::PathBuf};

mod logging;
Expand All @@ -26,7 +27,8 @@ pub fn init_config() -> Result<()> {
}));

if let (Some(app_dir), Some(old_app_dir)) = (app_dir, old_app_dir) {
if !app_dir.exists() && old_app_dir.exists() && migrate_dialog() {
let msg = t!("dialog.migrate");
if !app_dir.exists() && old_app_dir.exists() && migrate_dialog(msg.to_string().as_str()) {
if let Err(e) = do_config_migration(&old_app_dir, &app_dir) {
super::dialog::error_dialog(format!("failed to do migration: {:?}", e))
}
Expand Down Expand Up @@ -193,7 +195,7 @@ pub fn init_service() -> Result<()> {
Ok(())
}

fn do_config_migration(old_app_dir: &PathBuf, app_dir: &PathBuf) -> anyhow::Result<()> {
pub fn do_config_migration(old_app_dir: &PathBuf, app_dir: &PathBuf) -> anyhow::Result<()> {
if let Err(e) = fs::rename(old_app_dir, app_dir) {
match e.kind() {
#[cfg(windows)]
Expand Down
2 changes: 2 additions & 0 deletions backend/tauri/src/utils/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,5 @@ pub mod init;
pub mod resolve;
pub mod tmpl;
// mod winhelp;
#[cfg(windows)]
pub mod winreg;
33 changes: 33 additions & 0 deletions backend/tauri/src/utils/winreg.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
use std::{
io::ErrorKind,
path::{Path, PathBuf},
};

use anyhow::Result;
use winreg::{enums::*, RegKey};

pub fn get_app_dir() -> Result<Option<PathBuf>> {
let hcu = RegKey::predef(HKEY_CURRENT_USER);
let key = match hcu.open_subkey("Software\\Clash Nyanpasu") {
Ok(key) => key,
Err(e) => {
if let ErrorKind::NotFound = e.kind() {
return Ok(None);
}
return Err(e.into());
}
};
let path: String = key.get_value("AppDir")?;
if path.is_empty() {
return Ok(None);
}
Ok(Some(PathBuf::from(path)))
}

pub fn set_app_dir(path: &Path) -> Result<()> {
let hcu = RegKey::predef(HKEY_CURRENT_USER);
let (key, _) = hcu.create_subkey("Software\\Clash Nyanpasu")?;
let path = path.to_str().unwrap(); // safe to unwrap
key.set_value("AppDir", &path)?;
Ok(())
}
3 changes: 2 additions & 1 deletion locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
},
"dialog": {
"panic": "Please report this issue to Github issue tracker.",
"migrate": "Old version config file detected\nMigrate to new version or not?\n WARNING: This will override your current config if exists"
"migrate": "Old version config file detected\nMigrate to new version or not?\n WARNING: This will override your current config if exists",
"custom_app_dir_migrate": "You will set custom app dir to %{path}\n Shall we move the current app dir to the new one?"
}
}
3 changes: 2 additions & 1 deletion locales/zh.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
},
"dialog": {
"panic": "请将此问题汇报到 Github 问题追踪器",
"migrate": "检测到旧版本配置文件\n是否迁移到新版本?\n警告: 此操作会覆盖掉现有配置文件"
"migrate": "检测到旧版本配置文件\n是否迁移到新版本?\n警告: 此操作会覆盖掉现有配置文件",
"custom_app_dir_migrate": "你将要更改应用目录至 %{path}。\n需要将现有数据迁移到新目录吗?"
}
}
57 changes: 54 additions & 3 deletions src/components/setting/setting-verge.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@ import { useMessage } from "@/hooks/use-notification";
import { useVerge } from "@/hooks/use-verge";
import {
collectLogs,
isPortable,
openAppDir,
openCoreDir,
openLogsDir,
setCustomAppDir,
} from "@/services/cmds";
import getSystem from "@/utils/get-system";
import { ArrowForward, IosShare } from "@mui/icons-material";
import { ArrowForward, IosShare, Settings } from "@mui/icons-material";
import {
Chip,
CircularProgress,
Expand All @@ -19,8 +21,9 @@ import {
Typography,
} from "@mui/material";
import { version } from "@root/package.json";
import { open } from "@tauri-apps/api/dialog";
import { checkUpdate } from "@tauri-apps/api/updater";
import { useLockFn } from "ahooks";
import { useAsyncEffect, useLockFn } from "ahooks";
import { useRef, useState } from "react";
import { useTranslation } from "react-i18next";
import MDYSwitch from "../common/mdy-switch";
Expand All @@ -46,6 +49,11 @@ const SettingVerge = ({ onError }: Props) => {

const { verge, patchVerge } = useVerge();
const { theme_mode, language, disable_auto_check_update } = verge ?? {};
const [portable, setPortable] = useState(false);

useAsyncEffect(async () => {
setPortable(await isPortable());
});

const [loading, setLoading] = useState({
theme_mode: false,
Expand Down Expand Up @@ -94,6 +102,30 @@ const SettingVerge = ({ onError }: Props) => {

const onSwitchFormat = (_e: any, value: boolean) => value;

const [changingAppDir, setChangingAppDir] = useState(false);
const changeAppDir = useLockFn(async () => {
setChangingAppDir(true);
try {
const selected = await open({ directory: true, multiple: false }); // TODO: use current app dir as defaultPath
if (!selected) return; // user cancelled the selection
if (Array.isArray(selected)) {
useMessage(t("Multiple directories are not supported"), {
title: t("Error"),
type: "error",
});
return;
}
await setCustomAppDir(selected);
} catch (err: any) {
useMessage(err.message || err.toString(), {
title: t("Error"),
type: "error",
});
} finally {
setChangingAppDir(false);
}
});

return (
<SettingList title={t("Nyanpasu Setting")}>
<ThemeViewer ref={themeRef} />
Expand Down Expand Up @@ -197,7 +229,26 @@ const SettingVerge = ({ onError }: Props) => {
</IconButton>
</SettingItem>

<SettingItem label={t("Open App Dir")}>
<SettingItem
label={t("Open App Dir")}
extra={
<IconButton
color="inherit"
size="small"
disabled={changingAppDir}
onClick={changeAppDir}
>
{changingAppDir ? (
<CircularProgress color="inherit" size={20} />
) : (
<Settings
fontSize="inherit"
style={{ cursor: "pointer", opacity: 0.75 }}
/>
)}
</IconButton>
}
>
<IconButton
color="inherit"
size="small"
Expand Down
8 changes: 8 additions & 0 deletions src/services/cmds.ts
Original file line number Diff line number Diff line change
Expand Up @@ -256,3 +256,11 @@ export async function selectProxy(group: string, name: string) {
export async function updateProxyProvider(name: string) {
return invoke<void>("update_proxy_provider", { name });
}

export async function getCustomAppDir() {
return invoke<string | null>("get_custom_app_dir");
}

export async function setCustomAppDir(dir: string) {
return invoke<void>("set_custom_app_dir", { dir });
}

0 comments on commit 2e48df0

Please sign in to comment.