mirror of
https://github.com/monkeytypegame/monkeytype.git
synced 2025-09-08 15:47:37 +08:00
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](https://github.com/monkeytypegame/monkeytype/pull/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 <cfe@sexy-developer.com>
This commit is contained in:
parent
27019d189f
commit
6dad5415c2
10 changed files with 127 additions and 0 deletions
|
@ -705,6 +705,25 @@
|
|||
<button data-config-value="4">missed punch</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section fullWidth" data-config-name="playTimeWarning">
|
||||
<div class="groupTitle">
|
||||
<i class="fas fa-exclamation-triangle"></i>
|
||||
<span>play time warning</span>
|
||||
<button class="text" tabindex="-1">
|
||||
<i class="fas fa-fw fa-link"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="text">
|
||||
Play a short warning sound if you are close to the end of a timed test.
|
||||
</div>
|
||||
<div class="buttons">
|
||||
<button data-config-value="off">off</button>
|
||||
<button data-config-value="1">1 second</button>
|
||||
<button data-config-value="3">3 seconds</button>
|
||||
<button data-config-value="5">5 seconds</button>
|
||||
<button data-config-value="10">10 seconds</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sectionSpacer"></div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ import QuickEndCommands from "./lists/quick-end";
|
|||
import OppositeShiftModeCommands from "./lists/opposite-shift-mode";
|
||||
import SoundOnErrorCommands from "./lists/sound-on-error";
|
||||
import SoundVolumeCommands from "./lists/sound-volume";
|
||||
import TimeWarningCommands from "./lists/time-warning";
|
||||
import FlipTestColorsCommands from "./lists/flip-test-colors";
|
||||
import SmoothLineScrollCommands from "./lists/smooth-line-scroll";
|
||||
import AlwaysShowDecimalCommands from "./lists/always-show-decimal";
|
||||
|
@ -242,6 +243,7 @@ export const commands: CommandsSubgroup = {
|
|||
...SoundVolumeCommands,
|
||||
...SoundOnClickCommands,
|
||||
...SoundOnErrorCommands,
|
||||
...TimeWarningCommands,
|
||||
|
||||
//caret
|
||||
...SmoothCaretCommands,
|
||||
|
|
37
frontend/src/ts/commandline/lists/time-warning.ts
Normal file
37
frontend/src/ts/commandline/lists/time-warning.ts
Normal file
|
@ -0,0 +1,37 @@
|
|||
import {
|
||||
PlayTimeWarning,
|
||||
PlayTimeWarningSchema,
|
||||
} from "@monkeytype/schemas/configs";
|
||||
import * as UpdateConfig from "../../config";
|
||||
import * as SoundController from "../../controllers/sound-controller";
|
||||
import { Command, CommandsSubgroup } from "../types";
|
||||
|
||||
const subgroup: CommandsSubgroup = {
|
||||
title: "Time warning...",
|
||||
configKey: "playTimeWarning",
|
||||
list: (Object.keys(PlayTimeWarningSchema.Values) as PlayTimeWarning[]).map(
|
||||
(time) => ({
|
||||
id: `setPlayTimeWarning${time}`,
|
||||
display:
|
||||
time === "off" ? "off" : `${time} second${time !== "1" ? "s" : ""}`,
|
||||
configValue: time,
|
||||
exec: (): void => {
|
||||
UpdateConfig.setPlayTimeWarning(time);
|
||||
if (time !== "off") {
|
||||
void SoundController.playTimeWarning();
|
||||
}
|
||||
},
|
||||
})
|
||||
),
|
||||
};
|
||||
|
||||
const commands: Command[] = [
|
||||
{
|
||||
id: "changePlayTimeWarning",
|
||||
display: "Time warning...",
|
||||
icon: "fa-exclamation-triangle",
|
||||
subgroup,
|
||||
},
|
||||
];
|
||||
|
||||
export default commands;
|
|
@ -394,6 +394,10 @@ const configMetadata: ConfigMetadata = {
|
|||
displayString: "play sound on error",
|
||||
changeRequiresRestart: false,
|
||||
},
|
||||
playTimeWarning: {
|
||||
displayString: "play time warning",
|
||||
changeRequiresRestart: false,
|
||||
},
|
||||
|
||||
// caret
|
||||
smoothCaret: {
|
||||
|
@ -855,6 +859,13 @@ export function setSoundVolume(
|
|||
return genericSet("soundVolume", val, nosave);
|
||||
}
|
||||
|
||||
export function setPlayTimeWarning(
|
||||
value: ConfigSchemas.PlayTimeWarning,
|
||||
nosave?: boolean
|
||||
): boolean {
|
||||
return genericSet("playTimeWarning", value, nosave);
|
||||
}
|
||||
|
||||
//difficulty
|
||||
export function setDifficulty(
|
||||
diff: ConfigSchemas.Difficulty,
|
||||
|
|
|
@ -101,6 +101,7 @@ const obj = {
|
|||
tapeMode: "off",
|
||||
tapeMargin: 50,
|
||||
maxLineWidth: 0,
|
||||
playTimeWarning: "off",
|
||||
} as Config;
|
||||
|
||||
export function getDefaultConfig(): Config {
|
||||
|
|
|
@ -33,6 +33,16 @@ type ErrorSounds = Record<
|
|||
let errorSounds: ErrorSounds | null = null;
|
||||
let clickSounds: ClickSounds | null = null;
|
||||
|
||||
let timeWarning: Howl | null = null;
|
||||
|
||||
async function initTimeWarning(): Promise<void> {
|
||||
const Howl = (await gethowler()).Howl;
|
||||
if (timeWarning !== null) return;
|
||||
timeWarning = new Howl({
|
||||
src: "../sound/timeWarning.wav",
|
||||
});
|
||||
}
|
||||
|
||||
async function initErrorSound(): Promise<void> {
|
||||
const Howl = (await gethowler()).Howl;
|
||||
if (errorSounds !== null) return;
|
||||
|
@ -610,6 +620,14 @@ function playScale(scale: ValidScales, scaleMeta: ScaleData): void {
|
|||
oscillatorNode.stop(audioCtx.currentTime + 2);
|
||||
}
|
||||
|
||||
export async function playTimeWarning(): Promise<void> {
|
||||
if (timeWarning === null) await initTimeWarning();
|
||||
const soundToPlay = timeWarning as Howl;
|
||||
soundToPlay.stop();
|
||||
soundToPlay.seek(0);
|
||||
soundToPlay.play();
|
||||
}
|
||||
|
||||
export function playNote(
|
||||
codeOverride?: string,
|
||||
oscillatorTypeOverride?: SupportedOscillatorTypes
|
||||
|
|
|
@ -285,6 +285,14 @@ async function initGroups(): Promise<void> {
|
|||
UpdateConfig.setSoundVolume,
|
||||
"range"
|
||||
) as SettingsGroup<ConfigValue>;
|
||||
groups["playTimeWarning"] = new SettingsGroup(
|
||||
"playTimeWarning",
|
||||
UpdateConfig.setPlayTimeWarning,
|
||||
"button",
|
||||
() => {
|
||||
if (Config.playTimeWarning !== "off") void Sound.playTimeWarning();
|
||||
}
|
||||
) as SettingsGroup<ConfigValue>;
|
||||
groups["playSoundOnError"] = new SettingsGroup(
|
||||
"playSoundOnError",
|
||||
UpdateConfig.setPlaySoundOnError,
|
||||
|
|
|
@ -17,6 +17,7 @@ import * as Time from "../states/time";
|
|||
import * as TimerEvent from "../observables/timer-event";
|
||||
import * as LayoutfluidFunboxTimer from "../test/funbox/layoutfluid-funbox-timer";
|
||||
import { KeymapLayout, Layout } from "@monkeytype/schemas/configs";
|
||||
import * as SoundController from "../controllers/sound-controller";
|
||||
|
||||
type TimerStats = {
|
||||
dateNow: number;
|
||||
|
@ -175,6 +176,26 @@ function checkIfTimeIsUp(): void {
|
|||
if (timerDebug) console.timeEnd("times up check");
|
||||
}
|
||||
|
||||
function playTimeWarning(): void {
|
||||
if (timerDebug) console.time("play timer warning");
|
||||
|
||||
let maxTime = undefined;
|
||||
|
||||
if (Config.mode === "time") {
|
||||
maxTime = Config.time;
|
||||
} else if (Config.mode === "custom" && CustomText.getLimitMode() === "time") {
|
||||
maxTime = CustomText.getLimitValue();
|
||||
}
|
||||
|
||||
if (
|
||||
maxTime !== undefined &&
|
||||
Time.get() === maxTime - parseInt(Config.playTimeWarning, 10)
|
||||
) {
|
||||
void SoundController.playTimeWarning();
|
||||
}
|
||||
if (timerDebug) console.timeEnd("play timer warning");
|
||||
}
|
||||
|
||||
// ---------------------------------------
|
||||
|
||||
let timerStats: TimerStats[] = [];
|
||||
|
@ -188,6 +209,7 @@ async function timerStep(): Promise<void> {
|
|||
Time.increment();
|
||||
premid();
|
||||
updateTimer();
|
||||
if (Config.playTimeWarning !== "off") playTimeWarning();
|
||||
const wpmAndRaw = calculateWpmRaw();
|
||||
const acc = calculateAcc();
|
||||
monkey(wpmAndRaw);
|
||||
|
|
BIN
frontend/static/sound/timeWarning.wav
Normal file
BIN
frontend/static/sound/timeWarning.wav
Normal file
Binary file not shown.
|
@ -355,6 +355,13 @@ export const CustomBackgroundSchema = z
|
|||
.or(z.literal(""));
|
||||
export type CustomBackground = z.infer<typeof CustomBackgroundSchema>;
|
||||
|
||||
export const PlayTimeWarningSchema = z
|
||||
.enum(["off", "1", "3", "5", "10"])
|
||||
.describe(
|
||||
"How many seconds before the end of the test to play a warning sound."
|
||||
);
|
||||
export type PlayTimeWarning = z.infer<typeof PlayTimeWarningSchema>;
|
||||
|
||||
export const ConfigSchema = z
|
||||
.object({
|
||||
// test
|
||||
|
@ -402,6 +409,7 @@ export const ConfigSchema = z
|
|||
soundVolume: SoundVolumeSchema,
|
||||
playSoundOnClick: PlaySoundOnClickSchema,
|
||||
playSoundOnError: PlaySoundOnErrorSchema,
|
||||
playTimeWarning: PlayTimeWarningSchema,
|
||||
|
||||
// caret
|
||||
smoothCaret: SmoothCaretSchema,
|
||||
|
@ -536,6 +544,7 @@ export const ConfigGroupsLiteral = {
|
|||
soundVolume: "sound",
|
||||
playSoundOnClick: "sound",
|
||||
playSoundOnError: "sound",
|
||||
playTimeWarning: "sound",
|
||||
|
||||
//caret
|
||||
smoothCaret: "caret",
|
||||
|
|
Loading…
Add table
Reference in a new issue