Skip to content

Commit

Permalink
Add keyboard controls and clean up the code a bit
Browse files Browse the repository at this point in the history
  • Loading branch information
loehnertz committed Jun 8, 2024
1 parent 5b69f99 commit 3cce357
Show file tree
Hide file tree
Showing 2 changed files with 116 additions and 47 deletions.
15 changes: 10 additions & 5 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<meta charset="UTF-8">
<meta content="width=device-width, initial-scale=1.0" name="viewport">
<title>Gong Fu Tea Timer</title>
<meta content="A feature-rich timer app for Gong Fu tea sessions." name="description">
<link href="./image/icon/apple-touch-icon.png" rel="apple-touch-icon" sizes="152x152">
<link href="./image/icon/favicon-32x32.png" rel="icon" sizes="32x32" type="image/png">
<link href="./image/icon/favicon-16x16.png" rel="icon" sizes="16x16" type="image/png">
Expand All @@ -13,7 +14,7 @@
<meta content="#da532c" name="msapplication-TileColor">
<meta content="./image/icon/browserconfig.xml" name="msapplication-config">
<meta content="#ffffff" name="theme-color">
<link href="https://cdn.jsdelivr.net/npm/bulma@1.0.0/css/bulma.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/bulma@1/css/bulma.min.css" rel="stylesheet">
<link href="./style/styles.css" rel="stylesheet">
</head>
<body>
Expand Down Expand Up @@ -103,7 +104,10 @@ <h1 class="title is-1 has-text-centered">🍵</h1>
<div class="field mt-5">
<label class="label" for="offsetTime">Adjust Current Infusion Time (seconds):</label>
<div class="control">
<input :disabled="timerRunning" class="input" id="offsetTime" type="number"
<input :disabled="timerRunning"
class="input"
id="offsetTime"
type="number"
v-model.number="offsetTime">
</div>
</div>
Expand Down Expand Up @@ -131,7 +135,7 @@ <h1 class="title is-1 has-text-centered">🍵</h1>
</div>
<hr>
<div class="field mt-5">
<button @click="backToSettings" class="button is-danger is-fullwidth">Back to Settings</button>
<button @click="confirmBackToSettings" class="button is-danger is-fullwidth">Back to Settings</button>
</div>
<div class="has-text-centered mt-4">
<p>By <a href="http://jakob.codes/" target="_blank">Jakob Löhnertz</a></p>
Expand All @@ -147,8 +151,9 @@ <h1 class="title is-1 has-text-centered">🍵</h1>
.then(function (registration) {
console.log('Service Worker registered with scope:', registration.scope);
}).catch(function (error) {
console.log('Service Worker registration failed:', error);
});
console.log('Service Worker registration failed:', error);
}
);
}
</script>
</body>
Expand Down
148 changes: 106 additions & 42 deletions script/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ new Vue({
offsetTime: 0,
timeRemaining: 0,
timerRunning: false,
selectedPreset: "",
selectedPreset: '',
showSettings: true,
beepWarningPlayed: false,
timerWorker: null,
Expand Down Expand Up @@ -46,20 +46,22 @@ new Vue({
/**
* Watches for changes to the selected preset and updates the initial and increment times accordingly.
*/
async selectedPreset(newValue) {
selectedPreset(newValue) {
if (newValue) {
this.initialTime = newValue.firstInfusion;
this.incrementTime = newValue.additionalInfusions;
} else if (this.storedSettings) {
this.initialTime = this.storedSettings.initialTime;
this.incrementTime = this.storedSettings.incrementTime;
}
await this.resetTimer();
this.resetTimer();
},
/**
* Watches for changes to the infusion count and persists the new value to local storage.
*/
infusionCount(newValue) {
if (!newValue || this.showSettings) return;

const storedSettings = this.storedSettings;
storedSettings.infusionCount = newValue;
this.persistSettings(storedSettings);
Expand All @@ -68,6 +70,8 @@ new Vue({
* Watches for changes to the initial time and persists the new value to local storage.
*/
initialTime(newValue) {
if (!newValue || this.showSettings) return;

const storedSettings = this.storedSettings;
storedSettings.initialTime = newValue;
this.persistSettings(storedSettings);
Expand All @@ -76,10 +80,24 @@ new Vue({
* Watches for changes to the increment time and persists the new value to local storage.
*/
incrementTime(newValue) {
if (!newValue || this.showSettings) return;

const storedSettings = this.storedSettings;
storedSettings.incrementTime = newValue;
this.persistSettings(storedSettings);
},
/**
* Watches for changes to the offset time and updates the time remaining.
*/
offsetTime() {
this.timeRemaining = parseFloat(this.infusionTime.toFixed(1));
},
/**
* Watches for changes to the time remaining and updates the window title.
*/
timeRemaining() {
this.updateWindowTitle();
},
},
methods: {
/**
Expand All @@ -91,64 +109,58 @@ new Vue({
/**
* Toggles the timer between start and stop states.
*/
async toggleStartStop() {
toggleStartStop() {
if (!this.timerRunning) {
await this.startTimer();
this.startTimer();
} else {
await this.resetTimer();
this.resetTimer();
}
},
/**
* Starts the timer with the current `timeRemaining`.
*/
async startTimer() {
startTimer() {
this.timerWorker.postMessage({command: 'start', payload: {initialTime: this.timeRemaining}});
},
/**
* Stops the timer without resetting the `timeRemaining`.
*/
async stopTimer() {
stopTimer() {
this.timerWorker.postMessage({command: 'stop', payload: {initialTime: this.timeRemaining}});
},
/**
* Resets the timer to the current `infusionTime`.
*/
async resetTimer() {
resetTimer() {
this.timerWorker.postMessage({command: 'reset', payload: {initialTime: this.infusionTime}});
},
/**
* Moves to the previous infusion if possible and resets the timer.
*/
async previousInfusion() {
previousInfusion() {
if (this.infusionCount > 1) {
this.infusionCount--;
await this.resetTimer();
this.resetTimer();
}
},
/**
* Skips to the next infusion and resets the timer.
*/
async nextInfusion() {
nextInfusion() {
this.infusionCount++;
await this.resetTimer();
this.resetTimer();
},
/**
* Adjusts the offset time by a given percentage.
* @param {number} percentage - The percentage to adjust the offset time by.
*/
adjustOffsetByPercentage(percentage) {
if (!this.timerRunning) {
this.offsetTime = (this.incrementTime * percentage) / 100;
this.timeRemaining = parseFloat(this.infusionTime.toFixed(1));
this.updateWindowTitle();
}
this.offsetTime = (this.incrementTime * percentage) / 100;
},
/**
* Confirms the current settings and initializes the session.
*/
async confirmSettings() {
this.showSettings = false;

confirmSettings() {
this.infusionCount = 1;
this.offsetTime = 0;

Expand All @@ -159,38 +171,90 @@ new Vue({
};
this.persistSettings(settings);

await this.resetTimer();
this.resetTimer();

this.showSettings = false;
},
/**
* Confirms the return to settings and discards the current session.
*/
confirmBackToSettings() {
if (confirm('Are you sure you want to discard the current session and return to settings?')) {
this.backToSettings();
}
},
/**
* Returns to settings, discarding the current session from local storage.
*/
backToSettings() {
if (confirm('Are you sure you want to discard the current session and return to settings?')) {
this.showSettings = true;
localStorage.removeItem('settings');

localStorage.removeItem('settings');
document.title = 'Gong Fu Tea Timer';

document.title = "Gong Fu Tea Timer";
}
this.showSettings = true;
},
/**
* Loads the session data from the local storage and initializes the timer.
*/
async loadSession() {
loadSession() {
if (this.storedSettings) {
this.showSettings = false;
this.infusionCount = this.storedSettings.infusionCount;
this.initialTime = this.storedSettings.initialTime;
this.incrementTime = this.storedSettings.incrementTime;
}
await this.resetTimer();
this.resetTimer();
},
/**
* Persists the settings to local storage.
* @param {object} storedSettings - The settings to persist.
* Reloads the page if the settings are invalid.
* @param {object} settings - The settings to persist.
*/
persistSettings(settings) {
if (!settings || settings.infusionCount < 1 || settings.initialTime < 1 || settings.incrementTime < 1) {
this.backToSettings();
location.reload();
return;
}
localStorage.setItem('settings', JSON.stringify(settings));
},
/**
* Handles keydown events for the timer.
* @param {KeyboardEvent} event - The keydown event.
*/
persistSettings(storedSettings) {
localStorage.setItem('settings', JSON.stringify(storedSettings));
handleKeydown(event) {
switch (event.code) {
case 'Space':
event.preventDefault();
this.toggleStartStop();
break;
case 'ArrowLeft':
event.preventDefault();
this.previousInfusion();
break;
case 'ArrowRight':
event.preventDefault();
this.nextInfusion();
break;
case 'ArrowUp':
event.preventDefault();
if (!this.timerRunning) {
this.offsetTime += 1;
}
break;
case 'ArrowDown':
event.preventDefault();
if (!this.timerRunning) {
if (this.offsetTime > 0) {
this.offsetTime -= 1;
}
}
break;
case 'Backspace':
event.preventDefault();
this.confirmBackToSettings();
break;
}
},
/**
* Handles messages received from the Web Worker running the timer.
Expand All @@ -208,9 +272,8 @@ new Vue({
switch (command) {
case 'tick':
this.timerRunning = true;
this.timeRemaining = timeRemaining;

this.updateWindowTitle();
this.timeRemaining = timeRemaining;

if (this.timeRemaining <= 5 && !this.beepWarningPlayed) {
this.beepWarningPlayed = true;
Expand All @@ -225,28 +288,29 @@ new Vue({
await this.beepEnd.play();

// This also resets the timer via a web worker event
await this.nextInfusion();
this.nextInfusion();

break;
case 'reset':
this.timerRunning = false;
this.timeRemaining = timeRemaining;

this.updateWindowTitle();
this.timeRemaining = timeRemaining;

this.beepWarningPlayed = false;

break;
}
},
},
async mounted() {
mounted() {
this.timerWorker = new Worker('./script/timerWorker.js');
this.timerWorker.onmessage = await this.handleWorkerMessage;
this.timerWorker.onmessage = async (e) => await this.handleWorkerMessage(e);

await this.beepWarning.load();
await this.beepEnd.load();
this.loadSession();

await this.loadSession();
window.addEventListener('keydown', (e) => this.handleKeydown(e));
},
beforeDestroy() {
window.removeEventListener('keydown', (e) => this.handleKeydown(e));
},
});

0 comments on commit 3cce357

Please sign in to comment.