players stay in run all timers mode
This commit is contained in:
@@ -1,10 +1,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
:class="['player-list-item flex items-center justify-between p-3 my-2 rounded-lg shadow cursor-pointer',
|
:class="['player-list-item flex items-center justify-between p-3 my-2 rounded-lg shadow cursor-pointer transition-all duration-200 ease-in-out',
|
||||||
itemBgClass,
|
itemStateClasses,
|
||||||
{ 'opacity-60': player.isSkipped,
|
{ 'opacity-50 filter grayscale contrast-75': player.isSkipped },
|
||||||
'animate-pulsePositive': player.isTimerRunning && player.currentTimerSec >=0,
|
{ 'opacity-75': !player.isTimerRunning && !player.isSkipped }
|
||||||
}]"
|
]"
|
||||||
@click="handleTap"
|
@click="handleTap"
|
||||||
>
|
>
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
@@ -12,17 +12,24 @@
|
|||||||
v-if="player.avatar"
|
v-if="player.avatar"
|
||||||
:src="player.avatar"
|
:src="player.avatar"
|
||||||
alt="Player Avatar"
|
alt="Player Avatar"
|
||||||
class="w-12 h-12 rounded-full object-cover mr-3 border-2 flex-shrink-0"
|
class="w-12 h-12 rounded-full object-cover mr-3 border-2"
|
||||||
:class="player.isSkipped ? 'border-gray-500 filter grayscale' : 'border-blue-400 dark:border-blue-300'"
|
:class="player.isSkipped ? 'border-gray-400 dark:border-gray-600' :
|
||||||
|
player.isTimerRunning ? 'border-green-400 dark:border-green-500' :
|
||||||
|
'border-yellow-400 dark:border-yellow-500'"
|
||||||
/>
|
/>
|
||||||
<DefaultAvatarIcon
|
<DefaultAvatarIcon
|
||||||
v-else
|
v-else
|
||||||
class="w-12 h-12 rounded-full object-cover mr-3 border-2 text-gray-400 dark:text-gray-500 bg-gray-200 dark:bg-gray-700 p-1 flex-shrink-0"
|
class="w-12 h-12 rounded-full object-cover mr-3 border-2 p-1"
|
||||||
:class="player.isSkipped ? 'border-gray-500 filter grayscale !text-gray-300 dark:!text-gray-600' : 'border-blue-400 dark:border-blue-300'"
|
:class="player.isSkipped ? 'text-gray-400 bg-gray-200 border-gray-400 dark:text-gray-600 dark:bg-gray-700 dark:border-gray-600' :
|
||||||
|
player.isTimerRunning ? 'text-green-600 bg-green-100 border-green-400 dark:text-green-300 dark:bg-green-800 dark:border-green-500' :
|
||||||
|
'text-yellow-600 bg-yellow-50 border-yellow-400 dark:text-yellow-300 dark:bg-yellow-800 dark:border-yellow-500'"
|
||||||
/>
|
/>
|
||||||
<div>
|
<div>
|
||||||
<h3 class="text-lg font-medium">{{ player.name }}</h3>
|
<h3 class="text-lg font-medium" :class="{'text-gray-500 dark:text-gray-400': !player.isTimerRunning && !player.isSkipped}">
|
||||||
|
{{ player.name }}
|
||||||
|
</h3>
|
||||||
<p v-if="player.isSkipped" class="text-xs text-red-500 dark:text-red-400">SKIPPED</p>
|
<p v-if="player.isSkipped" class="text-xs text-red-500 dark:text-red-400">SKIPPED</p>
|
||||||
|
<p v-else-if="!player.isTimerRunning" class="text-xs text-yellow-600 dark:text-yellow-400">Paused</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col items-end">
|
<div class="flex flex-col items-end">
|
||||||
@@ -31,6 +38,7 @@
|
|||||||
:is-negative="player.currentTimerSec < 0"
|
:is-negative="player.currentTimerSec < 0"
|
||||||
:is-pulsating="player.isTimerRunning"
|
:is-pulsating="player.isTimerRunning"
|
||||||
class="text-2xl"
|
class="text-2xl"
|
||||||
|
:class="{'opacity-80': !player.isTimerRunning && !player.isSkipped}"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -51,20 +59,22 @@ const props = defineProps({
|
|||||||
const emit = defineEmits(['tapped']);
|
const emit = defineEmits(['tapped']);
|
||||||
|
|
||||||
const handleTap = () => {
|
const handleTap = () => {
|
||||||
|
// Allow tapping only if not skipped, to pause/resume their timer
|
||||||
if (!props.player.isSkipped) {
|
if (!props.player.isSkipped) {
|
||||||
emit('tapped');
|
emit('tapped');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const itemBgClass = computed(() => {
|
const itemStateClasses = computed(() => {
|
||||||
if (props.player.isSkipped) {
|
if (props.player.isSkipped) {
|
||||||
return 'bg-gray-200 dark:bg-gray-800 border border-gray-300 dark:border-gray-700';
|
return 'bg-gray-200 dark:bg-gray-800 border border-gray-300 dark:border-gray-700';
|
||||||
}
|
}
|
||||||
if (props.player.isTimerRunning) {
|
if (props.player.isTimerRunning) {
|
||||||
return props.player.currentTimerSec < 0
|
return props.player.currentTimerSec < 0
|
||||||
? 'bg-red-100 dark:bg-red-900/80 border border-red-300 dark:border-red-700'
|
? 'bg-red-100 dark:bg-red-900/80 border border-red-400 dark:border-red-600 animate-pulsePositive' // Pulsate background if running positive
|
||||||
: 'bg-green-50 dark:bg-green-800/70 border border-green-200 dark:border-green-700';
|
: 'bg-green-50 dark:bg-green-900/70 border border-green-400 dark:border-green-600 animate-pulsePositive';
|
||||||
}
|
}
|
||||||
return 'bg-white dark:bg-gray-700 border border-gray-200 dark:border-gray-600 hover:bg-gray-50 dark:hover:bg-gray-600';
|
// Paused state
|
||||||
|
return 'bg-yellow-50 dark:bg-yellow-900/50 border border-yellow-300 dark:border-yellow-700 hover:bg-yellow-100 dark:hover:bg-yellow-900/70';
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex flex-col safe-area-height safe-area-padding overflow-hidden" :class="{'dark': theme === 'dark'}">
|
<div class="flex flex-col h-screen overflow-hidden" :class="{'dark': theme === 'dark'}">
|
||||||
|
<!-- Header Bar -->
|
||||||
<header class="p-3 bg-gray-100 dark:bg-gray-800 shadow-md flex justify-between items-center shrink-0">
|
<header class="p-3 bg-gray-100 dark:bg-gray-800 shadow-md flex justify-between items-center shrink-0">
|
||||||
<!-- ... header content ... -->
|
|
||||||
<div class="flex items-center space-x-2">
|
<div class="flex items-center space-x-2">
|
||||||
<button @click="navigateToSetup" class="btn btn-secondary text-sm">
|
<button @click="navigateToSetup" class="btn btn-secondary text-sm">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 inline mr-1" viewBox="0 0 20 20" fill="currentColor"><path d="M10.707 2.293a1 1 0 00-1.414 0l-7 7a1 1 0 001.414 1.414L4 10.414V17a1 1 0 001 1h2a1 1 0 001-1v-2a1 1 0 011-1h2a1 1 0 011 1v2a1 1 0 001 1h2a1 1 0 001-1v-6.586l.293.293a1 1 0 001.414-1.414l-7-7z" /></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 inline mr-1" viewBox="0 0 20 20" fill="currentColor"><path d="M10.707 2.293a1 1 0 00-1.414 0l-7 7a1 1 0 001.414 1.414L4 10.414V17a1 1 0 001 1h2a1 1 0 001-1v-2a1 1 0 011-1h2a1 1 0 011 1v2a1 1 0 001 1h2a1 1 0 001-1v-6.586l.293.293a1 1 0 001.414-1.414l-7-7z" /></svg>
|
||||||
@@ -28,9 +28,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<!-- Main content area should allow its children to use its full height -->
|
|
||||||
<main class="flex-grow overflow-hidden flex flex-col" ref="gameArea">
|
<main class="flex-grow overflow-hidden flex flex-col" ref="gameArea">
|
||||||
<!-- Normal Mode: This div itself needs to take full height of 'main' -->
|
<!-- Normal Mode -->
|
||||||
<div v-if="gameMode === 'normal' && currentPlayer && nextPlayer" class="h-full flex flex-col">
|
<div v-if="gameMode === 'normal' && currentPlayer && nextPlayer" class="h-full flex flex-col">
|
||||||
<PlayerDisplay
|
<PlayerDisplay
|
||||||
:player="currentPlayer"
|
:player="currentPlayer"
|
||||||
@@ -52,20 +51,21 @@
|
|||||||
<!-- All Timers Running Mode -->
|
<!-- All Timers Running Mode -->
|
||||||
<div v-if="gameMode === 'allTimers'" class="p-4 h-full flex flex-col">
|
<div v-if="gameMode === 'allTimers'" class="p-4 h-full flex flex-col">
|
||||||
<div class="mb-4 flex justify-start items-center">
|
<div class="mb-4 flex justify-start items-center">
|
||||||
<h2 class="text-2xl font-semibold">All Timers Running</h2>
|
<h2 class="text-2xl font-semibold">All Timers Mode</h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-grow overflow-y-auto space-y-2">
|
<div class="flex-grow overflow-y-auto space-y-2">
|
||||||
|
<!-- Use playersToListInAllTimersMode -->
|
||||||
<PlayerListItem
|
<PlayerListItem
|
||||||
v-for="(player) in playersInAllTimersView"
|
v-for="(player) in playersToListInAllTimersMode"
|
||||||
:key="player.id"
|
:key="player.id"
|
||||||
:player="player"
|
:player="player"
|
||||||
@tapped="() => handlePlayerTapAllTimers(indexInFullList(player.id))"
|
@tapped="() => handlePlayerTapAllTimers(indexInFullList(player.id))"
|
||||||
/>
|
/>
|
||||||
<p v-if="playersInAllTimersView.length === 0 && players.length > 0 && anyTimerCouldRun" class="text-center text-gray-500 dark:text-gray-400 py-6">
|
<p v-if="playersToListInAllTimersMode.length === 0 && players.length > 0 && anyTimerCouldRun" class="text-center text-gray-500 dark:text-gray-400 py-6">
|
||||||
All active timers paused.
|
All players are skipped or an issue occurred.
|
||||||
</p>
|
</p>
|
||||||
<p v-if="playersInAllTimersView.length === 0 && players.length > 0 && !anyTimerCouldRun" class="text-center text-gray-500 dark:text-gray-400 py-6">
|
<p v-if="playersToListInAllTimersMode.length === 0 && players.length > 0 && !anyTimerCouldRun" class="text-center text-gray-500 dark:text-gray-400 py-6">
|
||||||
No players eligible to run. (All skipped or issue)
|
All players are skipped.
|
||||||
</p>
|
</p>
|
||||||
<p v-if="players.length === 0" class="text-center text-gray-500 dark:text-gray-400 py-6">
|
<p v-if="players.length === 0" class="text-center text-gray-500 dark:text-gray-400 py-6">
|
||||||
No players to display.
|
No players to display.
|
||||||
@@ -98,14 +98,15 @@ const gameRunning = computed(() => store.getters.gameRunning);
|
|||||||
|
|
||||||
let timerInterval = null;
|
let timerInterval = null;
|
||||||
|
|
||||||
const playersInAllTimersView = computed(() => {
|
// Shows all non-skipped players when in 'allTimers' mode.
|
||||||
if (gameMode.value === 'allTimers') {
|
const playersToListInAllTimersMode = computed(() => {
|
||||||
if (!players.value) return [];
|
if (gameMode.value === 'allTimers' && players.value) {
|
||||||
return players.value.filter(p => p.isTimerRunning && !p.isSkipped);
|
return players.value.filter(p => !p.isSkipped);
|
||||||
}
|
}
|
||||||
return [];
|
return [];
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// This computed is still used for audio logic and auto-revert
|
||||||
const anyTimerRunningInAllMode = computed(() => {
|
const anyTimerRunningInAllMode = computed(() => {
|
||||||
if (!players.value) return false;
|
if (!players.value) return false;
|
||||||
return players.value.some(p => p.isTimerRunning && !p.isSkipped);
|
return players.value.some(p => p.isTimerRunning && !p.isSkipped);
|
||||||
@@ -122,7 +123,8 @@ const indexInFullList = (playerId) => {
|
|||||||
return players.value.findIndex(p => p.id === playerId);
|
return players.value.findIndex(p => p.id === playerId);
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
// ... (onMounted, onUnmounted, watchers, navigation, and other methods remain the same)
|
||||||
|
onMounted(async () => {
|
||||||
if (!players.value || players.value.length < 2) {
|
if (!players.value || players.value.length < 2) {
|
||||||
router.push({ name: 'Setup' });
|
router.push({ name: 'Setup' });
|
||||||
return;
|
return;
|
||||||
@@ -132,9 +134,8 @@ onMounted(() => {
|
|||||||
store.dispatch('tick');
|
store.dispatch('tick');
|
||||||
}, 1000);
|
}, 1000);
|
||||||
|
|
||||||
// Attempt to acquire wake lock if game is already running (e.g., page refresh)
|
|
||||||
if (gameRunning.value) {
|
if (gameRunning.value) {
|
||||||
WakeLockService.request();
|
await WakeLockService.request();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -142,30 +143,19 @@ onUnmounted(async () => {
|
|||||||
clearInterval(timerInterval);
|
clearInterval(timerInterval);
|
||||||
AudioService.stopContinuousTick();
|
AudioService.stopContinuousTick();
|
||||||
AudioService.cancelPassTurnSound();
|
AudioService.cancelPassTurnSound();
|
||||||
await WakeLockService.release(); // Release wake lock when leaving the game view
|
await WakeLockService.release();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Watch gameRunning state to acquire/release wake lock
|
|
||||||
watch(gameRunning, async (isRunning) => {
|
watch(gameRunning, async (isRunning) => {
|
||||||
if (isRunning) {
|
if (isRunning) {
|
||||||
await WakeLockService.request();
|
await WakeLockService.request();
|
||||||
} else {
|
} else {
|
||||||
// Don't release immediately on pause, only if all timers stop and stay stopped.
|
if (!WakeLockService.isActive()) return;
|
||||||
// For simplicity, we'll rely on onUnmounted for now or if explicitly navigating away.
|
|
||||||
// If you want to release on extended pause, you'd need more logic here.
|
|
||||||
// Perhaps only release if gameMode is normal and current player is paused,
|
|
||||||
// or if allTimers mode and ALL are paused.
|
|
||||||
// For now, aggressive release on any gameRunning=false might be too much.
|
|
||||||
// Let's only release it when navigating away or explicitly stopped.
|
|
||||||
// The browser will also release it if the tab is hidden.
|
|
||||||
// Let's make it simpler: release if game isn't running
|
|
||||||
if (!WakeLockService.isActive()) return; // Avoid releasing if not active
|
|
||||||
// A short delay before releasing to avoid flickering if quickly pausing/resuming
|
|
||||||
setTimeout(async () => {
|
setTimeout(async () => {
|
||||||
if (!store.getters.gameRunning && WakeLockService.isActive()) { // Check again before releasing
|
if (!store.getters.gameRunning && WakeLockService.isActive()) {
|
||||||
await WakeLockService.release();
|
await WakeLockService.release();
|
||||||
}
|
}
|
||||||
}, 3000); // e.g., release after 3 seconds of no activity
|
}, 3000);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -212,7 +202,7 @@ watch(() => currentPlayer.value?.isTimerRunning, (isRunning, wasRunning) => {
|
|||||||
|
|
||||||
|
|
||||||
const navigateToSetup = async () => {
|
const navigateToSetup = async () => {
|
||||||
await WakeLockService.release(); // Release before navigating
|
await WakeLockService.release();
|
||||||
const isAnyTimerActive = store.getters.gameRunning;
|
const isAnyTimerActive = store.getters.gameRunning;
|
||||||
if (isAnyTimerActive) {
|
if (isAnyTimerActive) {
|
||||||
if (window.confirm('Game is active. Going to Setup will pause all timers. Continue?')) {
|
if (window.confirm('Game is active. Going to Setup will pause all timers. Continue?')) {
|
||||||
@@ -225,21 +215,15 @@ const navigateToSetup = async () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const navigateToInfo = async () => {
|
const navigateToInfo = async () => {
|
||||||
await WakeLockService.release(); // Release before navigating
|
await WakeLockService.release();
|
||||||
if (store.getters.gameRunning) {
|
if (store.getters.gameRunning) {
|
||||||
store.commit('PAUSE_ALL_TIMERS'); // Good practice to pause if navigating away
|
store.commit('PAUSE_ALL_TIMERS');
|
||||||
}
|
}
|
||||||
router.push({ name: 'Info' });
|
router.push({ name: 'Info' });
|
||||||
};
|
};
|
||||||
|
|
||||||
const toggleMute = () => {
|
const toggleMute = () => { store.dispatch('setMuted', !isMuted.value); };
|
||||||
store.dispatch('setMuted', !isMuted.value);
|
const handleCurrentPlayerTap = () => { store.dispatch('toggleCurrentPlayerTimerNormalMode'); };
|
||||||
};
|
|
||||||
|
|
||||||
const handleCurrentPlayerTap = () => {
|
|
||||||
store.dispatch('toggleCurrentPlayerTimerNormalMode');
|
|
||||||
};
|
|
||||||
|
|
||||||
const handlePassTurn = () => {
|
const handlePassTurn = () => {
|
||||||
if(currentPlayer.value && !currentPlayer.value.isSkipped) {
|
if(currentPlayer.value && !currentPlayer.value.isSkipped) {
|
||||||
AudioService.cancelPassTurnSound();
|
AudioService.cancelPassTurnSound();
|
||||||
@@ -252,29 +236,26 @@ const handlePassTurn = () => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
const handlePlayerTapAllTimers = (playerIndex) => { store.dispatch('togglePlayerTimerAllTimersMode', playerIndex); };
|
||||||
|
const switchToAllTimersMode = () => { store.dispatch('switchToAllTimersMode'); };
|
||||||
|
const switchToNormalMode = () => { store.dispatch('switchToNormalMode'); };
|
||||||
|
|
||||||
const handlePlayerTapAllTimers = (playerIndex) => {
|
// Auto-revert logic
|
||||||
store.dispatch('togglePlayerTimerAllTimersMode', playerIndex);
|
|
||||||
};
|
|
||||||
|
|
||||||
const switchToAllTimersMode = () => {
|
|
||||||
store.dispatch('switchToAllTimersMode');
|
|
||||||
};
|
|
||||||
|
|
||||||
const switchToNormalMode = () => {
|
|
||||||
store.dispatch('switchToNormalMode');
|
|
||||||
};
|
|
||||||
|
|
||||||
watch(anyTimerRunningInAllMode, (anyRunning) => {
|
watch(anyTimerRunningInAllMode, (anyRunning) => {
|
||||||
|
// Only revert if we are IN allTimers mode and NO timers are running
|
||||||
if (gameMode.value === 'allTimers' && !anyRunning && players.value && players.value.length > 0) {
|
if (gameMode.value === 'allTimers' && !anyRunning && players.value && players.value.length > 0) {
|
||||||
const nonSkippedPlayersExist = players.value.some(p => !p.isSkipped);
|
const nonSkippedPlayersExist = players.value.some(p => !p.isSkipped);
|
||||||
if (nonSkippedPlayersExist) {
|
if (nonSkippedPlayersExist) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
// Double check condition before switching, state might change rapidly
|
||||||
if(gameMode.value === 'allTimers' && !store.getters.players.some(p => p.isTimerRunning && !p.isSkipped)){
|
if(gameMode.value === 'allTimers' && !store.getters.players.some(p => p.isTimerRunning && !p.isSkipped)){
|
||||||
|
console.log("All timers paused in AllTimersMode, reverting to Normal Mode.");
|
||||||
store.dispatch('switchToNormalMode');
|
store.dispatch('switchToNormalMode');
|
||||||
}
|
}
|
||||||
}, 200);
|
}, 250); // A small delay to prevent flickering if a timer is immediately restarted
|
||||||
} else {
|
} else {
|
||||||
|
// All players are skipped, so stay in all timers mode but paused.
|
||||||
|
console.log("All players skipped in AllTimersMode, staying paused.");
|
||||||
AudioService.stopContinuousTick();
|
AudioService.stopContinuousTick();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user