diff --git a/CHANGELOG.md b/CHANGELOG.md index 3fa2f6f..f099f2a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,7 @@ A preview of major changes can be found in the Wiki ([Latest Changes](https://gi - New "Explore" tab on player statistics, to explore darts thrown - Support for `ANY` and `MASTER` outs for `x01` legs - Simplified input for `x01` legs +- Ability to configure: "Announcement Volume", "Auto Busting" and "Auto Leg Finish" from frontend #### Changed - Updated to use `Node.js v18` diff --git a/src/components/navbar/components/configure-kcapp-modal/configure-kcapp-modal.component.js b/src/components/navbar/components/configure-kcapp-modal/configure-kcapp-modal.component.js index e77273e..e564418 100644 --- a/src/components/navbar/components/configure-kcapp-modal/configure-kcapp-modal.component.js +++ b/src/components/navbar/components/configure-kcapp-modal/configure-kcapp-modal.component.js @@ -4,7 +4,11 @@ module.exports = { onCreate(input) { this.state = { layouts: [ {id: "wide", name: "Wide"}, {id: "compact", name: "Compact"}, {id: "compact-large", name: "Compact (Large)"} ], - buttonLayout: "wide" + buttonLayout: "wide", + volume: 100, + confirmBusts: true, + autoFinishLegs: false, + autoFinishTime: 10 } }, onMount() { @@ -12,9 +16,45 @@ module.exports = { if (buttonLayout) { this.state.buttonLayout = buttonLayout } + + const volume = localStorage.get("volume"); + if (volume) { + this.state.volume = Math.min(Math.max(parseInt(volume * 100), 0), 100); + } + + const confirmBusts = localStorage.get("confirm-busts"); + if (confirmBusts !== null) { + this.state.confirmBusts = confirmBusts === 'true'; + } + + const autoFinishLegs = localStorage.get("auto-finish-legs"); + if (autoFinishLegs !== null) { + this.state.autoFinishLegs = autoFinishLegs === 'true'; + } + + const autoFinishTime = localStorage.get("auto-finish-time"); + if (autoFinishTime !== null) { + this.state.autoFinishTime = parseInt(autoFinishTime, 10) || 10; + } + }, + updateVolume(event, selected) { + this.state.volume = selected.value; + }, + toggleConfirmBusts(event) { + this.state.confirmBusts = event.target.checked; + }, + toggleAutoFinishLegs(event) { + this.state.autoFinishLegs = event.target.checked; + }, + updateAutoFinishTime(event) { + this.state.autoFinishTime = parseInt(event.target.value, 10); }, onSave() { this.state.buttonLayout = document.getElementById("buttonLayout").value; localStorage.set('button-layout', this.state.buttonLayout); + localStorage.set('confirm-busts', this.state.confirmBusts); + localStorage.set('auto-finish-legs', this.state.autoFinishLegs); + localStorage.set('auto-finish-time', this.state.autoFinishTime); + localStorage.set('volume', this.state.volume / 100); }, } diff --git a/src/components/navbar/components/configure-kcapp-modal/configure-kcapp-modal.marko b/src/components/navbar/components/configure-kcapp-modal/configure-kcapp-modal.marko index a94fb3b..068c107 100644 --- a/src/components/navbar/components/configure-kcapp-modal/configure-kcapp-modal.marko +++ b/src/components/navbar/components/configure-kcapp-modal/configure-kcapp-modal.marko @@ -11,11 +11,51 @@
- + +
+
+ +
+
+ Announcer Volume +
+
+
+ + +
+
+
+
+
+ Leg Options +
+
+
+ + + Auto Finish Legs +
+ +
+ + seconds +
+ +
+
+
+ + + Confirm Busts?
diff --git a/src/components/navbar/components/configure-kcapp-modal/configure-kcapp-modal.style.less b/src/components/navbar/components/configure-kcapp-modal/configure-kcapp-modal.style.less new file mode 100644 index 0000000..9f3aec2 --- /dev/null +++ b/src/components/navbar/components/configure-kcapp-modal/configure-kcapp-modal.style.less @@ -0,0 +1,52 @@ +.block-container-header > span { + font-weight: 700; +} + +.form-check-input { + display: none; +} + +.toggle-button { + width: 50px; + height: 24px; + background-color: #ccc; + border-radius: 12px; + position: relative; + cursor: pointer; + transition: background-color 0.3s; + margin-bottom: -5px; +} + +.toggle-button .toggle-slider { + position: absolute; + top: 2px; + left: 2px; + width: 20px; + height: 20px; + background-color: white; + border-radius: 50%; + transition: transform 0.3s; +} + +.form-check-input:checked + .toggle-button { + background-color: #4caf50; +} + +.form-check-input:checked + .toggle-button .toggle-slider { + transform: translateX(26px); +} + +.block-container-with-header { + display: flex; + align-items: center; + margin-bottom: 0px; + margin-top: 0px; +} + +.form-group { + margin-bottom: 0; +} + +.toggle-button { + display: inline-block; +} \ No newline at end of file diff --git a/src/components/scorecard-header/scorecard-header.marko b/src/components/scorecard-header/scorecard-header.marko index 45a2073..09e480d 100644 --- a/src/components/scorecard-header/scorecard-header.marko +++ b/src/components/scorecard-header/scorecard-header.marko @@ -1,7 +1,7 @@ $ const types = require("../scorecard/components/match_types"); -
+
diff --git a/src/components/scorecard-header/scorecard-header.style.less b/src/components/scorecard-header/scorecard-header.style.less index d5bcc48..17907cc 100644 --- a/src/components/scorecard-header/scorecard-header.style.less +++ b/src/components/scorecard-header/scorecard-header.style.less @@ -1,3 +1,20 @@ +/*@media (max-width: 1340px) { + .scorecard-header { + overflow: hidden; + transform: scaleY(0.75); + transform-origin: top left; + margin-bottom: -40px !important; + } + + .label-player-name { + font-size: 14pt; + } + + .label-player-score { + font-size: 24pt; + } +}*/ + table { width: 100%; table-layout: fixed; @@ -35,7 +52,7 @@ table { } .label-player-name { - font-size: 10pt; + font-size: 18pt; padding: 0px; margin: 0px; color: #ffffff; @@ -50,7 +67,7 @@ table { .label-player-score { font-weight: 700; padding: 10px 0px 0px 0px; - font-size: 40pt; + font-size: 32pt; } .player-legs { diff --git a/src/components/scorecard/components/x01.js b/src/components/scorecard/components/x01.js index c1f4833..99d9b4f 100644 --- a/src/components/scorecard/components/x01.js +++ b/src/components/scorecard/components/x01.js @@ -1,4 +1,5 @@ const alertify = require("../../../util/alertify"); +const localStorage = require("../../../util/localstorage"); const types = require("./match_types"); exports.removeLast = function(dart, external) { @@ -66,26 +67,27 @@ exports.confirmThrow = function (external) { const isBust = module.exports.isBust(this.state.player, dart, this.state.totalScore, this.state.leg); if (isCheckout) { submitting = true; - alertify.confirm('Leg will be finished.', - () => { - this.emit('leg-finished', true); - }, () => { - this.removeLast(); - this.emit('leg-finished', false); - }); - } else if (isBust) { + } + else if (isBust) { submitting = true; this.state.isBusted = true; - alertify.confirm('Player busted', - () => { - alertify.success('Player busted'); - this.emit('player-busted', true); - }, - () => { - this.removeLast(); - this.state.isBusted = false; - this.emit('player-busted', false); - }); + const isConfirmBust = localStorage.getBool('confirm-busts'); + + if (isConfirmBust) { + alertify.confirm('Player busted', + () => { + alertify.success('Player busted'); + this.emit('player-busted', true); + }, + () => { + this.removeLast(); + this.state.isBusted = false; + this.emit('player-busted', false); + }); + } else { + alertify.success('Player busted'); + this.emit('player-busted', true); + } } if (!this.state.player.player.options || this.state.player.player.options.subtract_per_dart) { this.state.player.current_score -= scored; @@ -96,3 +98,4 @@ exports.confirmThrow = function (external) { } return submitting; } + diff --git a/src/components/scorecard/scorecard.component.js b/src/components/scorecard/scorecard.component.js index f981733..dc8a46a 100644 --- a/src/components/scorecard/scorecard.component.js +++ b/src/components/scorecard/scorecard.component.js @@ -1,4 +1,5 @@ const alertify = require("../../util/alertify"); +const localStorage = require("../../util/localstorage"); const x01 = require("./components/x01"); const shootout = require("./components/shootout"); @@ -260,12 +261,39 @@ module.exports = { }, confirmLegFinish() { - alertify.confirm('Leg will be finished.', + let countdown = localStorage.get('auto-finish-time') || 10; + let confirmed = false; + + const okFunction = () => { + confirmed = true; + this.emit('leg-finished', true); + }; + + const confirmDialog = alertify.confirm(`Leg will be finished`, + okFunction, () => { - this.emit('leg-finished', true); - }, () => { this.removeLast(); this.emit('leg-finished', false); + } + ); + + const isAutoFinish = localStorage.getBool('auto-finish-legs'); + if (isAutoFinish) { + confirmDialog.setContent(`Leg will be finished (${countdown}s)`); + const countdownInterval = setInterval(() => { + countdown--; + confirmDialog.setContent(`Leg will be finished (${countdown}s)`); + + if (countdown <= 0) { + clearInterval(countdownInterval); + if (!confirmed) { + okFunction(); + } + } + }, 1000); + confirmDialog.set('onclose', function() { + clearInterval(countdownInterval); }); + } } }; diff --git a/src/util/alertify.js b/src/util/alertify.js index 547aa40..6f3bf85 100644 --- a/src/util/alertify.js +++ b/src/util/alertify.js @@ -37,7 +37,7 @@ exports.alert = (text, okFnc) => { .show(); } exports.confirm = (text, okFnc, cancelFnc) => { - bootstrap().confirm(text, okFnc, cancelFnc) + return bootstrap().confirm(text, okFnc, cancelFnc) .setting({ title: TITLE, defaultFocus: 'ok', diff --git a/src/util/localstorage.js b/src/util/localstorage.js index cf57980..679f5ea 100644 --- a/src/util/localstorage.js +++ b/src/util/localstorage.js @@ -4,6 +4,13 @@ function getKey(key) { exports.get = key => localStorage.getItem(getKey(key)); exports.getInt = key => parseInt(localStorage.getItem(getKey(key))); +exports.getBool = key => { + const item = localStorage.getItem(getKey(key)); + if (!item) { + return false; + } + return item === 'true'; +} exports.set = (key, value) => localStorage.setItem(getKey(key), value); exports.getKey = getKey; diff --git a/src/util/socket.io-helper.js b/src/util/socket.io-helper.js index 93a2dfc..c0556ac 100644 --- a/src/util/socket.io-helper.js +++ b/src/util/socket.io-helper.js @@ -3,6 +3,7 @@ const io = require('socket.io-client'); const alertify = require('./alertify'); const speaker = require('./speaker'); const types = require('../components/scorecard/components/match_types'); +const localStorage = require('./localstorage'); exports.connect = (url) => { const socket = io(url); @@ -89,13 +90,17 @@ exports.say = (data, thiz) => { if (thiz.state.venueConfig) { voice = thiz.state.venueConfig.tts_voice; } + let volume = localStorage.get("volume"); + volume = volume ? Math.min(Math.max(parseFloat(volume), 0.0), 1.0) : 1.0; const oldPlayer = thiz.state.audioAnnouncer; const isAudioAnnouncement = (oldPlayer.duration > 0 && !oldPlayer.paused) || (!isNaN(oldPlayer.duration) && !oldPlayer.ended && oldPlayer.paused); if (data.audios) { const audioPlayers = [ ]; for (const file of data.audios) { - audioPlayers.push(file.file ? new Audio(file.file) : speaker.getUtteranceWithVoice(file, voice)); + const audioPlayer = file.file ? new Audio(file.file) : speaker.getUtteranceWithVoice(file, voice); + audioPlayer.volume = volume; + audioPlayers.push(audioPlayer); } for (let i = 0; i < audioPlayers.length; i++) {