Fix: Timer not working on Apple devices, Sound not preloaded, Sound
played multiple times
This commit is contained in:
parent
f418bb8543
commit
2ae2684567
4 changed files with 114 additions and 49 deletions
0
README.md
Normal file
0
README.md
Normal file
|
@ -98,7 +98,7 @@ class SyncedTimer
|
||||||
if (($this->_basepath !== '' && strpos($_SERVER['REQUEST_URI'], $this->_basepath) === false) || $_SERVER['REQUEST_URI'] === $this->_basepath)
|
if (($this->_basepath !== '' && strpos($_SERVER['REQUEST_URI'], $this->_basepath) === false) || $_SERVER['REQUEST_URI'] === $this->_basepath)
|
||||||
$this->_path = "/";
|
$this->_path = "/";
|
||||||
else
|
else
|
||||||
$this->_path = str_replace($this->_basepath, "", $_SERVER['REQUEST_URI']);
|
$this->_path = str_replace($this->_basepath, "", parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH));
|
||||||
}
|
}
|
||||||
|
|
||||||
private function _redirect($path)
|
private function _redirect($path)
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
document.addEventListener('alpine:init' , () => {
|
document.addEventListener('alpine:init', () => {
|
||||||
console.log("Synced-timer is starting")
|
console.log("Synced-timer is starting")
|
||||||
|
|
||||||
Alpine.store("siteData", {
|
Alpine.store("siteData", {
|
||||||
lastResponse: null,
|
lastResponse: null,
|
||||||
currentData: null,
|
currentData: null,
|
||||||
time: '00:00:00',
|
time: '00:00:00',
|
||||||
header: '...',
|
header: '...',
|
||||||
|
|
||||||
|
@ -14,24 +14,24 @@ document.addEventListener('alpine:init' , () => {
|
||||||
init() {
|
init() {
|
||||||
Alpine.effect(() => {
|
Alpine.effect(() => {
|
||||||
const response = this.lastResponse
|
const response = this.lastResponse
|
||||||
if(!response) return;
|
if (!response) return;
|
||||||
|
|
||||||
if(response.status !== 200) {
|
if (response.status !== 200) {
|
||||||
this.header = `Error: ${response.status}`
|
this.header = `Error: ${response.status}`
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.currentData = response.data
|
this.currentData = response.data
|
||||||
Alpine.store("audioPlayback").isDisabledByServer = !this.currentData.soundEnabled
|
Alpine.store("audioPlayback").isDisabledByServer = !this.currentData.soundEnabled
|
||||||
this.header = this.currentData.header
|
this.header = this.currentData.header
|
||||||
})
|
})
|
||||||
|
|
||||||
setInterval(() => {
|
setInterval(() => {
|
||||||
if(!this.currentData) return;
|
if (!this.currentData) return;
|
||||||
|
|
||||||
const passedSeconds = parseInt(((performance.now()-this.timeOffsetToServer) / 1000) - this.currentData.startedAt);
|
const passedSeconds = parseInt(((performance.now() - this.timeOffsetToServer) / 1000) - this.currentData.startedAt);
|
||||||
let remaningSeconds;
|
let remaningSeconds;
|
||||||
if(this.currentData.repeatEnabled) {
|
if (this.currentData.repeatEnabled) {
|
||||||
remaningSeconds = parseInt(this.currentData.time * 60 - (passedSeconds % (this.currentData.time * 60)));
|
remaningSeconds = parseInt(this.currentData.time * 60 - (passedSeconds % (this.currentData.time * 60)));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -47,26 +47,38 @@ document.addEventListener('alpine:init' , () => {
|
||||||
scheduleNextRequest() {
|
scheduleNextRequest() {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
let requestUrl;
|
let requestUrl;
|
||||||
const requestId = Date.now();
|
// Add a request Id to be able to identify this request later on
|
||||||
fetch(`/api/${username}#${requestId}`)
|
fetch(`/api/${username}?id=${parseInt(performance.now())}`)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
requestUrl = response.url
|
requestUrl = response.url
|
||||||
return response.json()
|
return response.json()
|
||||||
})
|
})
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
if(data && data.status === 200) {
|
this.handleRequestFinished(data, requestUrl)
|
||||||
const performanceEntry = performance.getEntriesByName(`${requestUrl}#${requestId}`)[0];
|
|
||||||
console.log(performanceEntry)
|
this.lastResponse = data
|
||||||
const requestDuration = performanceEntry.responseEnd - performanceEntry.requestStart;
|
this.scheduleNextRequest()
|
||||||
this.timeOffsetToServer = performanceEntry.responseEnd - data.data.currentServerTime + (requestDuration / 2);
|
});
|
||||||
console.log(requestDuration, "offset: ", this.timeOffsetToServer)
|
|
||||||
}
|
|
||||||
|
|
||||||
this.lastResponse = data
|
|
||||||
this.scheduleNextRequest()
|
|
||||||
});
|
|
||||||
}, 1000)
|
}, 1000)
|
||||||
}
|
},
|
||||||
|
|
||||||
|
handleRequestFinished(data, requestName) {
|
||||||
|
if (!data || data.status !== 200) return;
|
||||||
|
|
||||||
|
// identify request using its id
|
||||||
|
const performanceEntry = performance.getEntriesByName(requestName)[0];
|
||||||
|
if (!performanceEntry) {
|
||||||
|
// retry
|
||||||
|
console.warn("Could not find performance entry, discarding...")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(performanceEntry)
|
||||||
|
const requestDuration = performanceEntry.responseEnd - performanceEntry.requestStart;
|
||||||
|
this.timeOffsetToServer = performanceEntry.responseEnd - data.data.currentServerTime + (requestDuration / 2);
|
||||||
|
console.log(requestDuration, "offset: ", this.timeOffsetToServer)
|
||||||
|
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
Alpine.store("audioPlayback", {
|
Alpine.store("audioPlayback", {
|
||||||
|
@ -75,13 +87,19 @@ document.addEventListener('alpine:init' , () => {
|
||||||
isDisabledByUser: false,
|
isDisabledByUser: false,
|
||||||
isDisabledByServer: true,
|
isDisabledByServer: true,
|
||||||
audioPlayer: null,
|
audioPlayer: null,
|
||||||
|
replayLocked: false,
|
||||||
|
sounds: {
|
||||||
|
silence: '/static/sound/silence.mp3',
|
||||||
|
countdown: '/static/sound/countdown.mp3',
|
||||||
|
beep: '/static/sound/beep.mp3'
|
||||||
|
},
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
this.audioPlayer = new Audio('/static/sound/silence.mp3');
|
this.audioPlayer = new Audio('/static/sound/silence.mp3');
|
||||||
|
|
||||||
this.testPermission()
|
this.testPermission()
|
||||||
const audioPlaybackStore = this
|
const audioPlaybackStore = this
|
||||||
|
|
||||||
Alpine.bind('audioPlaybackEnableButton', () => ({
|
Alpine.bind('audioPlaybackEnableButton', () => ({
|
||||||
'@click'() {
|
'@click'() {
|
||||||
audioPlaybackStore.testPermission()
|
audioPlaybackStore.testPermission()
|
||||||
|
@ -95,15 +113,19 @@ document.addEventListener('alpine:init' , () => {
|
||||||
}))
|
}))
|
||||||
|
|
||||||
Alpine.effect(() => {
|
Alpine.effect(() => {
|
||||||
if(Alpine.store("audioPlayback").isDisabledByUser) this.isAvailable = false;
|
if (Alpine.store("audioPlayback").isDisabledByUser) this.isAvailable = false;
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
testPermission() {
|
testPermission() {
|
||||||
this.audioPlayer.src = '/static/sound/silence.mp3';
|
this.audioPlayer.src = '/static/sound/silence.mp3';
|
||||||
this.audioPlayer.play().then(() => {
|
this.audioPlayer.play().then(() => {
|
||||||
this.isAvailable = true
|
this.isAvailable = true
|
||||||
this.hasBeenTested = true
|
this.hasBeenTested = true
|
||||||
|
|
||||||
|
this.preloadSound(this.sounds.beep)
|
||||||
|
this.preloadSound(this.sounds.countdown)
|
||||||
|
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
console.warn("Audio permission not granted!")
|
console.warn("Audio permission not granted!")
|
||||||
this.isAvailable = false
|
this.isAvailable = false
|
||||||
|
@ -112,11 +134,41 @@ document.addEventListener('alpine:init' , () => {
|
||||||
},
|
},
|
||||||
|
|
||||||
playSound(soundFile) {
|
playSound(soundFile) {
|
||||||
if(!this.audioPlayer.paused || !this.isAvailable || this.isDisabledByUser || this.isDisabledByServer) {
|
if (!this.audioPlayer.paused || !this.isAvailable || this.isDisabledByUser || this.isDisabledByServer || this.replayLocked) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.audioPlayer.src = soundFile;
|
if (this.audioPlayer.src !== soundFile) {
|
||||||
|
this.audioPlayer.src = soundFile;
|
||||||
|
}
|
||||||
|
this.replayLocked = true;
|
||||||
this.audioPlayer.play();
|
this.audioPlayer.play();
|
||||||
|
},
|
||||||
|
|
||||||
|
preloadSound(soundFile) {
|
||||||
|
if (this.audioPlayer.src === soundFile) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
console.log(`Preloading sound ${soundFile}`)
|
||||||
|
this.audioPlayer.src = soundFile;
|
||||||
|
this.audioPlayer.load();
|
||||||
|
},
|
||||||
|
|
||||||
|
playCountdown() {
|
||||||
|
this.playSound(this.sounds.countdown)
|
||||||
|
},
|
||||||
|
preloadCountdown() {
|
||||||
|
this.preloadSound(this.sounds.countdown)
|
||||||
|
},
|
||||||
|
|
||||||
|
playBeep() {
|
||||||
|
this.playSound(this.sounds.beep);
|
||||||
|
},
|
||||||
|
preloadBeep() {
|
||||||
|
this.preloadSound(this.sounds.beep);
|
||||||
|
},
|
||||||
|
|
||||||
|
unlockReplay() {
|
||||||
|
this.replayLocked = false;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -127,9 +179,9 @@ document.addEventListener('alpine:init' , () => {
|
||||||
document.documentElement.requestFullscreen();
|
document.documentElement.requestFullscreen();
|
||||||
this.enabled = true
|
this.enabled = true
|
||||||
} else {
|
} else {
|
||||||
if (document.exitFullscreen) {
|
if (document.exitFullscreen) {
|
||||||
document.exitFullscreen();
|
document.exitFullscreen();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -159,7 +211,7 @@ function getTimerText(remaningSeconds) {
|
||||||
var remaningMinutes = zeroPad(parseInt(remaningSeconds / 60) % 60, 2)
|
var remaningMinutes = zeroPad(parseInt(remaningSeconds / 60) % 60, 2)
|
||||||
var remaningSeconds = zeroPad(remaningSeconds % 60, 2)
|
var remaningSeconds = zeroPad(remaningSeconds % 60, 2)
|
||||||
|
|
||||||
if(parseInt(remaningHours) === 0) {
|
if (parseInt(remaningHours) === 0) {
|
||||||
return " " + remaningMinutes + ":" + remaningSeconds + " "
|
return " " + remaningMinutes + ":" + remaningSeconds + " "
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -167,10 +219,25 @@ function getTimerText(remaningSeconds) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function playTimerSound(remaningSeconds) {
|
function playTimerSound(remaningSeconds) {
|
||||||
if(parseInt(remaningSeconds) === 5) {
|
switch (parseInt(remaningSeconds)) {
|
||||||
Alpine.store("audioPlayback").playSound('/static/sound/countdown.mp3')
|
case 65:
|
||||||
|
Alpine.store("audioPlayback").preloadBeep()
|
||||||
|
break;
|
||||||
|
case 60:
|
||||||
|
Alpine.store("audioPlayback").playBeep()
|
||||||
|
break;
|
||||||
|
case 59:
|
||||||
|
Alpine.store("audioPlayback").unlockReplay()
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 10:
|
||||||
|
Alpine.store("audioPlayback").preloadCountdown()
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
Alpine.store("audioPlayback").playCountdown()
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
Alpine.store("audioPlayback").unlockReplay()
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
else if(parseInt(remaningSeconds) === 60){
|
|
||||||
Alpine.store("audioPlayback").playSound('/static/sound/beep.mp3')
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -424,9 +424,7 @@ class LandingpageTheme
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/js/bootstrap.bundle.min.js" integrity="sha384-ygbV9kiqUc6oa4msXn9868pTtWMgiQaeYH7/t7LECLbyPA2x65Kgf80OJFdroafW" crossorigin="anonymous"></script>
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/js/bootstrap.bundle.min.js" integrity="sha384-ygbV9kiqUc6oa4msXn9868pTtWMgiQaeYH7/t7LECLbyPA2x65Kgf80OJFdroafW" crossorigin="anonymous"></script>
|
||||||
|
|
||||||
<!-- JavaScript -->
|
<!-- JavaScript -->
|
||||||
<script src="/static/js/lib/apline.min.js" defer></script>
|
<script src="/static/js/lib/alpine.min.js" defer></script>
|
||||||
<script src="/static/js/lib/purify.min.js" defer></script>
|
|
||||||
<script src="/static/js/synced-timer.js" defer></script>
|
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
:root {
|
:root {
|
||||||
|
@ -510,10 +508,10 @@ class LandingpageTheme
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
|
||||||
|
|
||||||
<script src="/static/js/lib/alpine.min.js" defer></script>
|
|
||||||
<script src="/static/js/synced-timer.js"></script>
|
<script src="/static/js/synced-timer.js"></script>
|
||||||
|
</script>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
<?php
|
<?php
|
||||||
|
|
Loading…
Reference in a new issue