disable screen lock
This commit is contained in:
@@ -17,6 +17,7 @@ Game enthusiasts who play turn-based games (board games, tabletop RPGs, card gam
|
|||||||
* **JavaScript:** For application logic, timer functionality, and event handling.
|
* **JavaScript:** For application logic, timer functionality, and event handling.
|
||||||
* **Web Audio API:** For audio feedback (ticking sounds, alerts).
|
* **Web Audio API:** For audio feedback (ticking sounds, alerts).
|
||||||
* **Browser API:** For capturing Players' photo.
|
* **Browser API:** For capturing Players' photo.
|
||||||
|
* **Screen Wake Lock API:** For preventing of the screen lock in a PWA.
|
||||||
* **Local Storage/IndexedDB:** For persistent storage of player data, timer states, and settings.
|
* **Local Storage/IndexedDB:** For persistent storage of player data, timer states, and settings.
|
||||||
* **Service Worker:** Essential for PWA functionality (offline access, push notifications - potential future feature).
|
* **Service Worker:** Essential for PWA functionality (offline access, push notifications - potential future feature).
|
||||||
* **Manifest File:** Defines the PWA's metadata (name, icons, theme color).
|
* **Manifest File:** Defines the PWA's metadata (name, icons, theme color).
|
||||||
@@ -125,7 +126,7 @@ For an enhanced tactile experience, Nexus Timer supports Smart Buttons based on
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
## Building for the production
|
## Building for the production
|
||||||
Navigate to your project directory on the server
|
Navigate to the projects' directory on the server
|
||||||
```bash
|
```bash
|
||||||
cd /virt
|
cd /virt
|
||||||
```
|
```
|
||||||
@@ -154,7 +155,7 @@ Use the editor to create or overwrite the service:
|
|||||||
```bash
|
```bash
|
||||||
sudo systemctl edit --force --full virt-nexus-timer.service
|
sudo systemctl edit --force --full virt-nexus-timer.service
|
||||||
```
|
```
|
||||||
Paste the contents from virt-nexus-timer.service, then save and exit.
|
Paste the content from `systemd/virt-nexus-timer.service`, then save and exit.
|
||||||
|
|
||||||
Enable on system boot and start the service
|
Enable on system boot and start the service
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
@@ -41,14 +41,13 @@
|
|||||||
"Pass Turn / My Pause" Hotkey:
|
"Pass Turn / My Pause" Hotkey:
|
||||||
</label>
|
</label>
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<!-- Display field - not an input anymore, triggers overlay -->
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
id="playerHotkeyDisplay"
|
id="playerHotkeyDisplay"
|
||||||
@click="startCapturePlayerHotkey"
|
@click="startCapturePlayerHotkey"
|
||||||
class="input-base w-12 h-8 text-center font-mono text-lg p-0 cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-700"
|
class="input-base w-12 h-8 font-mono text-lg p-0 cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-700 flex items-center justify-center"
|
||||||
>
|
>
|
||||||
{{ editablePlayer.hotkey ? editablePlayer.hotkey.toUpperCase() : '-' }}
|
<span>{{ editablePlayer.hotkey ? editablePlayer.hotkey.toUpperCase() : '-' }}</span>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
|
|||||||
66
src/services/WakeLockService.js
Normal file
66
src/services/WakeLockService.js
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
// src/services/WakeLockService.js
|
||||||
|
let wakeLock = null;
|
||||||
|
let wakeLockActive = false;
|
||||||
|
|
||||||
|
const requestWakeLock = async () => {
|
||||||
|
if ('wakeLock' in navigator && !wakeLockActive) {
|
||||||
|
try {
|
||||||
|
wakeLock = await navigator.wakeLock.request('screen');
|
||||||
|
wakeLockActive = true;
|
||||||
|
console.log('Screen Wake Lock activated.');
|
||||||
|
|
||||||
|
wakeLock.addEventListener('release', () => {
|
||||||
|
console.log('Screen Wake Lock was released.');
|
||||||
|
wakeLockActive = false;
|
||||||
|
wakeLock = null; // Clear the reference
|
||||||
|
// Optionally, re-request if it was released unexpectedly and should be active
|
||||||
|
// For now, we'll let it be re-requested manually by the app logic
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`Failed to acquire Screen Wake Lock: ${err.name}, ${err.message}`);
|
||||||
|
wakeLock = null;
|
||||||
|
wakeLockActive = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.warn('Screen Wake Lock API not supported or already active.');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const releaseWakeLock = async () => {
|
||||||
|
if (wakeLock && wakeLockActive) {
|
||||||
|
try {
|
||||||
|
await wakeLock.release();
|
||||||
|
// The 'release' event listener on wakeLock itself will set wakeLockActive = false and wakeLock = null
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`Failed to release Screen Wake Lock: ${err.name}, ${err.message}`);
|
||||||
|
// Even if release fails, mark as inactive to allow re-request
|
||||||
|
wakeLock = null;
|
||||||
|
wakeLockActive = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// console.log('No active Screen Wake Lock to release or already released.');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Handle visibility changes to re-acquire lock if necessary
|
||||||
|
const handleVisibilityChange = () => {
|
||||||
|
if (wakeLock !== null && document.visibilityState === 'visible') {
|
||||||
|
// If we had a wake lock and the page became visible again,
|
||||||
|
// it might have been released by the browser. Try to re-acquire.
|
||||||
|
// This behavior is usually handled automatically by the browser with the 'release' event
|
||||||
|
// but can be a fallback. For now, we rely on manual re-request.
|
||||||
|
// console.log('Page visible, checking wake lock status.');
|
||||||
|
} else if (document.visibilityState === 'hidden' && wakeLockActive) {
|
||||||
|
// The browser usually releases the wake lock when tab is hidden.
|
||||||
|
// Our 'release' event listener should handle this.
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
document.addEventListener('visibilitychange', handleVisibilityChange);
|
||||||
|
// document.addEventListener('fullscreenchange', handleVisibilityChange); // Also useful for fullscreen
|
||||||
|
|
||||||
|
export const WakeLockService = {
|
||||||
|
request: requestWakeLock,
|
||||||
|
release: releaseWakeLock,
|
||||||
|
isActive: () => wakeLockActive,
|
||||||
|
};
|
||||||
@@ -83,6 +83,7 @@ import { useRouter } from 'vue-router';
|
|||||||
import PlayerDisplay from '../components/PlayerDisplay.vue';
|
import PlayerDisplay from '../components/PlayerDisplay.vue';
|
||||||
import PlayerListItem from '../components/PlayerListItem.vue';
|
import PlayerListItem from '../components/PlayerListItem.vue';
|
||||||
import { AudioService } from '../services/AudioService';
|
import { AudioService } from '../services/AudioService';
|
||||||
|
import { WakeLockService } from '../services/WakeLockService';
|
||||||
|
|
||||||
const store = useStore();
|
const store = useStore();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
@@ -93,7 +94,7 @@ const currentPlayer = computed(() => store.getters.currentPlayer);
|
|||||||
const nextPlayer = computed(() => store.getters.nextPlayer);
|
const nextPlayer = computed(() => store.getters.nextPlayer);
|
||||||
const gameMode = computed(() => store.getters.gameMode);
|
const gameMode = computed(() => store.getters.gameMode);
|
||||||
const isMuted = computed(() => store.getters.isMuted);
|
const isMuted = computed(() => store.getters.isMuted);
|
||||||
// const gameRunning = computed(() => store.getters.gameRunning);
|
const gameRunning = computed(() => store.getters.gameRunning);
|
||||||
|
|
||||||
let timerInterval = null;
|
let timerInterval = null;
|
||||||
|
|
||||||
@@ -130,12 +131,42 @@ onMounted(() => {
|
|||||||
timerInterval = setInterval(() => {
|
timerInterval = setInterval(() => {
|
||||||
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) {
|
||||||
|
WakeLockService.request();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
onUnmounted(() => {
|
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
|
||||||
|
});
|
||||||
|
|
||||||
|
// Watch gameRunning state to acquire/release wake lock
|
||||||
|
watch(gameRunning, async (isRunning) => {
|
||||||
|
if (isRunning) {
|
||||||
|
await WakeLockService.request();
|
||||||
|
} else {
|
||||||
|
// Don't release immediately on pause, only if all timers stop and stay stopped.
|
||||||
|
// 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 () => {
|
||||||
|
if (!store.getters.gameRunning && WakeLockService.isActive()) { // Check again before releasing
|
||||||
|
await WakeLockService.release();
|
||||||
|
}
|
||||||
|
}, 3000); // e.g., release after 3 seconds of no activity
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
watch(gameMode, (newMode) => {
|
watch(gameMode, (newMode) => {
|
||||||
@@ -180,7 +211,8 @@ watch(() => currentPlayer.value?.isTimerRunning, (isRunning, wasRunning) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
const navigateToSetup = () => {
|
const navigateToSetup = async () => {
|
||||||
|
await WakeLockService.release(); // Release before navigating
|
||||||
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?')) {
|
||||||
@@ -192,9 +224,10 @@ const navigateToSetup = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const navigateToInfo = () => {
|
const navigateToInfo = async () => {
|
||||||
|
await WakeLockService.release(); // Release before navigating
|
||||||
if (store.getters.gameRunning) {
|
if (store.getters.gameRunning) {
|
||||||
store.commit('PAUSE_ALL_TIMERS');
|
store.commit('PAUSE_ALL_TIMERS'); // Good practice to pause if navigating away
|
||||||
}
|
}
|
||||||
router.push({ name: 'Info' });
|
router.push({ name: 'Info' });
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -72,9 +72,9 @@
|
|||||||
type="button"
|
type="button"
|
||||||
id="globalHotkeyStopPauseDisplay"
|
id="globalHotkeyStopPauseDisplay"
|
||||||
@click="startCaptureGlobalHotkey('stopPause')"
|
@click="startCaptureGlobalHotkey('stopPause')"
|
||||||
class="input-base w-12 h-8 text-center font-mono text-lg p-0 cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-700"
|
class="input-base w-12 h-8 font-mono text-lg p-0 cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-700 flex items-center justify-center"
|
||||||
>
|
>
|
||||||
{{ globalHotkeyStopPauseDisplay || '-' }}
|
<span>{{ globalHotkeyStopPauseDisplay || '-' }}</span>
|
||||||
</button>
|
</button>
|
||||||
<button type="button" @click="clearGlobalHotkey('stopPause')" class="text-xs text-blue-500 hover:underline ml-2 px-2 py-1 rounded hover:bg-gray-100 dark:hover:bg-gray-600">
|
<button type="button" @click="clearGlobalHotkey('stopPause')" class="text-xs text-blue-500 hover:underline ml-2 px-2 py-1 rounded hover:bg-gray-100 dark:hover:bg-gray-600">
|
||||||
Clear
|
Clear
|
||||||
@@ -91,9 +91,9 @@
|
|||||||
type="button"
|
type="button"
|
||||||
id="globalHotkeyRunAllDisplay"
|
id="globalHotkeyRunAllDisplay"
|
||||||
@click="startCaptureGlobalHotkey('runAll')"
|
@click="startCaptureGlobalHotkey('runAll')"
|
||||||
class="input-base w-12 h-8 text-center font-mono text-lg p-0 cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-700"
|
class="input-base w-12 h-8 font-mono text-lg p-0 cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-700 flex items-center justify-center"
|
||||||
>
|
>
|
||||||
{{ globalHotkeyRunAllDisplay || '-' }}
|
<span>{{ globalHotkeyRunAllDisplay || '-' }}</span>
|
||||||
</button>
|
</button>
|
||||||
<button type="button" @click="clearGlobalHotkey('runAll')" class="text-xs text-blue-500 hover:underline ml-2 px-2 py-1 rounded hover:bg-gray-100 dark:hover:bg-gray-600">
|
<button type="button" @click="clearGlobalHotkey('runAll')" class="text-xs text-blue-500 hover:underline ml-2 px-2 py-1 rounded hover:bg-gray-100 dark:hover:bg-gray-600">
|
||||||
Clear
|
Clear
|
||||||
|
|||||||
Reference in New Issue
Block a user