forked from akanyan/STARTLINER
202 lines
5.6 KiB
Vue
202 lines
5.6 KiB
Vue
<script setup lang="ts">
|
|
import { ComputedRef, computed, onMounted, ref } from 'vue';
|
|
import Button from 'primevue/button';
|
|
import Carousel from 'primevue/carousel';
|
|
import Dialog from 'primevue/dialog';
|
|
import { fromKeycode } from '../keyboard';
|
|
import { useClientStore, usePrfStore } from '../stores';
|
|
import { prettyPrint } from '../util';
|
|
import { VueMarkdownIt } from '@f3ve/vue-markdown-it';
|
|
import { useI18n } from 'vue-i18n';
|
|
|
|
const { t } = useI18n();
|
|
|
|
const prf = usePrfStore();
|
|
const client = useClientStore();
|
|
|
|
const props = defineProps({
|
|
visible: Boolean,
|
|
firstTime: Boolean,
|
|
onFinish: Function,
|
|
});
|
|
|
|
interface Datum {
|
|
text: string;
|
|
image: string;
|
|
}
|
|
|
|
const game = computed(() => prf.current?.meta.game);
|
|
|
|
const processText = (s: string) => {
|
|
// Why do I have to do this
|
|
s = s
|
|
.split('\n')
|
|
.map((l) => l.trim())
|
|
.join('\n');
|
|
if (prf.current!.data.keyboard?.data.enabled) {
|
|
const testKey = prf.current!.data.keyboard?.data.test;
|
|
const readable = fromKeycode(testKey);
|
|
if (readable !== null) {
|
|
return s.replace(
|
|
'%TESTMENU%',
|
|
`${readable} or a button on the back of the controller`
|
|
);
|
|
}
|
|
}
|
|
return s.replace('%TESTMENU%', 'a button on the back of the controller');
|
|
};
|
|
|
|
const loadPage = (title: string, messages?: object) => {
|
|
return {
|
|
text: t(`onboarding.${title}`, {
|
|
endlink: '</a>',
|
|
black: '<span class="bg-black text-white">',
|
|
end: '</span>',
|
|
...messages,
|
|
}),
|
|
image: `help-${title}.png`,
|
|
};
|
|
};
|
|
|
|
let systemProcessing: Datum;
|
|
let standardOngeki: Datum;
|
|
let standardChunithm: Datum;
|
|
let lever: Datum;
|
|
let server: Datum;
|
|
let finaleOngeki: Datum;
|
|
let finaleChunithm: Datum;
|
|
|
|
const data: ComputedRef<Datum[]> = computed(() => {
|
|
const res = [];
|
|
|
|
switch (prf.current?.meta.game) {
|
|
case 'ongeki':
|
|
res.push(systemProcessing);
|
|
res.push(standardOngeki);
|
|
res.push(lever);
|
|
res.push(finaleOngeki);
|
|
break;
|
|
case 'chunithm':
|
|
res.push(standardChunithm);
|
|
res.push(server);
|
|
res.push(finaleChunithm);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return res;
|
|
});
|
|
|
|
const context = ref({
|
|
index: 0,
|
|
});
|
|
|
|
onMounted(async () => {
|
|
[standardOngeki, systemProcessing, lever, server, finaleOngeki] =
|
|
await Promise.all([
|
|
loadPage('standard', {
|
|
bigblack: '<div class="p-2 mt-1 mb-1 bg-black text-white">',
|
|
endbig: '</div>',
|
|
}),
|
|
loadPage('ongeki-system-processing'),
|
|
loadPage('ongeki-lever'),
|
|
loadPage('chunithm-server', {
|
|
link: '<a href="https://gitea.tendokyu.moe/Dniel97/SEGAguide/wiki/FAQ#game-is-stuck-at-checking-distribution-server" target="_blank">',
|
|
}),
|
|
loadPage('finale', {
|
|
segaguide:
|
|
'<a href="https://gitea.tendokyu.moe/Dniel97/SEGAguide/wiki/FAQ" target="_blank">',
|
|
twotorial: '<a href="https://two-torial.xyz/" target="_blank">',
|
|
}),
|
|
]);
|
|
standardOngeki = {
|
|
...standardOngeki,
|
|
image: '/help-standard-ongeki.png',
|
|
};
|
|
standardChunithm = {
|
|
...standardOngeki,
|
|
image: '/help-standard-chunithm.png',
|
|
};
|
|
finaleOngeki = {
|
|
...finaleOngeki,
|
|
image: '/help-finale-ongeki.png',
|
|
};
|
|
finaleChunithm = {
|
|
...finaleOngeki,
|
|
image: '/help-finale-chunithm.png',
|
|
};
|
|
});
|
|
|
|
const exitLabel = computed(() => {
|
|
return props.firstTime === true &&
|
|
context.value.index < data.value.length - 1
|
|
? 'Skip'
|
|
: 'Close';
|
|
});
|
|
</script>
|
|
|
|
<template>
|
|
<Dialog
|
|
modal
|
|
:visible="visible"
|
|
:closable="false"
|
|
:header="
|
|
firstTime
|
|
? `It looks like you're running ${game ? prettyPrint(game) : '<game>'} for the first time`
|
|
: `${game ? prettyPrint(game) : '<game>'} help`
|
|
"
|
|
:style="{ width: '760px', scale: client.scaleValue }"
|
|
v-on:show="() => (context.index = 0)"
|
|
>
|
|
<Carousel
|
|
:value="data"
|
|
:num-visible="1"
|
|
:num-scroll="1"
|
|
:context="context"
|
|
v-on:update:page="(p) => (context.index = p)"
|
|
>
|
|
<template #item="slotProps">
|
|
<div class="md-container markdown">
|
|
<vue-markdown-it
|
|
:source="processText(slotProps.data?.text)"
|
|
:options="{
|
|
typographer: true,
|
|
breaks: true,
|
|
html: true,
|
|
}"
|
|
/>
|
|
</div>
|
|
<div
|
|
class="border border-surface-200 dark:border-surface-700 rounded m-2"
|
|
>
|
|
<img :src="slotProps.data.image" />
|
|
</div>
|
|
</template>
|
|
</Carousel>
|
|
<div style="width: 100%; text-align: center">
|
|
<Button
|
|
v-if="context.index < data.length - 1"
|
|
class="m-auto mr-4"
|
|
label="Next"
|
|
@click="() => (context.index += 1)"
|
|
/>
|
|
<Button
|
|
class="m-auto"
|
|
:label="exitLabel"
|
|
@click="() => onFinish && onFinish()"
|
|
/>
|
|
</div>
|
|
</Dialog>
|
|
</template>
|
|
|
|
<style lang="css">
|
|
.p-dialog ::-webkit-scrollbar {
|
|
display: none;
|
|
}
|
|
|
|
.md-container {
|
|
height: 9.5rem;
|
|
}
|
|
</style>
|