feat: new error banner, qol

This commit is contained in:
2025-04-04 22:14:09 +00:00
parent ca871f069f
commit 93e0a7e313
7 changed files with 112 additions and 26 deletions

View File

@ -63,7 +63,7 @@ pub async fn startline(app: AppHandle, refresh: bool) -> Result<(), String> {
if let Some(p) = &mut appd.profile {
log::debug!("{}", hash);
p.line_up(hash, refresh, app.clone()).await
.map_err(|e| format!("Lineup failed:\n{}", e))?;
.map_err(|e| e.to_string())?;
let app_clone = app.clone();
let p_clone = p.clone();
tauri::async_runtime::spawn(async move {

View File

@ -88,7 +88,9 @@ impl Display {
settings.borrow_mut().resolution = Resolution::new(width, height);
}
display_set.apply()?;
display_set.apply().map_err(
|_| anyhow!("The selected monitor has been disconnected or doesn't support the chosen display mode")
)?;
displayz::refresh()?;
log::debug!("prepare display: done");

View File

@ -1,6 +1,7 @@
<script setup lang="ts">
import { Ref, computed, onMounted, ref } from 'vue';
import Button from 'primevue/button';
import Dialog from 'primevue/dialog';
import InputIcon from 'primevue/inputicon';
import InputText from 'primevue/inputtext';
import Tab from 'primevue/tab';
@ -8,13 +9,19 @@ import TabList from 'primevue/tablist';
import TabPanel from 'primevue/tabpanel';
import TabPanels from 'primevue/tabpanels';
import Tabs from 'primevue/tabs';
import { listen } from '@tauri-apps/api/event';
import ModList from './ModList.vue';
import ModStore from './ModStore.vue';
import OptionList from './OptionList.vue';
import ProfileList from './ProfileList.vue';
import StartButton from './StartButton.vue';
import { invoke } from '../invoke';
import { useClientStore, useGeneralStore, usePkgStore, usePrfStore } from '../stores';
import {
useClientStore,
useGeneralStore,
usePkgStore,
usePrfStore,
} from '../stores';
import { Dirs } from '../types';
const pkg = usePkgStore();
@ -56,35 +63,81 @@ onMounted(async () => {
});
});
const errorVisible = ref(false);
const errorMessage = ref('No error');
const errorHeader = ref('No header');
listen<{ message: string; header: string }>('invoke-error', (event) => {
errorVisible.value = true;
errorMessage.value = event.payload.message;
errorHeader.value = event.payload.header;
});
</script>
<template>
<main :class="client.scaleFactor === 's' ? 'main-scale-s' : client.scaleFactor === 'm' ? 'main-scale-m' : client.scaleFactor === 'l' ? 'main-scale-l' : 'main-scale-xl'">
<main
:class="
client.scaleFactor === 's'
? 'main-scale-s'
: client.scaleFactor === 'm'
? 'main-scale-m'
: client.scaleFactor === 'l'
? 'main-scale-l'
: 'main-scale-xl'
"
>
<Dialog
modal
:visible="errorVisible"
:closable="false /*this shit doesn't work */"
:header="errorHeader"
:style="{ width: '50vw' }"
>
<div class="flex flex-col gap-4">
{{ errorMessage }}
<Button
class="m-auto"
label="A sad state of affairs"
@click="errorVisible = false"
/>
</div>
</Dialog>
<Tabs
lazy
:value="currentTab"
v-on:update:value="(value) => { currentTab = value; }"
v-on:update:value="
(value) => {
currentTab = value;
}
"
class="h-screen"
>
<div class="fixed w-full flex z-100">
<TabList class="grow" :show-navigators="false">
<Tab :value="3"
><div class="pi pi-users"></div
><div class="pi pi-users" v-tooltip="'Profiles'"></div
></Tab>
<Tab :disabled="isProfileDisabled" :value="0"
><div class="pi pi-box"></div
><div
class="pi pi-box"
v-tooltip="'Installed packages'"
></div
></Tab>
<Tab v-if="prf.current?.meta.game === 'chunithm'" :value="4"
><div class="pi pi-ticket"></div
><div class="pi pi-ticket" v-tooltip="'Patches'"></div
></Tab>
<Tab
v-if="pkg.networkStatus === 'online'"
:disabled="isProfileDisabled"
:value="1"
><div class="pi pi-download"></div
><div
class="pi pi-download"
v-tooltip="'Package store'"
></div
></Tab>
<Tab :disabled="isProfileDisabled" :value="2"
><div class="pi pi-cog"></div
><div class="pi pi-cog" v-tooltip="'Settings'"></div
></Tab>
<div class="grow"></div>
@ -96,7 +149,7 @@ onMounted(async () => {
</InputIcon>
<InputText
v-if="currentTab === 2"
style="min-width: 0; width: 25dvw;"
style="min-width: 0; width: 25dvw"
class="self-center"
size="small"
placeholder="Search"
@ -104,7 +157,7 @@ onMounted(async () => {
/>
<InputText
v-else
style="min-width: 0; width: 25dvw;"
style="min-width: 0; width: 25dvw"
class="self-center"
size="small"
placeholder="Search"
@ -119,14 +172,20 @@ onMounted(async () => {
:disabled="true"
/>
<Button
v-if="pkg.networkStatus === 'offline' && !client.offlineMode"
v-if="
pkg.networkStatus === 'offline' &&
!client.offlineMode
"
class="shrink self-center"
icon="pi pi-sync"
size="small"
@click="pkg.fetch(false)"
/>
<Button
v-if="pkg.networkStatus === 'online' && pkg.hasAvailableUpdates"
v-if="
pkg.networkStatus === 'online' &&
pkg.hasAvailableUpdates
"
icon="pi pi-download"
label="UPDATE ALL"
size="small"
@ -161,8 +220,13 @@ onMounted(async () => {
</footer>
</TabPanel>
<TabPanel :value="4">
CHUNITHM patches are not yet implemented.<br />Use
<a href=https://patcher.two-torial.xyz/ target="_blank" style="text-decoration: underline;">patcher.two-torial.xyz</a>
CHUNITHM patches are not implemented yet.<br />Use
<a
href="https://patcher.two-torial.xyz/"
target="_blank"
style="text-decoration: underline"
>patcher.two-torial.xyz</a
>
</TabPanel>
</TabPanels>
<div v-if="currentTab === 5 || currentTab === 3">
@ -196,19 +260,19 @@ body {
}
.main-scale-s {
zoom: 1.0
zoom: 1;
}
.main-scale-m {
zoom: 1.25
zoom: 1.25;
}
.main-scale-l {
zoom: 1.4
zoom: 1.4;
}
.main-scale-xl {
zoom: 1.7
zoom: 1.7;
}
.p-tablist {

View File

@ -1,7 +1,9 @@
<script setup lang="ts">
import Button from 'primevue/button';
import InputText from 'primevue/inputtext';
import * as path from '@tauri-apps/api/path';
import { open } from '@tauri-apps/plugin-dialog';
import { usePrfStore } from '../stores';
const props = defineProps({
placeholder: String,
@ -12,10 +14,25 @@ const props = defineProps({
callback: Function,
});
const prf = usePrfStore();
const filePick = async () => {
const exePath = prf.current?.data.sgt.target;
let defaultPath: string | undefined;
if (
exePath !== undefined &&
exePath.length > 0 &&
props.value !== undefined &&
!(await path.isAbsolute(props.value))
) {
defaultPath = await path.join(exePath, '..');
defaultPath = await path.join(defaultPath, props.value);
defaultPath = await path.join(defaultPath, '..');
}
const res = await open({
multiple: false,
directory: props.directory,
defaultPath,
filters:
props.promptname && props.extension
? [
@ -28,7 +45,7 @@ const filePick = async () => {
});
if (res != null && props.callback !== undefined) {
props.callback(res);
/*path.relative(cfgs.current?.data.exe_dir ?? '', res) */
/*path.relative(prf.current?.data.sgt.target ?? '', res) */
}
};
</script>

View File

@ -7,6 +7,9 @@ const prf = usePrfStore();
</script>
<template>
<div v-if="prf.list.length === 0">
Welcome to STARTLINER! Start by creating a profile.
</div>
<div class="mt-4 flex flex-row flex-wrap align-middle gap-4">
<Button
label="O.N.G.E.K.I. profile"

View File

@ -45,7 +45,7 @@ const updatesModel = computed({
</OptionRow>
<OptionRow
title="Offline mode"
tooltip="Disables the package store. Applies after a restart"
tooltip="Disables the package store. Requires a restart."
>
<ToggleSwitch v-model="offlineModel" />
</OptionRow>

View File

@ -3,7 +3,7 @@ import {
InvokeOptions,
invoke as real_invoke,
} from '@tauri-apps/api/core';
import { message } from '@tauri-apps/plugin-dialog';
import { emit } from '@tauri-apps/api/event';
export const invoke = async <T>(
cmd: string,
@ -14,9 +14,9 @@ export const invoke = async <T>(
return await real_invoke(cmd, args, options);
} catch (e: unknown) {
if (typeof e === 'string') {
await message(`${cmd}: ${e}`, {
title: `Error`,
kind: 'error',
emit('invoke-error', {
message: e,
header: `${cmd} failed`,
});
} else {
console.error(`Unresolved error: ${e}`);