122 lines
3.2 KiB
Vue
122 lines
3.2 KiB
Vue
<template>
|
|
<v-app :theme="theme">
|
|
<v-app-bar :elevation="2">
|
|
<v-app-bar-title>SHOTGUN</v-app-bar-title>
|
|
<template #append>
|
|
<v-btn :icon="themeIcon" @click="toggleDark()"></v-btn>
|
|
</template>
|
|
</v-app-bar>
|
|
<v-main>
|
|
<Game v-if="playing" :game="game" :dealer="dealer" @action="(evt: Action) => action(evt)" />
|
|
<v-container v-else-if="loading" class="fill-height">
|
|
<v-row class="d-flex justify-center">
|
|
<v-progress-circular class="pa-8" indeterminate size="128"></v-progress-circular>
|
|
</v-row>
|
|
</v-container>
|
|
<v-container v-else>
|
|
<TheLanding />
|
|
<v-row v-if="!loadingMe && !me" class="d-flex justify-center">
|
|
<TheLogin />
|
|
</v-row>
|
|
<v-row v-else-if="!loadingMe" class="d-flex justify-center">
|
|
<v-btn @click="clickPlay" class="mx-4" color="primary">Play</v-btn>
|
|
<v-btn class="mx-4">Log Out</v-btn>
|
|
</v-row>
|
|
</v-container>
|
|
</v-main>
|
|
</v-app>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { useDark, useToggle, useWebSocket } from '@vueuse/core';
|
|
import { computed, onMounted, ref, watchEffect } from 'vue';
|
|
import type { Action, Game, GameStart } from '@/lib/game';
|
|
|
|
const dark = useDark();
|
|
const toggleDark = useToggle(dark);
|
|
const theme = computed(() => dark.value ? 'dark' : 'light');
|
|
const themeIcon = computed(() => dark.value ? 'mdi-moon-waxing-crescent' : 'mdi-white-balance-sunny');
|
|
|
|
const { status, data, send, open } = useWebSocket<string>(`wss://${window.location.host}/queue`, {
|
|
immediate: false,
|
|
heartbeat: {
|
|
interval: 1000,
|
|
message: '{"action":"ping"}',
|
|
},
|
|
onDisconnected: (ws, evt) => {
|
|
console.warn('lost connection', {evt});
|
|
game.value = null;
|
|
dealer.value = null;
|
|
}
|
|
});
|
|
const game = ref<Game | null>(null);
|
|
const dealer = ref<boolean | null>(null);
|
|
watchEffect(() => {
|
|
console.log('data', data.value);
|
|
if (data.value == null) {
|
|
game.value = null;
|
|
dealer.value = null;
|
|
if (window.location.pathname.includes('game/')) {
|
|
history.replaceState(null, '', window.origin);
|
|
}
|
|
return;
|
|
}
|
|
const m = JSON.parse(data.value) as Game | GameStart;
|
|
if ('id' in m) {
|
|
// Game start.
|
|
dealer.value = m.dealer;
|
|
history.replaceState(null, '', `${window.origin}/game/${m.id}`);
|
|
return;
|
|
}
|
|
// Game state update.
|
|
game.value = m as Game;
|
|
});
|
|
|
|
const playing = computed(() => game.value != null);
|
|
const loading = computed(() => status.value === 'CONNECTING' || status.value === 'OPEN' && !playing.value);
|
|
|
|
function clickPlay() {
|
|
open();
|
|
}
|
|
|
|
const testGame = ref<Game>({
|
|
players: [
|
|
{ hp: 4, items: ['🔍', '🔪', '', '', '', '', '', ''] },
|
|
{ hp: 3, items: ['👮', '🚬', '', '', '', '', '', ''] },
|
|
],
|
|
round: 1,
|
|
dealer: true,
|
|
damage: 1,
|
|
previous: null,
|
|
live: 3,
|
|
blank: 4,
|
|
});
|
|
|
|
function action(evt: Action) {
|
|
console.log('send action', evt);
|
|
send(JSON.stringify(action));
|
|
}
|
|
|
|
const loadingMe = ref(true);
|
|
const me = ref<string | null>(null);
|
|
onMounted(async () => {
|
|
loadingMe.value = true;
|
|
try {
|
|
const resp = await fetch('/user/me', {
|
|
method: 'GET',
|
|
mode: 'same-origin',
|
|
credentials: 'same-origin',
|
|
})
|
|
loadingMe.value = false;
|
|
if (!resp.ok) {
|
|
me.value = null;
|
|
return;
|
|
}
|
|
const { id } = await resp.json();
|
|
me.value = id as string;
|
|
} catch {
|
|
me.value = null;
|
|
}
|
|
});
|
|
</script>
|