feat: the other profile buttons

This commit is contained in:
2025-03-14 00:23:47 +00:00
parent fd27000c05
commit 90ba27c967
11 changed files with 112 additions and 33 deletions

View File

@ -7,7 +7,7 @@ use crate::model::misc::Game;
use crate::pkg::{Package, PkgKey};
use crate::pkg_store::InstallResult;
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::util;
@ -159,24 +159,63 @@ pub async fn rename_profile(
let new_meta = ProfileMeta {
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() {
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
.map_err(|e| format!("Unable to rename: {}", e))?;
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;
if let Some(current) = &mut appd.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;
}
}

View File

@ -51,7 +51,7 @@ impl DownloadHandler {
cache_file_w.sync_all().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)?;

View File

@ -154,6 +154,8 @@ pub async fn run(_args: Vec<String>) {
cmd::init_profile,
cmd::load_profile,
cmd::rename_profile,
cmd::duplicate_profile,
cmd::delete_profile,
cmd::get_current_profile,
cmd::save_current_profile,

View File

@ -25,7 +25,7 @@ pub async fn prepare_packages<'a>(p: &'a impl ProfilePaths, pkgs: &BTreeSet<PkgK
.join("app")
.join("BepInEx");
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");

View File

@ -55,7 +55,7 @@ impl AnyProfile {
pub fn rename(&mut self, name: String) {
match self {
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
}
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
}

View File

@ -3,6 +3,7 @@ use tauri::AppHandle;
use tauri::Emitter;
use std::{collections::BTreeSet, path::PathBuf, process::Stdio};
use crate::model::config::BepInEx;
use crate::profiles::fixed_name;
use crate::util::PathStr;
use crate::{model::{config::{Display, DisplayMode, Network, Segatools}, misc::Game, segatools_base::segatools_base}, pkg::PkgKey, util};
use super::{Profile, ProfileMeta, ProfilePaths};
@ -27,14 +28,10 @@ pub struct OngekiProfile {
impl Profile for OngekiProfile {
fn new(name: String) -> Result<Self> {
let mut fixed_name = name.trim().replace(" ", "-");
while util::profile_config_dir(&Game::Ongeki, &name).exists() {
fixed_name = format!("new-{}", name);
}
let name = fixed_name(&ProfileMeta { name, game: Game::Ongeki }, true);
let p = OngekiProfile {
name: Some(fixed_name),
name: Some(name.clone()),
mods: BTreeSet::new(),
sgt: Segatools::default(),
display: Display::default(),
@ -43,7 +40,8 @@ impl Profile for OngekiProfile {
};
p.save()?;
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)
}
@ -71,7 +69,7 @@ impl Profile for OngekiProfile {
}
std::fs::write(&path, s)
.map_err(|e| anyhow!("error when writing to {:?}: {}", path, e))?;
log::info!("Written to {}", path.to_string_lossy());
log::info!("Written to {:?}", path);
Ok(())
}
@ -79,7 +77,7 @@ impl Profile for OngekiProfile {
async fn start(&self, app: AppHandle) -> Result<()> {
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 amd_builder;

View File

@ -66,15 +66,19 @@ pub fn pkg_dir_of(namespace: &str, name: &str) -> PathBuf {
pkg_dir().join(format!("{}-{}", namespace, name))
}
pub fn copy_recursive(src: &Path, dst: &Path) -> std::io::Result<()> {
std::fs::create_dir_all(&dst).unwrap();
for entry in std::fs::read_dir(src)? {
pub fn copy_directory(src: impl AsRef<Path>, dst: impl AsRef<Path>, recursive: bool) -> std::io::Result<()> {
std::fs::create_dir_all(dst.as_ref()).unwrap();
for entry in std::fs::read_dir(src.as_ref())? {
let entry = entry?;
let meta = entry.metadata()?;
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 {
log::warn!("Skipping directory {:?}", meta);
}
} else {
std::fs::copy(&entry.path(), &dst.join(entry.file_name()))?;
std::fs::copy(&entry.path(), &dst.as_ref().join(entry.file_name()))?;
}
}
Ok(())

View File

@ -4,6 +4,7 @@ import Button from 'primevue/button';
import InputText from 'primevue/inputtext';
import * as path from '@tauri-apps/api/path';
import { open } from '@tauri-apps/plugin-shell';
import { invoke } from '../invoke';
import { useGeneralStore, usePrfStore } from '../stores';
import { ProfileMeta } from '../types';
@ -19,7 +20,7 @@ if (props.p === undefined) {
throw new Error('Invalid ProfileListEntry');
}
const rename = async (event: KeyboardEvent) => {
const renameProfile = async (event: KeyboardEvent) => {
if (event.key !== 'Enter') {
return;
}
@ -32,14 +33,28 @@ const rename = async (event: KeyboardEvent) => {
typeof event.target.value === 'string'
) {
const value = event.target.value
.trim()
.replaceAll(' ', '-')
.replaceAll('..', '')
.replaceAll('\\', '')
.replaceAll('/', '');
if (value.length > 0) {
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>
<template>
@ -60,7 +75,7 @@ const rename = async (event: KeyboardEvent) => {
<InputText
:model-value="p!.name"
@vue:mounted="$event?.el?.focus()"
@keyup="rename"
@keyup="renameProfile"
@focusout="isEditing = false"
>
</InputText></div
@ -73,7 +88,7 @@ const rename = async (event: KeyboardEvent) => {
size="small"
class="self-center ml-2"
style="width: 2rem; height: 2rem"
:disabled="true"
@click="deleteProfile"
/>
<Button
rounded
@ -83,7 +98,7 @@ const rename = async (event: KeyboardEvent) => {
size="small"
class="self-center"
style="width: 2rem; height: 2rem"
:disabled="true"
@click="duplicateProfile"
/>
<Button
rounded

View File

@ -5,6 +5,7 @@ import Theme from '@primevue/themes/aura';
import PrimeVue from 'primevue/config';
import Tooltip from 'primevue/tooltip';
import App from './components/App.vue';
import { changePrimaryColor } from './util';
const pinia = createPinia();
const app = createApp(App);
@ -17,5 +18,6 @@ app.use(PrimeVue, {
preset: Preset,
},
});
changePrimaryColor(null);
app.directive('tooltip', Tooltip);
app.mount('#app');

View File

@ -114,12 +114,16 @@ export const usePrfStore = defineStore('prf', () => {
);
const reload = async () => {
const p: any = await invoke('get_current_profile');
if (p['OngekiProfile'] !== undefined) {
const p = (await invoke('get_current_profile')) as any;
if (p != null && 'OngekiProfile' in p) {
current.value = { ...p.OngekiProfile, game: 'ongeki' };
} else {
current.value = null;
}
if (current.value !== null) {
changePrimaryColor(current.value.game);
} else {
changePrimaryColor(null);
}
};
@ -167,9 +171,7 @@ export const usePrfStore = defineStore('prf', () => {
};
const reloadList = async () => {
// list.value.splice(0, list.value.length);
list.value = (await invoke('list_profiles')) as ProfileMeta[];
console.log(list.value);
};
const togglePkg = async (pkg: Package | undefined, enable: boolean) => {

View File

@ -1,8 +1,13 @@
import { updatePrimaryPalette } from '@primevue/themes';
import { Package } from './types';
import { Game, Package } from './types';
export const changePrimaryColor = (game: 'ongeki' | 'chunithm') => {
const color = game === 'ongeki' ? 'pink' : 'yellow';
export const changePrimaryColor = (game: Game | null) => {
const color =
game === 'ongeki'
? 'pink'
: game === 'chunithm'
? 'yellow'
: 'bluegray';
updatePrimaryPalette({
50: `{${color}.50}`,