302 lines
9.4 KiB
Vue
302 lines
9.4 KiB
Vue
<script setup lang="ts">
|
|
import { Ref, ref } from 'vue';
|
|
import Button from 'primevue/button';
|
|
import Dialog from 'primevue/dialog';
|
|
import Select from 'primevue/select';
|
|
import SelectButton from 'primevue/selectbutton';
|
|
import ToggleSwitch from 'primevue/toggleswitch';
|
|
import * as path from '@tauri-apps/api/path';
|
|
import { open } from '@tauri-apps/plugin-dialog';
|
|
import ProfileListEntry from './ProfileListEntry.vue';
|
|
import { invoke } from '../invoke';
|
|
import { useClientStore, useGeneralStore, usePrfStore } from '../stores';
|
|
import { useI18n } from 'vue-i18n';
|
|
|
|
const { t } = useI18n();
|
|
|
|
const prf = usePrfStore();
|
|
const client = useClientStore();
|
|
const general = useGeneralStore();
|
|
|
|
const hasChunithm = ref(false);
|
|
const exportVisible = ref(false);
|
|
const exportKeychip = ref(false);
|
|
const files = new Set<string>();
|
|
|
|
(async () => {
|
|
hasChunithm.value = (
|
|
(await invoke('list_platform_capabilities')) as string[]
|
|
).includes('chunithm');
|
|
})();
|
|
|
|
const fileList = [
|
|
'aime.txt',
|
|
'inohara.cfg',
|
|
'saekawa.toml',
|
|
'mu3.ini',
|
|
'segatools-base.ini',
|
|
'pre.sh',
|
|
'pre.bat',
|
|
'post.sh',
|
|
'post.bat',
|
|
];
|
|
|
|
const diagnosticList = {
|
|
ongeki: ['mu3.ini', 'segatools-base.ini'],
|
|
chunithm: ['segatools-base.ini'],
|
|
};
|
|
|
|
const diagnostic = ref(false);
|
|
|
|
const exportTemplate = async () => {
|
|
const fl = [...files.values()];
|
|
exportVisible.value = false;
|
|
await invoke('export_profile', {
|
|
exportKeychip: exportKeychip.value,
|
|
isDiagnostic: diagnostic.value,
|
|
files:
|
|
diagnostic.value === true
|
|
? diagnosticList[prf.current!.meta.game]
|
|
: fl,
|
|
});
|
|
await invoke('open_file', {
|
|
path: await path.join(await general.configDir, 'exports'),
|
|
});
|
|
};
|
|
|
|
const fileListCurrent: Ref<string[]> = ref([]);
|
|
|
|
const recalcFileList = async () => {
|
|
const res: string[] = [];
|
|
files.clear();
|
|
for (const f of fileList) {
|
|
const p = await path.join(await prf.configDir, f);
|
|
if (await invoke('file_exists', { path: p })) {
|
|
res.push(f);
|
|
files.add(f);
|
|
}
|
|
}
|
|
fileListCurrent.value = res;
|
|
};
|
|
|
|
const openExportDialog = async () => {
|
|
await recalcFileList();
|
|
exportVisible.value = true;
|
|
};
|
|
|
|
const importPick = async () => {
|
|
const res = await open({
|
|
multiple: false,
|
|
directory: false,
|
|
filters: [
|
|
{
|
|
name: t('profile.template'),
|
|
extensions: ['zip'],
|
|
},
|
|
],
|
|
});
|
|
if (res != null) {
|
|
await invoke('import_profile', { path: res });
|
|
await prf.reloadList();
|
|
}
|
|
};
|
|
</script>
|
|
|
|
<template>
|
|
<Dialog
|
|
modal
|
|
:visible="exportVisible"
|
|
:closable="false /*this shit doesn't work */"
|
|
:header="`${t('profile.export')} ${prf.current?.meta.name}`"
|
|
:style="{ width: '330px', scale: client.scaleValue }"
|
|
>
|
|
<div class="flex flex-col gap-4">
|
|
<div class="flex flex-col items-center">
|
|
<SelectButton
|
|
v-model="diagnostic"
|
|
:options="[
|
|
{
|
|
title: t('profile.standardExport'),
|
|
value: false,
|
|
},
|
|
{
|
|
title: t('profile.diagnostic'),
|
|
value: true,
|
|
},
|
|
]"
|
|
:allow-empty="false"
|
|
option-label="title"
|
|
option-value="value"
|
|
>
|
|
</SelectButton>
|
|
</div>
|
|
<div class="flex flex-row">
|
|
<div class="grow">{{ t('profile.export') }} keychip</div>
|
|
<ToggleSwitch :disabled="diagnostic" v-model="exportKeychip" />
|
|
</div>
|
|
<div class="flex flex-row" v-for="f in fileListCurrent">
|
|
<div class="grow">{{ t('profile.export') }} {{ f }}</div>
|
|
<ToggleSwitch
|
|
:disabled="diagnostic"
|
|
:model-value="true"
|
|
@update:model-value="
|
|
(v) => {
|
|
if (v === true) {
|
|
files.add(f);
|
|
} else {
|
|
files.delete(f);
|
|
}
|
|
}
|
|
"
|
|
/>
|
|
</div>
|
|
<div style="width: 100%; text-align: center">
|
|
<Button
|
|
class="m-auto mr-3"
|
|
style="width: 80px"
|
|
label="OK"
|
|
@click="() => exportTemplate()"
|
|
/>
|
|
<Button
|
|
class="m-auto"
|
|
style="width: 80px"
|
|
label="Cancel"
|
|
@click="() => (exportVisible = false)"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</Dialog>
|
|
<div style="float: left">
|
|
<div v-if="prf.list.length === 0">
|
|
{{ t('profile.welcome') }}
|
|
</div>
|
|
<div class="mt-4 flex flex-row flex-wrap align-middle gap-4">
|
|
<Button
|
|
:label="t('profile.create', { game: t('game.ongeki') })"
|
|
icon="pi pi-file-plus"
|
|
class="ongeki-button profile-button"
|
|
@click="() => prf.create('ongeki')"
|
|
/>
|
|
<Button
|
|
v-if="hasChunithm"
|
|
:label="t('profile.create', { game: t('game.chunithm') })"
|
|
icon="pi pi-file-plus"
|
|
class="chunithm-button profile-button"
|
|
@click="() => prf.create('chunithm')"
|
|
/>
|
|
</div>
|
|
<div class="mt-4 flex flex-row flex-wrap align-middle gap-4">
|
|
<Button
|
|
:label="t('profile.importTemplate')"
|
|
icon="pi pi-file-import"
|
|
class="import-button profile-button"
|
|
@click="() => importPick()"
|
|
/>
|
|
<Button
|
|
:disabled="prf.current === null"
|
|
:label="t('profile.exportTemplate')"
|
|
icon="pi pi-file-export"
|
|
class="profile-button"
|
|
@click="() => openExportDialog()"
|
|
/>
|
|
</div>
|
|
<div class="mt-12 flex flex-col flex-wrap align-middle gap-4">
|
|
<div v-for="p in prf.list">
|
|
<ProfileListEntry :p="p" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div style="float: right" class="mr-5 mt-3 flex flex-col gap-4 items-end">
|
|
<div>
|
|
<div class="pi pi-language mr-2"></div>
|
|
<Select
|
|
:model-value="client.locale"
|
|
@update:model-value="async (v) => await client.setLocale(v)"
|
|
style="width: 200px"
|
|
:options="[
|
|
{ title: 'English', value: 'en' },
|
|
{ title: 'Español', value: 'es' },
|
|
{ title: '한국어', value: 'ko' },
|
|
{ title: '日本語', value: 'ja' },
|
|
{ title: 'Polski', value: 'pl' },
|
|
{ title: '简体中文', value: 'zh-Hans' },
|
|
]"
|
|
size="small"
|
|
option-label="title"
|
|
option-value="value"
|
|
></Select>
|
|
</div>
|
|
<SelectButton
|
|
style="height: 50px"
|
|
v-model="client.scaleModel"
|
|
:options="[
|
|
{ title: 'S', size: '0.8em', value: 's' },
|
|
{ title: 'M', size: '1.0em', value: 'm' },
|
|
{ title: 'L', size: '1.2em', value: 'l' },
|
|
{ title: 'XL', size: '1.4em', value: 'xl' },
|
|
]"
|
|
:allow-empty="false"
|
|
option-label="title"
|
|
option-value="value"
|
|
><template #option="slotProps">
|
|
<div :style="{ fontSize: slotProps.option.size }">
|
|
{{ slotProps.option.title }}
|
|
</div>
|
|
</template></SelectButton
|
|
>
|
|
<SelectButton
|
|
style="height: 50px"
|
|
:model-value="client.theme"
|
|
@update:model-value="(v) => client.setTheme(v)"
|
|
:options="[
|
|
{ title: 'System', value: 'system', icon: 'pi pi-home' },
|
|
{ title: 'Light', value: 'light', icon: 'pi pi-sun' },
|
|
{ title: 'Dark', value: 'dark', icon: 'pi pi-moon' },
|
|
]"
|
|
:allow-empty="false"
|
|
option-label="title"
|
|
option-value="value"
|
|
><template #option="slotProps">
|
|
<div :class="slotProps.option.icon"></div> </template
|
|
></SelectButton>
|
|
</div>
|
|
</template>
|
|
|
|
<style>
|
|
.profile-button {
|
|
width: 14em;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.ongeki-button {
|
|
background-color: var(--p-pink-400) !important;
|
|
border-color: var(--p-pink-400) !important;
|
|
}
|
|
|
|
.ongeki-button:hover,
|
|
.ongeki-button:active {
|
|
background-color: var(--p-pink-300) !important;
|
|
border-color: var(--p-pink-300) !important;
|
|
}
|
|
|
|
.chunithm-button {
|
|
background-color: var(--p-yellow-400) !important;
|
|
border-color: var(--p-yellow-400) !important;
|
|
}
|
|
.chunithm-button:hover,
|
|
.chunithm-button:active {
|
|
background-color: var(--p-yellow-300) !important;
|
|
border-color: var(--p-yellow-300) !important;
|
|
}
|
|
|
|
.import-button {
|
|
background-color: var(--p-purple-400) !important;
|
|
border-color: var(--p-purple-400) !important;
|
|
}
|
|
.import-button:hover,
|
|
.import-button:active {
|
|
background-color: var(--p-purple-300) !important;
|
|
border-color: var(--p-purple-300) !important;
|
|
}
|
|
</style>
|