feat: the other profile buttons
This commit is contained in:
@ -7,7 +7,7 @@ use crate::model::misc::Game;
|
|||||||
use crate::pkg::{Package, PkgKey};
|
use crate::pkg::{Package, PkgKey};
|
||||||
use crate::pkg_store::InstallResult;
|
use crate::pkg_store::InstallResult;
|
||||||
use crate::profiles::ongeki::OngekiProfile;
|
use crate::profiles::ongeki::OngekiProfile;
|
||||||
use crate::profiles::{AnyProfile, Profile, ProfileMeta, ProfilePaths};
|
use crate::profiles::{self, AnyProfile, Profile, ProfileMeta, ProfilePaths};
|
||||||
use crate::appdata::AppData;
|
use crate::appdata::AppData;
|
||||||
use crate::util;
|
use crate::util;
|
||||||
|
|
||||||
@ -159,24 +159,63 @@ pub async fn rename_profile(
|
|||||||
|
|
||||||
let new_meta = ProfileMeta {
|
let new_meta = ProfileMeta {
|
||||||
game: profile.game.clone(),
|
game: profile.game.clone(),
|
||||||
name: name.clone()
|
name: profiles::fixed_name(&ProfileMeta { game: profile.game.clone(), name }, false)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if new_meta.name == profile.name {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
if new_meta.config_dir().exists() {
|
if new_meta.config_dir().exists() {
|
||||||
return Err(format!("Profile {} already exists", &name));
|
return Err(format!("Profile {} already exists", &new_meta.name));
|
||||||
}
|
}
|
||||||
|
|
||||||
fs::rename(profile.config_dir(), new_meta.config_dir()).await
|
fs::rename(profile.config_dir(), new_meta.config_dir()).await
|
||||||
.map_err(|e| format!("Unable to rename: {}", e))?;
|
.map_err(|e| format!("Unable to rename: {}", e))?;
|
||||||
|
|
||||||
if let Err(e) = fs::rename(profile.data_dir(), new_meta.data_dir()).await {
|
if let Err(e) = fs::rename(profile.data_dir(), new_meta.data_dir()).await {
|
||||||
log::warn!("Unable to move data dir {}->{}: {}", &profile.name, &name, e);
|
log::warn!("Unable to move data dir {}->{}: {}", &profile.name, &new_meta.name, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut appd = state.lock().await;
|
let mut appd = state.lock().await;
|
||||||
if let Some(current) = &mut appd.profile {
|
if let Some(current) = &mut appd.profile {
|
||||||
if current.meta() == profile {
|
if current.meta() == profile {
|
||||||
current.rename(name);
|
current.rename(new_meta.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn duplicate_profile(profile: ProfileMeta) -> Result<(), String> {
|
||||||
|
log::debug!("invoke: duplicate_profile({:?})", profile);
|
||||||
|
|
||||||
|
let new_meta = ProfileMeta {
|
||||||
|
game: profile.game.clone(),
|
||||||
|
name: profiles::fixed_name(&profile, true)
|
||||||
|
};
|
||||||
|
|
||||||
|
util::copy_directory(profile.config_dir(), new_meta.config_dir(), false)
|
||||||
|
.map_err(|e| format!("Unable to duplicate: {}", e))?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn delete_profile(state: State<'_, Mutex<AppData>>, profile: ProfileMeta) -> Result<(), String> {
|
||||||
|
log::debug!("invoke: delete_profile({:?})", profile);
|
||||||
|
|
||||||
|
std::fs::remove_dir_all(profile.config_dir())
|
||||||
|
.map_err(|e| format!("Unable to delete {:?}: {}", profile.config_dir(), e))?;
|
||||||
|
if let Err(e) = std::fs::remove_dir_all(profile.data_dir()) {
|
||||||
|
log::warn!("Unable to delete: {:?} {}", profile.data_dir(), e);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut appd = state.lock().await;
|
||||||
|
if let Some(current) = &mut appd.profile {
|
||||||
|
if current.meta() == profile {
|
||||||
|
appd.profile = None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,7 +51,7 @@ impl DownloadHandler {
|
|||||||
cache_file_w.sync_all().await?;
|
cache_file_w.sync_all().await?;
|
||||||
tokio::fs::rename(&zip_path_part, &zip_path).await?;
|
tokio::fs::rename(&zip_path_part, &zip_path).await?;
|
||||||
|
|
||||||
log::debug!("Downloaded to {}", zip_path.to_string_lossy());
|
log::debug!("Downloaded to {:?}", zip_path);
|
||||||
|
|
||||||
app.emit("download-end", pkg_key)?;
|
app.emit("download-end", pkg_key)?;
|
||||||
|
|
||||||
|
@ -154,6 +154,8 @@ pub async fn run(_args: Vec<String>) {
|
|||||||
cmd::init_profile,
|
cmd::init_profile,
|
||||||
cmd::load_profile,
|
cmd::load_profile,
|
||||||
cmd::rename_profile,
|
cmd::rename_profile,
|
||||||
|
cmd::duplicate_profile,
|
||||||
|
cmd::delete_profile,
|
||||||
cmd::get_current_profile,
|
cmd::get_current_profile,
|
||||||
cmd::save_current_profile,
|
cmd::save_current_profile,
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ pub async fn prepare_packages<'a>(p: &'a impl ProfilePaths, pkgs: &BTreeSet<PkgK
|
|||||||
.join("app")
|
.join("app")
|
||||||
.join("BepInEx");
|
.join("BepInEx");
|
||||||
if bpx_dir.exists() {
|
if bpx_dir.exists() {
|
||||||
util::copy_recursive(&bpx_dir, &pfx_dir.join("BepInEx"))?;
|
util::copy_directory(&bpx_dir, &pfx_dir.join("BepInEx"), true)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let opt_dir = util::pkg_dir_of(namespace, &name[1..]).join("option");
|
let opt_dir = util::pkg_dir_of(namespace, &name[1..]).join("option");
|
||||||
|
@ -55,7 +55,7 @@ impl AnyProfile {
|
|||||||
pub fn rename(&mut self, name: String) {
|
pub fn rename(&mut self, name: String) {
|
||||||
match self {
|
match self {
|
||||||
Self::OngekiProfile(p) => {
|
Self::OngekiProfile(p) => {
|
||||||
p.name = Some(name);
|
p.name = Some(fixed_name(&ProfileMeta { name, game: Game::Ongeki }, false));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -160,3 +160,15 @@ fn meta_from_path(path: impl AsRef<Path>) -> Option<ProfileMeta> {
|
|||||||
|
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn fixed_name(meta: &ProfileMeta, prepend_new: bool) -> String {
|
||||||
|
let mut name = meta.name.trim()
|
||||||
|
.replace(" ", "-")
|
||||||
|
.replace("..", "").replace("/", "").replace("\\", "");
|
||||||
|
|
||||||
|
while prepend_new && util::profile_config_dir(&meta.game, &name).exists() {
|
||||||
|
name = format!("new-{}", name);
|
||||||
|
}
|
||||||
|
|
||||||
|
name
|
||||||
|
}
|
@ -3,6 +3,7 @@ use tauri::AppHandle;
|
|||||||
use tauri::Emitter;
|
use tauri::Emitter;
|
||||||
use std::{collections::BTreeSet, path::PathBuf, process::Stdio};
|
use std::{collections::BTreeSet, path::PathBuf, process::Stdio};
|
||||||
use crate::model::config::BepInEx;
|
use crate::model::config::BepInEx;
|
||||||
|
use crate::profiles::fixed_name;
|
||||||
use crate::util::PathStr;
|
use crate::util::PathStr;
|
||||||
use crate::{model::{config::{Display, DisplayMode, Network, Segatools}, misc::Game, segatools_base::segatools_base}, pkg::PkgKey, util};
|
use crate::{model::{config::{Display, DisplayMode, Network, Segatools}, misc::Game, segatools_base::segatools_base}, pkg::PkgKey, util};
|
||||||
use super::{Profile, ProfileMeta, ProfilePaths};
|
use super::{Profile, ProfileMeta, ProfilePaths};
|
||||||
@ -27,14 +28,10 @@ pub struct OngekiProfile {
|
|||||||
|
|
||||||
impl Profile for OngekiProfile {
|
impl Profile for OngekiProfile {
|
||||||
fn new(name: String) -> Result<Self> {
|
fn new(name: String) -> Result<Self> {
|
||||||
let mut fixed_name = name.trim().replace(" ", "-");
|
let name = fixed_name(&ProfileMeta { name, game: Game::Ongeki }, true);
|
||||||
|
|
||||||
while util::profile_config_dir(&Game::Ongeki, &name).exists() {
|
|
||||||
fixed_name = format!("new-{}", name);
|
|
||||||
}
|
|
||||||
|
|
||||||
let p = OngekiProfile {
|
let p = OngekiProfile {
|
||||||
name: Some(fixed_name),
|
name: Some(name.clone()),
|
||||||
mods: BTreeSet::new(),
|
mods: BTreeSet::new(),
|
||||||
sgt: Segatools::default(),
|
sgt: Segatools::default(),
|
||||||
display: Display::default(),
|
display: Display::default(),
|
||||||
@ -43,7 +40,8 @@ impl Profile for OngekiProfile {
|
|||||||
};
|
};
|
||||||
p.save()?;
|
p.save()?;
|
||||||
std::fs::write(p.config_dir().join("segatools-base.ini"), segatools_base())?;
|
std::fs::write(p.config_dir().join("segatools-base.ini"), segatools_base())?;
|
||||||
log::debug!("created profile-ongeki-{}", name);
|
log::debug!("created profile-ongeki-{}", &name);
|
||||||
|
|
||||||
Ok(p)
|
Ok(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,7 +69,7 @@ impl Profile for OngekiProfile {
|
|||||||
}
|
}
|
||||||
std::fs::write(&path, s)
|
std::fs::write(&path, s)
|
||||||
.map_err(|e| anyhow!("error when writing to {:?}: {}", path, e))?;
|
.map_err(|e| anyhow!("error when writing to {:?}: {}", path, e))?;
|
||||||
log::info!("Written to {}", path.to_string_lossy());
|
log::info!("Written to {:?}", path);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -79,7 +77,7 @@ impl Profile for OngekiProfile {
|
|||||||
async fn start(&self, app: AppHandle) -> Result<()> {
|
async fn start(&self, app: AppHandle) -> Result<()> {
|
||||||
let ini_path = self.data_dir().join("segatools.ini");
|
let ini_path = self.data_dir().join("segatools.ini");
|
||||||
|
|
||||||
log::debug!("With path {}", ini_path.to_string_lossy());
|
log::debug!("With path {:?}", ini_path);
|
||||||
|
|
||||||
let mut game_builder;
|
let mut game_builder;
|
||||||
let mut amd_builder;
|
let mut amd_builder;
|
||||||
|
@ -66,15 +66,19 @@ pub fn pkg_dir_of(namespace: &str, name: &str) -> PathBuf {
|
|||||||
pkg_dir().join(format!("{}-{}", namespace, name))
|
pkg_dir().join(format!("{}-{}", namespace, name))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn copy_recursive(src: &Path, dst: &Path) -> std::io::Result<()> {
|
pub fn copy_directory(src: impl AsRef<Path>, dst: impl AsRef<Path>, recursive: bool) -> std::io::Result<()> {
|
||||||
std::fs::create_dir_all(&dst).unwrap();
|
std::fs::create_dir_all(dst.as_ref()).unwrap();
|
||||||
for entry in std::fs::read_dir(src)? {
|
for entry in std::fs::read_dir(src.as_ref())? {
|
||||||
let entry = entry?;
|
let entry = entry?;
|
||||||
let meta = entry.metadata()?;
|
let meta = entry.metadata()?;
|
||||||
if meta.is_dir() {
|
if meta.is_dir() {
|
||||||
copy_recursive(&entry.path(), &dst.join(entry.file_name()))?;
|
if recursive == true {
|
||||||
|
copy_directory(&entry.path(), &dst.as_ref().join(entry.file_name()), true)?;
|
||||||
} else {
|
} else {
|
||||||
std::fs::copy(&entry.path(), &dst.join(entry.file_name()))?;
|
log::warn!("Skipping directory {:?}", meta);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
std::fs::copy(&entry.path(), &dst.as_ref().join(entry.file_name()))?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -4,6 +4,7 @@ import Button from 'primevue/button';
|
|||||||
import InputText from 'primevue/inputtext';
|
import InputText from 'primevue/inputtext';
|
||||||
import * as path from '@tauri-apps/api/path';
|
import * as path from '@tauri-apps/api/path';
|
||||||
import { open } from '@tauri-apps/plugin-shell';
|
import { open } from '@tauri-apps/plugin-shell';
|
||||||
|
import { invoke } from '../invoke';
|
||||||
import { useGeneralStore, usePrfStore } from '../stores';
|
import { useGeneralStore, usePrfStore } from '../stores';
|
||||||
import { ProfileMeta } from '../types';
|
import { ProfileMeta } from '../types';
|
||||||
|
|
||||||
@ -19,7 +20,7 @@ if (props.p === undefined) {
|
|||||||
throw new Error('Invalid ProfileListEntry');
|
throw new Error('Invalid ProfileListEntry');
|
||||||
}
|
}
|
||||||
|
|
||||||
const rename = async (event: KeyboardEvent) => {
|
const renameProfile = async (event: KeyboardEvent) => {
|
||||||
if (event.key !== 'Enter') {
|
if (event.key !== 'Enter') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -32,14 +33,28 @@ const rename = async (event: KeyboardEvent) => {
|
|||||||
typeof event.target.value === 'string'
|
typeof event.target.value === 'string'
|
||||||
) {
|
) {
|
||||||
const value = event.target.value
|
const value = event.target.value
|
||||||
|
.trim()
|
||||||
|
.replaceAll(' ', '-')
|
||||||
.replaceAll('..', '')
|
.replaceAll('..', '')
|
||||||
.replaceAll('\\', '')
|
.replaceAll('\\', '')
|
||||||
.replaceAll('/', '');
|
.replaceAll('/', '');
|
||||||
|
|
||||||
if (value.length > 0) {
|
if (value.length > 0) {
|
||||||
await prf.rename(props.p!, value);
|
await prf.rename(props.p!, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const duplicateProfile = async () => {
|
||||||
|
await invoke('duplicate_profile', { profile: props.p });
|
||||||
|
await prf.reloadList();
|
||||||
|
};
|
||||||
|
|
||||||
|
const deleteProfile = async () => {
|
||||||
|
await invoke('delete_profile', { profile: props.p });
|
||||||
|
await prf.reloadList();
|
||||||
|
await prf.reload();
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@ -60,7 +75,7 @@ const rename = async (event: KeyboardEvent) => {
|
|||||||
<InputText
|
<InputText
|
||||||
:model-value="p!.name"
|
:model-value="p!.name"
|
||||||
@vue:mounted="$event?.el?.focus()"
|
@vue:mounted="$event?.el?.focus()"
|
||||||
@keyup="rename"
|
@keyup="renameProfile"
|
||||||
@focusout="isEditing = false"
|
@focusout="isEditing = false"
|
||||||
>
|
>
|
||||||
</InputText></div
|
</InputText></div
|
||||||
@ -73,7 +88,7 @@ const rename = async (event: KeyboardEvent) => {
|
|||||||
size="small"
|
size="small"
|
||||||
class="self-center ml-2"
|
class="self-center ml-2"
|
||||||
style="width: 2rem; height: 2rem"
|
style="width: 2rem; height: 2rem"
|
||||||
:disabled="true"
|
@click="deleteProfile"
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
rounded
|
rounded
|
||||||
@ -83,7 +98,7 @@ const rename = async (event: KeyboardEvent) => {
|
|||||||
size="small"
|
size="small"
|
||||||
class="self-center"
|
class="self-center"
|
||||||
style="width: 2rem; height: 2rem"
|
style="width: 2rem; height: 2rem"
|
||||||
:disabled="true"
|
@click="duplicateProfile"
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
rounded
|
rounded
|
||||||
|
@ -5,6 +5,7 @@ import Theme from '@primevue/themes/aura';
|
|||||||
import PrimeVue from 'primevue/config';
|
import PrimeVue from 'primevue/config';
|
||||||
import Tooltip from 'primevue/tooltip';
|
import Tooltip from 'primevue/tooltip';
|
||||||
import App from './components/App.vue';
|
import App from './components/App.vue';
|
||||||
|
import { changePrimaryColor } from './util';
|
||||||
|
|
||||||
const pinia = createPinia();
|
const pinia = createPinia();
|
||||||
const app = createApp(App);
|
const app = createApp(App);
|
||||||
@ -17,5 +18,6 @@ app.use(PrimeVue, {
|
|||||||
preset: Preset,
|
preset: Preset,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
changePrimaryColor(null);
|
||||||
app.directive('tooltip', Tooltip);
|
app.directive('tooltip', Tooltip);
|
||||||
app.mount('#app');
|
app.mount('#app');
|
||||||
|
@ -114,12 +114,16 @@ export const usePrfStore = defineStore('prf', () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const reload = async () => {
|
const reload = async () => {
|
||||||
const p: any = await invoke('get_current_profile');
|
const p = (await invoke('get_current_profile')) as any;
|
||||||
if (p['OngekiProfile'] !== undefined) {
|
if (p != null && 'OngekiProfile' in p) {
|
||||||
current.value = { ...p.OngekiProfile, game: 'ongeki' };
|
current.value = { ...p.OngekiProfile, game: 'ongeki' };
|
||||||
|
} else {
|
||||||
|
current.value = null;
|
||||||
}
|
}
|
||||||
if (current.value !== null) {
|
if (current.value !== null) {
|
||||||
changePrimaryColor(current.value.game);
|
changePrimaryColor(current.value.game);
|
||||||
|
} else {
|
||||||
|
changePrimaryColor(null);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -167,9 +171,7 @@ export const usePrfStore = defineStore('prf', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const reloadList = async () => {
|
const reloadList = async () => {
|
||||||
// list.value.splice(0, list.value.length);
|
|
||||||
list.value = (await invoke('list_profiles')) as ProfileMeta[];
|
list.value = (await invoke('list_profiles')) as ProfileMeta[];
|
||||||
console.log(list.value);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const togglePkg = async (pkg: Package | undefined, enable: boolean) => {
|
const togglePkg = async (pkg: Package | undefined, enable: boolean) => {
|
||||||
|
11
src/util.ts
11
src/util.ts
@ -1,8 +1,13 @@
|
|||||||
import { updatePrimaryPalette } from '@primevue/themes';
|
import { updatePrimaryPalette } from '@primevue/themes';
|
||||||
import { Package } from './types';
|
import { Game, Package } from './types';
|
||||||
|
|
||||||
export const changePrimaryColor = (game: 'ongeki' | 'chunithm') => {
|
export const changePrimaryColor = (game: Game | null) => {
|
||||||
const color = game === 'ongeki' ? 'pink' : 'yellow';
|
const color =
|
||||||
|
game === 'ongeki'
|
||||||
|
? 'pink'
|
||||||
|
: game === 'chunithm'
|
||||||
|
? 'yellow'
|
||||||
|
: 'bluegray';
|
||||||
|
|
||||||
updatePrimaryPalette({
|
updatePrimaryPalette({
|
||||||
50: `{${color}.50}`,
|
50: `{${color}.50}`,
|
||||||
|
Reference in New Issue
Block a user