mirror of
https://github.com/Pandipipas/scoreko-dev.git
synced 2026-06-06 03:32:06 +00:00
Add commentary panel and Tekken-inspired OBS overlay
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
<script setup lang="ts">
|
||||
import { useCommentaryStore } from '../stores/commentary';
|
||||
|
||||
const commentaryStore = useCommentaryStore();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="commentary-panel">
|
||||
<div class="row items-center q-mb-md">
|
||||
<div class="text-h4">
|
||||
Commentary
|
||||
</div>
|
||||
<QSpace />
|
||||
<QBtn
|
||||
color="secondary"
|
||||
outline
|
||||
icon="swap_horiz"
|
||||
label="Intercambiar lados"
|
||||
@click="commentaryStore.swapCommentators"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="row q-col-gutter-lg">
|
||||
<div class="col-12 col-md-6">
|
||||
<QCard
|
||||
flat
|
||||
bordered
|
||||
>
|
||||
<QCardSection>
|
||||
<div class="text-subtitle1 text-weight-bold">
|
||||
Left side
|
||||
</div>
|
||||
</QCardSection>
|
||||
<QSeparator />
|
||||
<QCardSection>
|
||||
<QInput
|
||||
v-model="commentaryStore.leftCommentator"
|
||||
label="Commentator"
|
||||
dense
|
||||
outlined
|
||||
/>
|
||||
</QCardSection>
|
||||
</QCard>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-md-6">
|
||||
<QCard
|
||||
flat
|
||||
bordered
|
||||
>
|
||||
<QCardSection>
|
||||
<div class="text-subtitle1 text-weight-bold">
|
||||
Right side
|
||||
</div>
|
||||
</QCardSection>
|
||||
<QSeparator />
|
||||
<QCardSection>
|
||||
<QInput
|
||||
v-model="commentaryStore.rightCommentator"
|
||||
label="Commentator"
|
||||
dense
|
||||
outlined
|
||||
/>
|
||||
</QCardSection>
|
||||
</QCard>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.commentary-panel {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,84 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import { commentaryReplicant } from '../../../browser_shared/replicants';
|
||||
import type { Schemas } from '../../../types';
|
||||
|
||||
type Commentary = Schemas.Commentary;
|
||||
|
||||
const defaultCommentary: Commentary = {
|
||||
leftCommentator: '',
|
||||
rightCommentator: '',
|
||||
};
|
||||
|
||||
const normalizeCommentary = (input: unknown): Commentary => {
|
||||
const candidate = typeof input === 'object' && input !== null ? (input as Record<string, unknown>) : {};
|
||||
return {
|
||||
leftCommentator: typeof candidate.leftCommentator === 'string' ? candidate.leftCommentator : '',
|
||||
rightCommentator: typeof candidate.rightCommentator === 'string' ? candidate.rightCommentator : '',
|
||||
};
|
||||
};
|
||||
|
||||
export const useCommentaryStore = defineStore('commentary', () => {
|
||||
const commentary = ref<Commentary>({ ...defaultCommentary });
|
||||
const replicant = commentaryReplicant;
|
||||
const isApplyingReplicant = ref(false);
|
||||
|
||||
watch(
|
||||
() => replicant?.data,
|
||||
(value) => {
|
||||
if (!value) {
|
||||
return;
|
||||
}
|
||||
isApplyingReplicant.value = true;
|
||||
commentary.value = normalizeCommentary(value);
|
||||
isApplyingReplicant.value = false;
|
||||
},
|
||||
{ deep: true, immediate: true },
|
||||
);
|
||||
|
||||
watch(
|
||||
commentary,
|
||||
(value) => {
|
||||
if (isApplyingReplicant.value || !replicant) {
|
||||
return;
|
||||
}
|
||||
replicant.data = normalizeCommentary(value);
|
||||
replicant.save();
|
||||
},
|
||||
{ deep: true, flush: 'sync' },
|
||||
);
|
||||
|
||||
const leftCommentator = computed({
|
||||
get: () => commentary.value.leftCommentator,
|
||||
set: (value: string) => {
|
||||
commentary.value = {
|
||||
...commentary.value,
|
||||
leftCommentator: value,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
const rightCommentator = computed({
|
||||
get: () => commentary.value.rightCommentator,
|
||||
set: (value: string) => {
|
||||
commentary.value = {
|
||||
...commentary.value,
|
||||
rightCommentator: value,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
const swapCommentators = () => {
|
||||
commentary.value = {
|
||||
leftCommentator: commentary.value.rightCommentator,
|
||||
rightCommentator: commentary.value.leftCommentator,
|
||||
};
|
||||
};
|
||||
|
||||
return {
|
||||
commentary,
|
||||
leftCommentator,
|
||||
rightCommentator,
|
||||
swapCommentators,
|
||||
};
|
||||
});
|
||||
@@ -1,6 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { useHead } from '@unhead/vue';
|
||||
import BracketPanel from '../components/BracketPanel.vue';
|
||||
import CommentaryPanel from '../components/CommentaryPanel.vue';
|
||||
import ScoreboardPanel from '../components/ScoreboardPanel.vue';
|
||||
|
||||
defineOptions({ name: 'DashboardView' });
|
||||
@@ -12,14 +13,25 @@ useHead({ title: 'Dashboard' });
|
||||
<QPage class="q-pa-lg">
|
||||
<div class="row q-col-gutter-lg items-start dashboard-panels q-mt-lg">
|
||||
<div class="col-12 col-lg-6">
|
||||
<QCard
|
||||
bordered
|
||||
class="dashboard-panel-card"
|
||||
>
|
||||
<QCardSection class="dashboard-panel-content">
|
||||
<ScoreboardPanel />
|
||||
</QCardSection>
|
||||
</QCard>
|
||||
<div class="column q-gutter-lg">
|
||||
<QCard
|
||||
bordered
|
||||
class="dashboard-panel-card"
|
||||
>
|
||||
<QCardSection class="dashboard-panel-content">
|
||||
<ScoreboardPanel />
|
||||
</QCardSection>
|
||||
</QCard>
|
||||
|
||||
<QCard
|
||||
bordered
|
||||
class="dashboard-panel-card"
|
||||
>
|
||||
<QCardSection class="dashboard-panel-content">
|
||||
<CommentaryPanel />
|
||||
</QCardSection>
|
||||
</QCard>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-lg-6">
|
||||
<QCard
|
||||
|
||||
Reference in New Issue
Block a user