Skip to content

Commit 6dad541

Browse files
Miodecfehmer
andauthored
feat(sound): add play time warning (@Miodec) (#6759)
### Description <!-- Please describe the change(s) made in your PR --> ### Checks - [ ] Adding quotes? - [ ] Make sure to include translations for the quotes in the description (or another comment) so we can verify their content. - [ ] Adding a language? - Make sure to follow the [languages documentation](https://github.com/monkeytypegame/monkeytype/blob/master/docs/LANGUAGES.md) - [ ] Add language to `packages/contracts/src/schemas/languages.ts` - [ ] Add language to exactly one group in `frontend/src/ts/constants/languages.ts` - [ ] Add language json file to `frontend/static/languages` - [ ] Adding a theme? - Make sure to follow the [themes documentation](https://github.com/monkeytypegame/monkeytype/blob/master/docs/THEMES.md) - [ ] Add theme to `packages/contracts/src/schemas/themes.ts` - [ ] Add theme to `frontend/src/ts/constants/themes.ts` - [ ] Add theme css file to `frontend/static/themes` - Also please add a screenshot of the theme, it would be extra awesome if you do so! - [ ] Adding a layout? - [ ] Make sure to follow the [layouts documentation](https://github.com/monkeytypegame/monkeytype/blob/master/docs/LAYOUTS.md) - [ ] Add layout to `packages/contracts/src/schemas/layouts.ts` - [ ] Add layout json file to `frontend/static/layouts` - [ ] Check if any open issues are related to this PR; if so, be sure to tag them below. - [ ] Make sure the PR title follows the Conventional Commits standard. (https://www.conventionalcommits.org for more info) - [ ] Make sure to include your GitHub username prefixed with @ inside parentheses at the end of the PR title. <!-- label(optional scope): pull request title (@your_github_username) --> <!-- I know I know they seem boring but please do them, they help us and you will find out it also helps you.--> Closes # <!-- the issue(s) your PR resolves if any (delete if that is not the case) --> <!-- please also reference any issues and or PRs related to your pull request --> <!-- Also remove it if you are not following any issues. --> <!-- pro tip: you can mention an issue, PR, or discussion on GitHub by referencing its hash number e.g: [#1234](#1234) --> <!-- pro tip: you can press . (dot or period) in the code tab of any GitHub repo to get access to GitHub's VS Code web editor Enjoy! :) --> --------- Co-authored-by: Christian Fehmer <[email protected]>
1 parent 27019d1 commit 6dad541

File tree

10 files changed

+127
-0
lines changed

10 files changed

+127
-0
lines changed

frontend/src/html/pages/settings.html

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -705,6 +705,25 @@
705705
<button data-config-value="4">missed punch</button>
706706
</div>
707707
</div>
708+
<div class="section fullWidth" data-config-name="playTimeWarning">
709+
<div class="groupTitle">
710+
<i class="fas fa-exclamation-triangle"></i>
711+
<span>play time warning</span>
712+
<button class="text" tabindex="-1">
713+
<i class="fas fa-fw fa-link"></i>
714+
</button>
715+
</div>
716+
<div class="text">
717+
Play a short warning sound if you are close to the end of a timed test.
718+
</div>
719+
<div class="buttons">
720+
<button data-config-value="off">off</button>
721+
<button data-config-value="1">1 second</button>
722+
<button data-config-value="3">3 seconds</button>
723+
<button data-config-value="5">5 seconds</button>
724+
<button data-config-value="10">10 seconds</button>
725+
</div>
726+
</div>
708727
<div class="sectionSpacer"></div>
709728
</div>
710729

frontend/src/ts/commandline/lists.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import QuickEndCommands from "./lists/quick-end";
2424
import OppositeShiftModeCommands from "./lists/opposite-shift-mode";
2525
import SoundOnErrorCommands from "./lists/sound-on-error";
2626
import SoundVolumeCommands from "./lists/sound-volume";
27+
import TimeWarningCommands from "./lists/time-warning";
2728
import FlipTestColorsCommands from "./lists/flip-test-colors";
2829
import SmoothLineScrollCommands from "./lists/smooth-line-scroll";
2930
import AlwaysShowDecimalCommands from "./lists/always-show-decimal";
@@ -242,6 +243,7 @@ export const commands: CommandsSubgroup = {
242243
...SoundVolumeCommands,
243244
...SoundOnClickCommands,
244245
...SoundOnErrorCommands,
246+
...TimeWarningCommands,
245247

246248
//caret
247249
...SmoothCaretCommands,
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import {
2+
PlayTimeWarning,
3+
PlayTimeWarningSchema,
4+
} from "@monkeytype/schemas/configs";
5+
import * as UpdateConfig from "../../config";
6+
import * as SoundController from "../../controllers/sound-controller";
7+
import { Command, CommandsSubgroup } from "../types";
8+
9+
const subgroup: CommandsSubgroup = {
10+
title: "Time warning...",
11+
configKey: "playTimeWarning",
12+
list: (Object.keys(PlayTimeWarningSchema.Values) as PlayTimeWarning[]).map(
13+
(time) => ({
14+
id: `setPlayTimeWarning${time}`,
15+
display:
16+
time === "off" ? "off" : `${time} second${time !== "1" ? "s" : ""}`,
17+
configValue: time,
18+
exec: (): void => {
19+
UpdateConfig.setPlayTimeWarning(time);
20+
if (time !== "off") {
21+
void SoundController.playTimeWarning();
22+
}
23+
},
24+
})
25+
),
26+
};
27+
28+
const commands: Command[] = [
29+
{
30+
id: "changePlayTimeWarning",
31+
display: "Time warning...",
32+
icon: "fa-exclamation-triangle",
33+
subgroup,
34+
},
35+
];
36+
37+
export default commands;

frontend/src/ts/config.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,10 @@ const configMetadata: ConfigMetadata = {
394394
displayString: "play sound on error",
395395
changeRequiresRestart: false,
396396
},
397+
playTimeWarning: {
398+
displayString: "play time warning",
399+
changeRequiresRestart: false,
400+
},
397401

398402
// caret
399403
smoothCaret: {
@@ -855,6 +859,13 @@ export function setSoundVolume(
855859
return genericSet("soundVolume", val, nosave);
856860
}
857861

862+
export function setPlayTimeWarning(
863+
value: ConfigSchemas.PlayTimeWarning,
864+
nosave?: boolean
865+
): boolean {
866+
return genericSet("playTimeWarning", value, nosave);
867+
}
868+
858869
//difficulty
859870
export function setDifficulty(
860871
diff: ConfigSchemas.Difficulty,

frontend/src/ts/constants/default-config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ const obj = {
101101
tapeMode: "off",
102102
tapeMargin: 50,
103103
maxLineWidth: 0,
104+
playTimeWarning: "off",
104105
} as Config;
105106

106107
export function getDefaultConfig(): Config {

frontend/src/ts/controllers/sound-controller.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,16 @@ type ErrorSounds = Record<
3333
let errorSounds: ErrorSounds | null = null;
3434
let clickSounds: ClickSounds | null = null;
3535

36+
let timeWarning: Howl | null = null;
37+
38+
async function initTimeWarning(): Promise<void> {
39+
const Howl = (await gethowler()).Howl;
40+
if (timeWarning !== null) return;
41+
timeWarning = new Howl({
42+
src: "../sound/timeWarning.wav",
43+
});
44+
}
45+
3646
async function initErrorSound(): Promise<void> {
3747
const Howl = (await gethowler()).Howl;
3848
if (errorSounds !== null) return;
@@ -610,6 +620,14 @@ function playScale(scale: ValidScales, scaleMeta: ScaleData): void {
610620
oscillatorNode.stop(audioCtx.currentTime + 2);
611621
}
612622

623+
export async function playTimeWarning(): Promise<void> {
624+
if (timeWarning === null) await initTimeWarning();
625+
const soundToPlay = timeWarning as Howl;
626+
soundToPlay.stop();
627+
soundToPlay.seek(0);
628+
soundToPlay.play();
629+
}
630+
613631
export function playNote(
614632
codeOverride?: string,
615633
oscillatorTypeOverride?: SupportedOscillatorTypes

frontend/src/ts/pages/settings.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,14 @@ async function initGroups(): Promise<void> {
285285
UpdateConfig.setSoundVolume,
286286
"range"
287287
) as SettingsGroup<ConfigValue>;
288+
groups["playTimeWarning"] = new SettingsGroup(
289+
"playTimeWarning",
290+
UpdateConfig.setPlayTimeWarning,
291+
"button",
292+
() => {
293+
if (Config.playTimeWarning !== "off") void Sound.playTimeWarning();
294+
}
295+
) as SettingsGroup<ConfigValue>;
288296
groups["playSoundOnError"] = new SettingsGroup(
289297
"playSoundOnError",
290298
UpdateConfig.setPlaySoundOnError,

frontend/src/ts/test/test-timer.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import * as Time from "../states/time";
1717
import * as TimerEvent from "../observables/timer-event";
1818
import * as LayoutfluidFunboxTimer from "../test/funbox/layoutfluid-funbox-timer";
1919
import { KeymapLayout, Layout } from "@monkeytype/schemas/configs";
20+
import * as SoundController from "../controllers/sound-controller";
2021

2122
type TimerStats = {
2223
dateNow: number;
@@ -175,6 +176,26 @@ function checkIfTimeIsUp(): void {
175176
if (timerDebug) console.timeEnd("times up check");
176177
}
177178

179+
function playTimeWarning(): void {
180+
if (timerDebug) console.time("play timer warning");
181+
182+
let maxTime = undefined;
183+
184+
if (Config.mode === "time") {
185+
maxTime = Config.time;
186+
} else if (Config.mode === "custom" && CustomText.getLimitMode() === "time") {
187+
maxTime = CustomText.getLimitValue();
188+
}
189+
190+
if (
191+
maxTime !== undefined &&
192+
Time.get() === maxTime - parseInt(Config.playTimeWarning, 10)
193+
) {
194+
void SoundController.playTimeWarning();
195+
}
196+
if (timerDebug) console.timeEnd("play timer warning");
197+
}
198+
178199
// ---------------------------------------
179200

180201
let timerStats: TimerStats[] = [];
@@ -188,6 +209,7 @@ async function timerStep(): Promise<void> {
188209
Time.increment();
189210
premid();
190211
updateTimer();
212+
if (Config.playTimeWarning !== "off") playTimeWarning();
191213
const wpmAndRaw = calculateWpmRaw();
192214
const acc = calculateAcc();
193215
monkey(wpmAndRaw);
244 KB
Binary file not shown.

packages/schemas/src/configs.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,13 @@ export const CustomBackgroundSchema = z
355355
.or(z.literal(""));
356356
export type CustomBackground = z.infer<typeof CustomBackgroundSchema>;
357357

358+
export const PlayTimeWarningSchema = z
359+
.enum(["off", "1", "3", "5", "10"])
360+
.describe(
361+
"How many seconds before the end of the test to play a warning sound."
362+
);
363+
export type PlayTimeWarning = z.infer<typeof PlayTimeWarningSchema>;
364+
358365
export const ConfigSchema = z
359366
.object({
360367
// test
@@ -402,6 +409,7 @@ export const ConfigSchema = z
402409
soundVolume: SoundVolumeSchema,
403410
playSoundOnClick: PlaySoundOnClickSchema,
404411
playSoundOnError: PlaySoundOnErrorSchema,
412+
playTimeWarning: PlayTimeWarningSchema,
405413

406414
// caret
407415
smoothCaret: SmoothCaretSchema,
@@ -536,6 +544,7 @@ export const ConfigGroupsLiteral = {
536544
soundVolume: "sound",
537545
playSoundOnClick: "sound",
538546
playSoundOnError: "sound",
547+
playTimeWarning: "sound",
539548

540549
//caret
541550
smoothCaret: "caret",

0 commit comments

Comments
 (0)