mirror of
https://github.com/ericasch6/scrum-poker-internal.git
synced 2026-10-13 16:32:09 +00:00
Implement instant websocket room updates
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { useForm } from '@inertiajs/vue3';
|
||||
import { useHttp } from '@inertiajs/vue3';
|
||||
import { Eye, RotateCcw, Trash2 } from '@lucide/vue';
|
||||
|
||||
import { nextRound, reset as resetRound, reveal } from '@/routes/room';
|
||||
@@ -7,26 +7,29 @@ import type { RoomModeratorControlsProps } from '@/types/room';
|
||||
|
||||
const props = defineProps<RoomModeratorControlsProps>();
|
||||
|
||||
const revealForm = useForm<{ room?: string }>({});
|
||||
const nextRoundForm = useForm<{ room?: string }>({});
|
||||
const resetForm = useForm<{ room?: string }>({});
|
||||
const revealRequest = useHttp<{ room?: string }>(
|
||||
() => reveal(props.roomId),
|
||||
{},
|
||||
);
|
||||
const nextRoundRequest = useHttp<{ room?: string }>(
|
||||
() => nextRound(props.roomId),
|
||||
{},
|
||||
);
|
||||
const resetRequest = useHttp<{ room?: string }>(
|
||||
() => resetRound(props.roomId),
|
||||
{},
|
||||
);
|
||||
|
||||
function revealCards(): void {
|
||||
revealForm.submit(reveal(props.roomId), {
|
||||
preserveScroll: true,
|
||||
});
|
||||
void revealRequest.submit();
|
||||
}
|
||||
|
||||
function startNextRound(): void {
|
||||
nextRoundForm.submit(nextRound(props.roomId), {
|
||||
preserveScroll: true,
|
||||
});
|
||||
void nextRoundRequest.submit();
|
||||
}
|
||||
|
||||
function resetVotes(): void {
|
||||
resetForm.submit(resetRound(props.roomId), {
|
||||
preserveScroll: true,
|
||||
});
|
||||
void resetRequest.submit();
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -35,7 +38,7 @@ function resetVotes(): void {
|
||||
<button
|
||||
type="button"
|
||||
class="inline-flex items-center gap-2 rounded-xl border border-slate-600 bg-slate-800 px-4 py-2.5 font-semibold text-slate-200 transition hover:bg-slate-700 disabled:cursor-not-allowed disabled:opacity-40"
|
||||
:disabled="resetForm.processing || !hasVotes"
|
||||
:disabled="resetRequest.processing || !hasVotes"
|
||||
@click="resetVotes"
|
||||
>
|
||||
<Trash2 class="size-4" />
|
||||
@@ -45,18 +48,18 @@ function resetVotes(): void {
|
||||
|
||||
<div class="moderator-controls">
|
||||
<p
|
||||
v-if="revealForm.errors.room || nextRoundForm.errors.room"
|
||||
v-if="revealRequest.errors.room || nextRoundRequest.errors.room"
|
||||
class="max-w-64 rounded-lg bg-rose-950/90 px-3 py-2 text-right text-sm text-rose-200 shadow-lg"
|
||||
role="alert"
|
||||
>
|
||||
{{ revealForm.errors.room ?? nextRoundForm.errors.room }}
|
||||
{{ revealRequest.errors.room ?? nextRoundRequest.errors.room }}
|
||||
</p>
|
||||
|
||||
<button
|
||||
v-if="!revealed"
|
||||
type="button"
|
||||
class="inline-flex items-center gap-2 rounded-xl border border-slate-300 bg-slate-100 px-5 py-2.5 font-semibold text-slate-900 transition hover:bg-white disabled:opacity-50"
|
||||
:disabled="revealForm.processing"
|
||||
:disabled="revealRequest.processing"
|
||||
@click="revealCards"
|
||||
>
|
||||
<Eye class="size-4" />
|
||||
@@ -67,7 +70,7 @@ function resetVotes(): void {
|
||||
v-else
|
||||
type="button"
|
||||
class="inline-flex items-center gap-2 rounded-xl border border-slate-300 bg-slate-100 px-5 py-2.5 font-semibold text-slate-900 transition hover:bg-white disabled:opacity-50"
|
||||
:disabled="nextRoundForm.processing"
|
||||
:disabled="nextRoundRequest.processing"
|
||||
@click="startNextRound"
|
||||
>
|
||||
<RotateCcw class="size-4" />
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
<script setup lang="ts">
|
||||
import { useForm } from '@inertiajs/vue3';
|
||||
import { useHttp } from '@inertiajs/vue3';
|
||||
import { Keyboard } from '@lucide/vue';
|
||||
import { useEventListener } from '@vueuse/core';
|
||||
import { watch } from 'vue';
|
||||
|
||||
import { store as storeVote } from '@/routes/room/vote';
|
||||
import type { RoomVoteDeckProps } from '@/types/room';
|
||||
import type { RoomVoteDeckEmits, RoomVoteDeckProps } from '@/types/room';
|
||||
|
||||
const props = defineProps<RoomVoteDeckProps>();
|
||||
const emit = defineEmits<RoomVoteDeckEmits>();
|
||||
|
||||
const cardShortcuts = [
|
||||
'1',
|
||||
@@ -25,29 +26,32 @@ const cardShortcuts = [
|
||||
),
|
||||
];
|
||||
|
||||
const voteForm = useForm({
|
||||
const voteRequest = useHttp(() => storeVote(props.roomId), {
|
||||
value: props.currentVote ?? '',
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.currentVote,
|
||||
(value) => {
|
||||
if (!voteForm.processing) {
|
||||
voteForm.value = value ?? '';
|
||||
if (!voteRequest.processing) {
|
||||
voteRequest.value = value ?? '';
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
function submitVote(value: string): void {
|
||||
if (props.revealed || voteForm.processing) {
|
||||
if (props.revealed || voteRequest.processing) {
|
||||
return;
|
||||
}
|
||||
|
||||
voteForm.value = value;
|
||||
voteForm.submit(storeVote(props.roomId), {
|
||||
preserveScroll: true,
|
||||
const previousVote = props.currentVote;
|
||||
|
||||
voteRequest.value = value;
|
||||
emit('vote-changed', value);
|
||||
void voteRequest.submit({
|
||||
onError: () => {
|
||||
voteForm.value = props.currentVote ?? '';
|
||||
voteRequest.value = previousVote ?? '';
|
||||
emit('vote-changed', previousVote);
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -71,7 +75,7 @@ function handleKeyboardVote(event: KeyboardEvent): void {
|
||||
if (
|
||||
!props.presenterMode ||
|
||||
props.revealed ||
|
||||
voteForm.processing ||
|
||||
voteRequest.processing ||
|
||||
event.repeat ||
|
||||
event.altKey ||
|
||||
event.ctrlKey ||
|
||||
@@ -118,10 +122,10 @@ useEventListener('keydown', handleKeyboardVote);
|
||||
class="deck-card"
|
||||
:class="{
|
||||
'is-selected':
|
||||
!presenterMode && voteForm.value === card.key,
|
||||
!presenterMode && voteRequest.value === card.key,
|
||||
}"
|
||||
:disabled="voteForm.processing"
|
||||
:aria-pressed="voteForm.value === card.key"
|
||||
:disabled="voteRequest.processing"
|
||||
:aria-pressed="voteRequest.value === card.key"
|
||||
:aria-label="`Karte ${card.label} wählen`"
|
||||
@click="submitVote(card.key)"
|
||||
>
|
||||
@@ -138,11 +142,11 @@ useEventListener('keydown', handleKeyboardVote);
|
||||
</div>
|
||||
|
||||
<p
|
||||
v-if="voteForm.errors.value"
|
||||
v-if="voteRequest.errors.value"
|
||||
class="mt-3 text-sm text-rose-300"
|
||||
role="alert"
|
||||
>
|
||||
{{ voteForm.errors.value }}
|
||||
{{ voteRequest.errors.value }}
|
||||
</p>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user