From 1f4df9199d3eb713071635f754e2a0f129b214a4 Mon Sep 17 00:00:00 2001 From: Christian Fehmer Date: Fri, 4 Aug 2023 13:22:27 +0200 Subject: [PATCH] Added configuration for typing speed unit, removed alwaysShowCPM (#4492) fehmer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Added configuration for typing speed unit, removed alwaysShowCPM * review comments * fix live-burst, activityChart and results-pb label * Added support for typing speed unit in account histogram chart * trigger build * Update account.ts * Fix chart scaling and wpm/rawWpm hovers on result page fix chart scaling and bucket size on account page * refactor histogramChartData to support 0.5 steps * Revert dynamic scaling on accounts/result graph * Refactor histogramChartData to an int[] * Fix cutoff in account history * Fix labels on result page * Limit result chart label to two decimals * renamed show average wpm to show average speed * fix scaling on accounts history graph (again), not adding an easteregg 🤫 * hiding by default * fix scaling on accounts history graph episode three * move typingSpeedUnit related functions out of Misc * updating account page if typing speed unit changes * updating result page if changing units on the result page * missing buton change --------- Co-authored-by: Miodec --- backend/src/api/schemas/config-schema.ts | 4 +- frontend/src/ts/account/pb-tables.ts | 19 +- frontend/src/ts/commandline/commands.ts | 4 +- .../ts/commandline/lists/always-show-cpm.ts | 35 ---- .../src/ts/commandline/lists/show-average.ts | 6 +- .../ts/commandline/lists/typing-speed-unit.ts | 60 ++++++ frontend/src/ts/config.ts | 36 +++- frontend/src/ts/constants/default-config.ts | 2 +- .../src/ts/controllers/chart-controller.ts | 11 +- frontend/src/ts/elements/leaderboards.ts | 39 ++-- frontend/src/ts/elements/modes-notice.ts | 11 +- frontend/src/ts/pages/account.ts | 175 ++++++++---------- frontend/src/ts/pages/settings.ts | 6 +- frontend/src/ts/test/live-burst.ts | 2 + frontend/src/ts/test/live-wpm.ts | 7 +- frontend/src/ts/test/result.ts | 164 ++++++++-------- frontend/src/ts/test/test-ui.ts | 7 +- frontend/src/ts/types/types.d.ts | 13 +- frontend/src/ts/utils/typing-speed-units.ts | 66 +++++++ frontend/static/html/pages/settings.html | 37 ++-- 20 files changed, 411 insertions(+), 293 deletions(-) delete mode 100644 frontend/src/ts/commandline/lists/always-show-cpm.ts create mode 100644 frontend/src/ts/commandline/lists/typing-speed-unit.ts create mode 100644 frontend/src/ts/utils/typing-speed-units.ts diff --git a/backend/src/api/schemas/config-schema.ts b/backend/src/api/schemas/config-schema.ts index 369bcf92b..c7a8728ae 100644 --- a/backend/src/api/schemas/config-schema.ts +++ b/backend/src/api/schemas/config-schema.ts @@ -106,7 +106,7 @@ const CONFIG_SCHEMA = joi.object({ minWpmCustomSpeed: joi.number().min(0), highlightMode: joi.string().valid("off", "letter", "word"), tapeMode: joi.string().valid("off", "letter", "word"), - alwaysShowCPM: joi.boolean(), + typingSpeedUnit: joi.string().valid("wpm", "cpm", "wps", "cps", "wph"), enableAds: joi.string().valid("off", "on", "max"), ads: joi.string().valid("off", "result", "on", "sellout"), hideExtraLetters: joi.boolean(), @@ -128,7 +128,7 @@ const CONFIG_SCHEMA = joi.object({ burstHeatmap: joi.boolean(), britishEnglish: joi.boolean(), lazyMode: joi.boolean(), - showAverage: joi.string().valid("off", "wpm", "acc", "both"), + showAverage: joi.string().valid("off", "speed", "acc", "both"), }); export default CONFIG_SCHEMA; diff --git a/frontend/src/ts/account/pb-tables.ts b/frontend/src/ts/account/pb-tables.ts index 71ba7bbf7..b75b4ca13 100644 --- a/frontend/src/ts/account/pb-tables.ts +++ b/frontend/src/ts/account/pb-tables.ts @@ -1,6 +1,7 @@ import Config from "../config"; import format from "date-fns/format"; import * as Misc from "../utils/misc"; +import { get as getTypingSpeedUnit } from "../utils/typing-speed-units"; function clearTables(isProfile: boolean): void { const source = isProfile ? "Profile" : "Account"; @@ -127,9 +128,9 @@ function buildPbHtml( ): string { let retval = ""; let dateText = ""; - const multiplier = Config.alwaysShowCPM ? 5 : 1; const modeString = `${mode2} ${mode === "time" ? "seconds" : "words"}`; - const wpmCpm = Config.alwaysShowCPM ? "cpm" : "wpm"; + const speedUnit = Config.typingSpeedUnit; + const typingSpeedUnit = getTypingSpeedUnit(Config.typingSpeedUnit); try { const pbData = (pbs[mode][mode2] ?? []).sort((a, b) => b.wpm - a.wpm)[0]; const date = new Date(pbData.timestamp); @@ -137,15 +138,15 @@ function buildPbHtml( dateText = format(date, "dd MMM yyyy"); } - let wpmString: number | string = pbData.wpm * multiplier; + let speedString: number | string = typingSpeedUnit.convert(pbData.wpm); if (Config.alwaysShowDecimalPlaces) { - wpmString = Misc.roundTo2(wpmString).toFixed(2); + speedString = Misc.roundTo2(speedString).toFixed(2); } else { - wpmString = Math.round(wpmString); + speedString = Math.round(speedString); } - wpmString += ` ${wpmCpm}`; + speedString += ` ${speedUnit}`; - let rawString: number | string = pbData.raw * multiplier; + let rawString: number | string = typingSpeedUnit.convert(pbData.raw); if (Config.alwaysShowDecimalPlaces) { rawString = Misc.roundTo2(rawString).toFixed(2); } else { @@ -179,14 +180,14 @@ function buildPbHtml( retval = `
${modeString}
-
${Math.round(pbData.wpm * multiplier)}
+
${Math.round(typingSpeedUnit.convert(pbData.wpm))}
${ pbData.acc === undefined ? "-" : Math.floor(pbData.acc) + "%" }
${modeString}
-
${wpmString}
+
${speedString}
${rawString}
${accString}
${conString}
diff --git a/frontend/src/ts/commandline/commands.ts b/frontend/src/ts/commandline/commands.ts index ad0ca7728..c8401dd02 100644 --- a/frontend/src/ts/commandline/commands.ts +++ b/frontend/src/ts/commandline/commands.ts @@ -28,7 +28,7 @@ import SoundVolumeCommands from "./lists/sound-volume"; import FlipTestColorsCommands from "./lists/flip-test-colors"; import SmoothLineScrollCommands from "./lists/smooth-line-scroll"; import AlwaysShowDecimalCommands from "./lists/always-show-decimal"; -import AlwaysShowCpmCommands from "./lists/always-show-cpm"; +import TypingSpeedUnitCommands from "./lists/typing-speed-unit"; import StartGraphsAtZeroCommands from "./lists/start-graphs-at-zero"; import LazyModeCommands from "./lists/lazy-mode"; import ShowAllLinesCommands from "./lists/show-all-lines"; @@ -275,7 +275,7 @@ export const commands: MonkeyTypes.CommandsSubgroup = { ...TapeModeCommands, ...SmoothLineScrollCommands, ...ShowAllLinesCommands, - ...AlwaysShowCpmCommands, + ...TypingSpeedUnitCommands, ...AlwaysShowDecimalCommands, ...StartGraphsAtZeroCommands, ...FontSizeCommands, diff --git a/frontend/src/ts/commandline/lists/always-show-cpm.ts b/frontend/src/ts/commandline/lists/always-show-cpm.ts deleted file mode 100644 index a2e193bb6..000000000 --- a/frontend/src/ts/commandline/lists/always-show-cpm.ts +++ /dev/null @@ -1,35 +0,0 @@ -import * as UpdateConfig from "../../config"; - -const subgroup: MonkeyTypes.CommandsSubgroup = { - title: "Always show CPM...", - configKey: "alwaysShowCPM", - list: [ - { - id: "setAlwaysShowCPMOff", - display: "off", - configValue: false, - exec: (): void => { - UpdateConfig.setAlwaysShowCPM(false); - }, - }, - { - id: "setAlwaysShowCPMOn", - display: "on", - configValue: true, - exec: (): void => { - UpdateConfig.setAlwaysShowCPM(true); - }, - }, - ], -}; - -const commands: MonkeyTypes.Command[] = [ - { - id: "changeAlwaysShowCPM", - display: "Always show CPM...", - icon: "fa-tachometer-alt", - subgroup, - }, -]; - -export default commands; diff --git a/frontend/src/ts/commandline/lists/show-average.ts b/frontend/src/ts/commandline/lists/show-average.ts index 46b88dabe..f2738d3b9 100644 --- a/frontend/src/ts/commandline/lists/show-average.ts +++ b/frontend/src/ts/commandline/lists/show-average.ts @@ -14,10 +14,10 @@ const subgroup: MonkeyTypes.CommandsSubgroup = { }, { id: "setShowAverageSpeed", - display: "wpm", - configValue: "wpm", + display: "speed", + configValue: "speed", exec: (): void => { - UpdateConfig.setShowAverage("wpm"); + UpdateConfig.setShowAverage("speed"); }, }, { diff --git a/frontend/src/ts/commandline/lists/typing-speed-unit.ts b/frontend/src/ts/commandline/lists/typing-speed-unit.ts new file mode 100644 index 000000000..d2884e058 --- /dev/null +++ b/frontend/src/ts/commandline/lists/typing-speed-unit.ts @@ -0,0 +1,60 @@ +import * as UpdateConfig from "../../config"; + +const subgroup: MonkeyTypes.CommandsSubgroup = { + title: "Typing speed unit...", + configKey: "typingSpeedUnit", + list: [ + { + id: "setTypingSpeedUnitWpm", + display: "wpm", + configValue: "wpm", + exec: (): void => { + UpdateConfig.setTypingSpeedUnit("wpm"); + }, + }, + { + id: "setTypingSpeedUnitCpm", + display: "cpm", + configValue: "cpm", + exec: (): void => { + UpdateConfig.setTypingSpeedUnit("cpm"); + }, + }, + { + id: "setTypingSpeedUnitWps", + display: "wps", + configValue: "wps", + exec: (): void => { + UpdateConfig.setTypingSpeedUnit("wps"); + }, + }, + { + id: "setTypingSpeedUnitCps", + display: "cps", + configValue: "cps", + exec: (): void => { + UpdateConfig.setTypingSpeedUnit("cps"); + }, + }, + { + id: "setTypingSpeedUnitWph", + display: "wph", + configValue: "wph", + visible: false, + exec: (): void => { + UpdateConfig.setTypingSpeedUnit("wph"); + }, + }, + ], +}; + +const commands: MonkeyTypes.Command[] = [ + { + id: "changeTypingSpeedUnit", + display: "Typing speed unit...", + icon: "fa-tachometer-alt", + subgroup, + }, +]; + +export default commands; diff --git a/frontend/src/ts/config.ts b/frontend/src/ts/config.ts index a1d6d2a3f..3606b3d26 100644 --- a/frontend/src/ts/config.ts +++ b/frontend/src/ts/config.ts @@ -409,12 +409,20 @@ export function setAlwaysShowDecimalPlaces( return true; } -export function setAlwaysShowCPM(val: boolean, nosave?: boolean): boolean { - if (!isConfigValueValid("always show CPM", val, ["boolean"])) return false; - - config.alwaysShowCPM = val; - saveToLocalStorage("alwaysShowCPM", nosave); - ConfigEvent.dispatch("alwaysShowCPM", config.alwaysShowCPM); +export function setTypingSpeedUnit( + val: MonkeyTypes.TypingSpeedUnit, + nosave?: boolean +): boolean { + if ( + !isConfigValueValid("typing speed unit", val, [ + ["wpm", "cpm", "wps", "cps", "wph"], + ]) + ) { + return false; + } + config.typingSpeedUnit = val; + saveToLocalStorage("typingSpeedUnit", nosave); + ConfigEvent.dispatch("typingSpeedUnit", config.typingSpeedUnit, nosave); return true; } @@ -888,7 +896,9 @@ export function setShowAverage( nosave?: boolean ): boolean { if ( - !isConfigValueValid("show average", value, [["off", "wpm", "acc", "both"]]) + !isConfigValueValid("show average", value, [ + ["off", "speed", "acc", "both"], + ]) ) { return false; } @@ -1901,7 +1911,7 @@ export function apply( setNumbers(configObj.numbers, true); setPunctuation(configObj.punctuation, true); setHighlightMode(configObj.highlightMode, true); - setAlwaysShowCPM(configObj.alwaysShowCPM, true); + setTypingSpeedUnit(configObj.typingSpeedUnit, true); setHideExtraLetters(configObj.hideExtraLetters, true); setStartGraphsAtZero(configObj.startGraphsAtZero, true); setStrictSpace(configObj.strictSpace, true); @@ -1976,6 +1986,16 @@ function replaceLegacyValues( configObj.quickRestart = "esc"; } + //@ts-ignore + if (configObj.alwaysShowCPM === true) { + configObj.typingSpeedUnit = "cpm"; + } + + //@ts-ignore + if (configObj.showAverage === "wpm") { + configObj.showAverage = "speed"; + } + return configObj; } diff --git a/frontend/src/ts/constants/default-config.ts b/frontend/src/ts/constants/default-config.ts index 85516aca1..b2e5d08f5 100644 --- a/frontend/src/ts/constants/default-config.ts +++ b/frontend/src/ts/constants/default-config.ts @@ -73,7 +73,7 @@ export default { minWpm: "off", minWpmCustomSpeed: 100, highlightMode: "letter", - alwaysShowCPM: false, + typingSpeedUnit: "wpm", ads: "result", hideExtraLetters: false, strictSpace: false, diff --git a/frontend/src/ts/controllers/chart-controller.ts b/frontend/src/ts/controllers/chart-controller.ts index 9e5e77c2d..4d1345f3e 100644 --- a/frontend/src/ts/controllers/chart-controller.ts +++ b/frontend/src/ts/controllers/chart-controller.ts @@ -343,6 +343,7 @@ export const accountHistory: ChartWithUpdateColors< }, wpm: { axis: "y", + type: "linear", beginAtZero: true, min: 0, ticks: { @@ -478,7 +479,7 @@ export const accountHistory: ChartWithUpdateColors< tooltipItem.dataIndex ] as MonkeyTypes.HistoryChartData; let label = - `${Config.alwaysShowCPM ? "cpm" : "wpm"}: ${resultData.wpm}` + + `${Config.typingSpeedUnit}: ${resultData.wpm}` + "\n" + `raw: ${resultData.raw}` + "\n" + @@ -652,9 +653,9 @@ export const accountActivity: ChartWithUpdateColors< true )}\nTests Completed: ${resultData.amount}`; case 1: - return `Average ${ - Config.alwaysShowCPM ? "Cpm" : "Wpm" - }: ${Misc.roundTo2(resultData.y)}`; + return `Average ${Config.typingSpeedUnit}: ${Misc.roundTo2( + resultData.y + )}`; default: return ""; } @@ -755,7 +756,7 @@ export const accountHistogram: ChartWithUpdateColors< // )}\nTests Completed: ${resultData.amount}`; // case 1: // return `Average ${ - // Config.alwaysShowCPM ? "Cpm" : "Wpm" + // Config.typingSpeedUnit // }: ${Misc.roundTo2(resultData.y)}`; // default: // return ""; diff --git a/frontend/src/ts/elements/leaderboards.ts b/frontend/src/ts/elements/leaderboards.ts index 00cfd13b6..6cbab8b82 100644 --- a/frontend/src/ts/elements/leaderboards.ts +++ b/frontend/src/ts/elements/leaderboards.ts @@ -2,6 +2,7 @@ import Ape from "../ape"; import * as DB from "../db"; import Config from "../config"; import * as Misc from "../utils/misc"; +import { get as getTypingSpeedUnit } from "../utils/typing-speed-units"; import * as Notifications from "./notifications"; import format from "date-fns/format"; import { Auth } from "../firebase"; @@ -150,6 +151,7 @@ function updateFooter(lb: LbKey): void { return; } + const typingSpeedUnit = getTypingSpeedUnit(Config.typingSpeedUnit); if (DB.getSnapshot()?.lbOptOut === true) { $(`#leaderboardsWrapper table.${side} tfoot`).html(` @@ -185,14 +187,10 @@ function updateFooter(lb: LbKey): void { ${entry.rank} You${toppercent ? toppercent : ""} - ${(Config.alwaysShowCPM - ? entry.wpm * 5 - : entry.wpm - ).toFixed(2)}
${entry.acc.toFixed(2)}%
- ${(Config.alwaysShowCPM - ? entry.raw * 5 - : entry.raw - ).toFixed(2)}
${ + ${typingSpeedUnit.convert(entry.wpm).toFixed(2)}
+
${entry.acc.toFixed(2)}%
+ ${typingSpeedUnit.convert(entry.raw).toFixed(2)}
+
${ !entry.consistency || entry.consistency === "-" ? "-" : entry.consistency.toFixed(2) + "%" @@ -264,6 +262,7 @@ async function fillTable(lb: LbKey, prepend?: number): Promise { ); } + const typingSpeedUnit = getTypingSpeedUnit(Config.typingSpeedUnit); const loggedInUserName = DB.getSnapshot()?.name; const snap = DB.getSnapshot(); @@ -342,14 +341,10 @@ async function fillTable(lb: LbKey, prepend?: number): Promise { ${entry.badgeId ? getBadgeHTMLbyId(entry.badgeId) : ""}
- ${(Config.alwaysShowCPM - ? entry.wpm * 5 - : entry.wpm - ).toFixed(2)}
${entry.acc.toFixed(2)}%
- ${(Config.alwaysShowCPM - ? entry.raw * 5 - : entry.raw - ).toFixed(2)}
${ + ${typingSpeedUnit.convert(entry.wpm).toFixed(2)}
+
${entry.acc.toFixed(2)}%
+ ${typingSpeedUnit.convert(entry.raw).toFixed(2)}
+
${ !entry.consistency || entry.consistency === "-" ? "-" : entry.consistency.toFixed(2) + "%" @@ -608,15 +603,9 @@ export function show(): void { "disabled" ); } - if (Config.alwaysShowCPM) { - $("#leaderboards table thead tr td:nth-child(3)").html( - 'cpm
accuracy
' - ); - } else { - $("#leaderboards table thead tr td:nth-child(3)").html( - 'wpm
accuracy
' - ); - } + $("#leaderboards table thead tr td:nth-child(3)").html( + Config.typingSpeedUnit + '
accuracy
' + ); $("#leaderboardsWrapper") .stop(true, true) .css("opacity", 0) diff --git a/frontend/src/ts/elements/modes-notice.ts b/frontend/src/ts/elements/modes-notice.ts index 2a766a034..bed2b1f24 100644 --- a/frontend/src/ts/elements/modes-notice.ts +++ b/frontend/src/ts/elements/modes-notice.ts @@ -7,6 +7,7 @@ import * as TestWords from "../test/test-words"; import * as ConfigEvent from "../observables/config-event"; import { Auth } from "../firebase"; import * as CustomTextState from "../states/custom-text-name"; +import { get as getTypingSpeedUnit } from "../utils/typing-speed-units"; ConfigEvent.subscribe((eventKey) => { if ( @@ -21,7 +22,7 @@ ConfigEvent.subscribe((eventKey) => { "confidenceMode", "layout", "showAverage", - "alwaysShowCPM", + "typingSpeedUnit", ].includes(eventKey) ) { update(); @@ -142,10 +143,10 @@ export async function update(): Promise { } if (Auth?.currentUser && avgWPM > 0) { - const avgWPMText = ["wpm", "both"].includes(Config.showAverage) - ? Config.alwaysShowCPM - ? `${Math.round(avgWPM * 5)} cpm` - : `${avgWPM} wpm` + const avgWPMText = ["speed", "both"].includes(Config.showAverage) + ? getTypingSpeedUnit(Config.typingSpeedUnit).convertWithUnitSuffix( + avgWPM + ) : ""; const avgAccText = ["acc", "both"].includes(Config.showAverage) diff --git a/frontend/src/ts/pages/account.ts b/frontend/src/ts/pages/account.ts index a65a1847e..55a1ce250 100644 --- a/frontend/src/ts/pages/account.ts +++ b/frontend/src/ts/pages/account.ts @@ -12,11 +12,14 @@ import * as TodayTracker from "../test/today-tracker"; import * as Notifications from "../elements/notifications"; import Page from "./page"; import * as Misc from "../utils/misc"; +import { get as getTypingSpeedUnit } from "../utils/typing-speed-units"; import * as Profile from "../elements/profile"; import format from "date-fns/format"; import * as ConnectionState from "../states/connection"; import * as Skeleton from "../popups/skeleton"; -import type { ScaleChartOptions } from "chart.js"; +import type { ScaleChartOptions, LinearScaleOptions } from "chart.js"; +import * as ConfigEvent from "../observables/config-event"; +import * as ActivePage from "../states/active-page"; import { Auth } from "../firebase"; let filterDebug = false; @@ -32,6 +35,7 @@ let filteredResults: MonkeyTypes.Result[] = []; let visibleTableLines = 0; function loadMoreLines(lineIndex?: number): void { + const typingSpeedUnit = getTypingSpeedUnit(Config.typingSpeedUnit); if (!filteredResults || filteredResults.length === 0) return; let newVisibleLines; if (lineIndex && lineIndex > visibleTableLines) { @@ -49,9 +53,7 @@ function loadMoreLines(lineIndex?: number): void { let raw; try { - raw = Config.alwaysShowCPM - ? (result.rawWpm * 5).toFixed(2) - : result.rawWpm.toFixed(2); + raw = typingSpeedUnit.convert(result.rawWpm).toFixed(2); if (raw === undefined) { raw = "-"; } @@ -159,7 +161,7 @@ function loadMoreLines(lineIndex?: number): void { $(".pageAccount .history table tbody").append(` ${pb} - ${(Config.alwaysShowCPM ? result.wpm * 5 : result.wpm).toFixed(2)} + ${typingSpeedUnit.convert(result.wpm).toFixed(2)} ${raw} ${result.acc.toFixed(2)}% ${consistency} @@ -264,13 +266,9 @@ async function fillContent(): Promise { }; } - interface HistogramChartData { - [key: string]: number; - } - const activityChartData: ActivityChartData = {}; - - const histogramChartData: HistogramChartData = {}; + const histogramChartData: number[] = []; + const typingSpeedUnit = getTypingSpeedUnit(Config.typingSpeedUnit); filteredResults = []; $(".pageAccount .history table tbody").empty(); @@ -538,13 +536,18 @@ async function fillContent(): Promise { }; } - const bucket = Math.floor(Math.round(result.wpm) / 10) * 10; + const bucketSize = typingSpeedUnit.histogramDataBucketSize; + const bucket = Math.floor( + typingSpeedUnit.convert(result.wpm) / bucketSize + ); - if (Object.keys(histogramChartData).includes(String(bucket))) { - histogramChartData[bucket]++; - } else { - histogramChartData[bucket] = 1; + //grow array if needed + if (histogramChartData.length <= bucket) { + for (let i = histogramChartData.length; i <= bucket; i++) { + histogramChartData.push(0); + } } + histogramChartData[bucket]++; let tt = 0; if ( @@ -616,8 +619,8 @@ async function fillContent(): Promise { chartData.push({ x: filteredResults.length, - y: Config.alwaysShowCPM ? Misc.roundTo2(result.wpm * 5) : result.wpm, - wpm: Config.alwaysShowCPM ? Misc.roundTo2(result.wpm * 5) : result.wpm, + y: Misc.roundTo2(typingSpeedUnit.convert(result.wpm)), + wpm: Misc.roundTo2(typingSpeedUnit.convert(result.wpm)), acc: result.acc, mode: result.mode, mode2: result.mode2, @@ -625,9 +628,7 @@ async function fillContent(): Promise { language: result.language, timestamp: result.timestamp, difficulty: result.difficulty, - raw: Config.alwaysShowCPM - ? Misc.roundTo2(result.rawWpm * 5) - : result.rawWpm, + raw: Misc.roundTo2(typingSpeedUnit.convert(result.rawWpm)), isPb: result.isPb ?? false, }); @@ -656,11 +657,9 @@ async function fillContent(): Promise { } ); - if (Config.alwaysShowCPM) { - $(".pageAccount .group.history table thead tr td:nth-child(2)").text("cpm"); - } else { - $(".pageAccount .group.history table thead tr td:nth-child(2)").text("wpm"); - } + $(".pageAccount .group.history table thead tr td:nth-child(2)").text( + Config.typingSpeedUnit + ); await Misc.sleep(0); loadMoreLines(); @@ -684,9 +683,7 @@ async function fillContent(): Promise { activityChartData_avgWpm.push({ x: dateInt, y: Misc.roundTo2( - (Config.alwaysShowCPM - ? activityChartData[dateInt].totalWpm * 5 - : activityChartData[dateInt].totalWpm) / + typingSpeedUnit.convert(activityChartData[dateInt].totalWpm) / activityChartData[dateInt].amount ), }); @@ -697,11 +694,8 @@ async function fillContent(): Promise { ChartController.accountActivity.options as ScaleChartOptions<"bar" | "line"> ).scales; - if (Config.alwaysShowCPM) { - accountActivityScaleOptions["avgWpm"].title.text = "Average Cpm"; - } else { - accountActivityScaleOptions["avgWpm"].title.text = "Average Wpm"; - } + accountActivityScaleOptions["avgWpm"].title.text = + "Average " + Config.typingSpeedUnit; ChartController.accountActivity.data.datasets[0].data = activityChartData_time; @@ -711,21 +705,17 @@ async function fillContent(): Promise { const histogramChartDataBucketed: { x: number; y: number }[] = []; const labels: string[] = []; - const keys = Object.keys(histogramChartData); - for (let i = 0; i < keys.length; i++) { - const bucket = parseInt(keys[i]); - labels.push(`${bucket} - ${bucket + 9}`); + const bucketSize = typingSpeedUnit.histogramDataBucketSize; + const bucketSizeUpperBound = bucketSize - (bucketSize <= 1 ? 0.01 : 1); + + histogramChartData.forEach((amount: number, i: number) => { + const bucket = i * bucketSize; + labels.push(`${bucket} - ${bucket + bucketSizeUpperBound}`); histogramChartDataBucketed.push({ x: bucket, - y: histogramChartData[bucket], + y: amount, }); - if (bucket + 10 !== parseInt(keys[i + 1])) { - for (let j = bucket + 10; j < parseInt(keys[i + 1]); j += 10) { - histogramChartDataBucketed.push({ x: j, y: 0 }); - labels.push(`${j} - ${j + 9}`); - } - } - } + }); ChartController.accountHistogram.data.labels = labels; ChartController.accountHistogram.data.datasets[0].data = @@ -735,11 +725,10 @@ async function fillContent(): Promise { ChartController.accountHistory.options as ScaleChartOptions<"line"> ).scales; - if (Config.alwaysShowCPM) { - accountHistoryScaleOptions["wpm"].title.text = "Characters per Minute"; - } else { - accountHistoryScaleOptions["wpm"].title.text = "Words per Minute"; - } + const accountHistoryWpmOptions = accountHistoryScaleOptions[ + "wpm" + ] as LinearScaleOptions; + accountHistoryWpmOptions.title.text = typingSpeedUnit.fullUnitString; if (chartData.length > 0) { // get pb points @@ -803,26 +792,29 @@ async function fillContent(): Promise { const wpms = chartData.map((r) => r.y); const minWpmChartVal = Math.min(...wpms); const maxWpmChartVal = Math.max(...wpms); + const wpmStepSize = typingSpeedUnit.historyStepSize; + const maxWpmChartValWithBuffer = + Math.floor(maxWpmChartVal) + + (wpmStepSize - (Math.floor(maxWpmChartVal) % wpmStepSize)); // let accuracies = accChartData.map((r) => r.y); - accountHistoryScaleOptions["wpm"].max = - Math.floor(maxWpmChartVal) + (10 - (Math.floor(maxWpmChartVal) % 10)); - accountHistoryScaleOptions["pb"].max = - Math.floor(maxWpmChartVal) + (10 - (Math.floor(maxWpmChartVal) % 10)); - accountHistoryScaleOptions["wpmAvgTen"].max = - Math.floor(maxWpmChartVal) + (10 - (Math.floor(maxWpmChartVal) % 10)); - accountHistoryScaleOptions["wpmAvgHundred"].max = - Math.floor(maxWpmChartVal) + (10 - (Math.floor(maxWpmChartVal) % 10)); + accountHistoryWpmOptions.max = maxWpmChartValWithBuffer; + + accountHistoryWpmOptions.ticks.stepSize = wpmStepSize; + + accountHistoryScaleOptions["pb"].max = maxWpmChartValWithBuffer; + accountHistoryScaleOptions["wpmAvgTen"].max = maxWpmChartValWithBuffer; + accountHistoryScaleOptions["wpmAvgHundred"].max = maxWpmChartValWithBuffer; if (!Config.startGraphsAtZero) { const minWpmChartValFloor = Math.floor(minWpmChartVal); - accountHistoryScaleOptions["wpm"].min = minWpmChartValFloor; + accountHistoryWpmOptions.min = minWpmChartValFloor; accountHistoryScaleOptions["pb"].min = minWpmChartValFloor; accountHistoryScaleOptions["wpmAvgTen"].min = minWpmChartValFloor; accountHistoryScaleOptions["wpmAvgHundred"].min = minWpmChartValFloor; } else { - accountHistoryScaleOptions["wpm"].min = 0; + accountHistoryWpmOptions.min = 0; accountHistoryScaleOptions["pb"].min = 0; accountHistoryScaleOptions["wpmAvgTen"].min = 0; accountHistoryScaleOptions["wpmAvgHundred"].min = 0; @@ -852,38 +844,31 @@ async function fillContent(): Promise { Misc.secondsToString(Math.round(totalSecondsFiltered), true, true) ); - let highestSpeed: number | string = topWpm; - if (Config.alwaysShowCPM) { - highestSpeed = topWpm * 5; - } + let highestSpeed: number | string = typingSpeedUnit.convert(topWpm); + if (Config.alwaysShowDecimalPlaces) { highestSpeed = Misc.roundTo2(highestSpeed).toFixed(2); } else { highestSpeed = Math.round(highestSpeed); } - const wpmCpm = Config.alwaysShowCPM ? "cpm" : "wpm"; + const speedUnit = Config.typingSpeedUnit; - $(".pageAccount .highestWpm .title").text(`highest ${wpmCpm}`); + $(".pageAccount .highestWpm .title").text(`highest ${speedUnit}`); $(".pageAccount .highestWpm .val").text(highestSpeed); - let averageSpeed: number | string = totalWpm; - if (Config.alwaysShowCPM) { - averageSpeed = totalWpm * 5; - } + let averageSpeed: number | string = typingSpeedUnit.convert(totalWpm); if (Config.alwaysShowDecimalPlaces) { averageSpeed = Misc.roundTo2(averageSpeed / testCount).toFixed(2); } else { averageSpeed = Math.round(averageSpeed / testCount); } - $(".pageAccount .averageWpm .title").text(`average ${wpmCpm}`); + $(".pageAccount .averageWpm .title").text(`average ${speedUnit}`); $(".pageAccount .averageWpm .val").text(averageSpeed); - let averageSpeedLast10: number | string = wpmLast10total; - if (Config.alwaysShowCPM) { - averageSpeedLast10 = wpmLast10total * 5; - } + let averageSpeedLast10: number | string = + typingSpeedUnit.convert(wpmLast10total); if (Config.alwaysShowDecimalPlaces) { averageSpeedLast10 = Misc.roundTo2(averageSpeedLast10 / last10).toFixed(2); } else { @@ -891,40 +876,33 @@ async function fillContent(): Promise { } $(".pageAccount .averageWpm10 .title").text( - `average ${wpmCpm} (last 10 tests)` + `average ${speedUnit} (last 10 tests)` ); $(".pageAccount .averageWpm10 .val").text(averageSpeedLast10); - let highestRawSpeed: number | string = rawWpm.max; - if (Config.alwaysShowCPM) { - highestRawSpeed = rawWpm.max * 5; - } + let highestRawSpeed: number | string = typingSpeedUnit.convert(rawWpm.max); if (Config.alwaysShowDecimalPlaces) { highestRawSpeed = Misc.roundTo2(highestRawSpeed).toFixed(2); } else { highestRawSpeed = Math.round(highestRawSpeed); } - $(".pageAccount .highestRaw .title").text(`highest raw ${wpmCpm}`); + $(".pageAccount .highestRaw .title").text(`highest raw ${speedUnit}`); $(".pageAccount .highestRaw .val").text(highestRawSpeed); - let averageRawSpeed: number | string = rawWpm.total; - if (Config.alwaysShowCPM) { - averageRawSpeed = rawWpm.total * 5; - } + let averageRawSpeed: number | string = typingSpeedUnit.convert(rawWpm.total); if (Config.alwaysShowDecimalPlaces) { averageRawSpeed = Misc.roundTo2(averageRawSpeed / rawWpm.count).toFixed(2); } else { averageRawSpeed = Math.round(averageRawSpeed / rawWpm.count); } - $(".pageAccount .averageRaw .title").text(`average raw ${wpmCpm}`); + $(".pageAccount .averageRaw .title").text(`average raw ${speedUnit}`); $(".pageAccount .averageRaw .val").text(averageRawSpeed); - let averageRawSpeedLast10: number | string = rawWpm.last10Total; - if (Config.alwaysShowCPM) { - averageRawSpeedLast10 = rawWpm.last10Total * 5; - } + let averageRawSpeedLast10: number | string = typingSpeedUnit.convert( + rawWpm.last10Total + ); if (Config.alwaysShowDecimalPlaces) { averageRawSpeedLast10 = Misc.roundTo2( averageRawSpeedLast10 / rawWpm.last10Count @@ -936,7 +914,7 @@ async function fillContent(): Promise { } $(".pageAccount .averageRaw10 .title").text( - `average raw ${wpmCpm} (last 10 tests)` + `average raw ${speedUnit} (last 10 tests)` ); $(".pageAccount .averageRaw10 .val").text(averageRawSpeedLast10); @@ -1029,11 +1007,8 @@ async function fillContent(): Promise { $(".pageAccount .group.chart .below .text").text( `Speed change per hour spent typing: ${ - plus + - Misc.roundTo2( - Config.alwaysShowCPM ? wpmChangePerHour * 5 : wpmChangePerHour - ) - } ${Config.alwaysShowCPM ? "cpm" : "wpm"}` + plus + Misc.roundTo2(typingSpeedUnit.convert(wpmChangePerHour)) + } ${Config.typingSpeedUnit}` ); $(".pageAccount .estimatedWordsTyped .val").text(totalEstimatedWords); @@ -1312,6 +1287,12 @@ $(".pageAccount .profile").on("click", ".details .copyLink", () => { ); }); +ConfigEvent.subscribe((eventKey) => { + if (ActivePage.get() === "account" && eventKey === "typingSpeedUnit") { + update(); + } +}); + export const page = new Page( "account", $(".page.pageAccount"), diff --git a/frontend/src/ts/pages/settings.ts b/frontend/src/ts/pages/settings.ts index 07b6870e5..615950a6f 100644 --- a/frontend/src/ts/pages/settings.ts +++ b/frontend/src/ts/pages/settings.ts @@ -379,9 +379,9 @@ async function initGroups(): Promise { UpdateConfig.setAlwaysShowDecimalPlaces, "button" ) as SettingsGroup; - groups["alwaysShowCPM"] = new SettingsGroup( - "alwaysShowCPM", - UpdateConfig.setAlwaysShowCPM, + groups["typingSpeedUnit"] = new SettingsGroup( + "typingSpeedUnit", + UpdateConfig.setTypingSpeedUnit, "button" ) as SettingsGroup; groups["customBackgroundSize"] = new SettingsGroup( diff --git a/frontend/src/ts/test/live-burst.ts b/frontend/src/ts/test/live-burst.ts index 6dcea2803..a2c4b9c3b 100644 --- a/frontend/src/ts/test/live-burst.ts +++ b/frontend/src/ts/test/live-burst.ts @@ -1,9 +1,11 @@ import Config from "../config"; import * as TestState from "../test/test-state"; import * as ConfigEvent from "../observables/config-event"; +import { get as getTypingSpeedUnit } from "../utils/typing-speed-units"; export async function update(burst: number): Promise { if (!Config.showLiveBurst) return; + burst = Math.round(getTypingSpeedUnit(Config.typingSpeedUnit).convert(burst)); (document.querySelector("#miniTimerAndLiveWpm .burst") as Element).innerHTML = burst.toString(); (document.querySelector("#liveBurst") as Element).innerHTML = diff --git a/frontend/src/ts/test/live-wpm.ts b/frontend/src/ts/test/live-wpm.ts index fbcacf61a..9f44c37c0 100644 --- a/frontend/src/ts/test/live-wpm.ts +++ b/frontend/src/ts/test/live-wpm.ts @@ -1,6 +1,7 @@ import Config from "../config"; import * as TestState from "../test/test-state"; import * as ConfigEvent from "../observables/config-event"; +import { get as getTypingSpeedUnit } from "../utils/typing-speed-units"; const liveWpmElement = document.querySelector("#liveWpm") as Element; const miniLiveWpmElement = document.querySelector( @@ -12,9 +13,9 @@ export function update(wpm: number, raw: number): void { if (Config.blindMode) { number = raw; } - if (Config.alwaysShowCPM) { - number = Math.round(number * 5); - } + number = Math.round( + getTypingSpeedUnit(Config.typingSpeedUnit).convert(number) + ); miniLiveWpmElement.innerHTML = number.toString(); liveWpmElement.innerHTML = number.toString(); } diff --git a/frontend/src/ts/test/result.ts b/frontend/src/ts/test/result.ts index 02a0477d9..170756b5c 100644 --- a/frontend/src/ts/test/result.ts +++ b/frontend/src/ts/test/result.ts @@ -17,6 +17,7 @@ import * as QuoteRatePopup from "../popups/quote-rate-popup"; import * as GlarsesMode from "../states/glarses-mode"; import * as SlowTimer from "../states/slow-timer"; import * as Misc from "../utils/misc"; +import { get as getTypingSpeedUnit } from "../utils/typing-speed-units"; import * as FunboxList from "./funbox/funbox-list"; import * as PbCrown from "./pb-crown"; import * as TestConfig from "./test-config"; @@ -24,6 +25,7 @@ import * as TestInput from "./test-input"; import * as TestStats from "./test-stats"; import * as TestUI from "./test-ui"; import * as TodayTracker from "./today-tracker"; +import * as ConfigEvent from "../observables/config-event"; import confetti from "canvas-confetti"; import type { AnnotationOptions } from "chartjs-plugin-annotation"; @@ -48,7 +50,9 @@ let resultScaleOptions = ( ).scales; async function updateGraph(): Promise { + const typingSpeedUnit = getTypingSpeedUnit(Config.typingSpeedUnit); const labels = []; + for (let i = 1; i <= TestInput.wpmHistory.length; i++) { if (TestStats.lastSecondNotRound && i === TestInput.wpmHistory.length) { labels.push(Misc.roundTo2(result.testDuration).toString()); @@ -56,21 +60,20 @@ async function updateGraph(): Promise { labels.push(i.toString()); } } - resultScaleOptions["wpm"].title.text = Config.alwaysShowCPM - ? "Characters per Minute" - : "Words per Minute"; + resultScaleOptions["wpm"].title.text = typingSpeedUnit.fullUnitString; + const chartData1 = [ - ...(Config.alwaysShowCPM - ? TestInput.wpmHistory.map((a) => a * 5) - : TestInput.wpmHistory), + ...TestInput.wpmHistory.map((a) => + Misc.roundTo2(typingSpeedUnit.convert(a)) + ), ]; if (result.chartData === "toolong") return; const chartData2 = [ - ...(Config.alwaysShowCPM - ? result.chartData.raw.map((a) => a * 5) - : result.chartData.raw), + ...result.chartData.raw.map((a) => + Misc.roundTo2(typingSpeedUnit.convert(a)) + ), ]; if ( @@ -92,14 +95,12 @@ async function updateGraph(): Promise { ChartController.result.data.labels = labels; ChartController.result.data.datasets[0].data = chartData1; ChartController.result.data.datasets[1].data = smoothedRawData; - - ChartController.result.data.datasets[0].label = Config.alwaysShowCPM - ? "cpm" - : "wpm"; + ChartController.result.data.datasets[0].label = Config.typingSpeedUnit; maxChartVal = Math.max( ...[Math.max(...smoothedRawData), Math.max(...chartData1)] ); + if (!Config.startGraphsAtZero) { const minChartVal = Math.min( ...[Math.min(...smoothedRawData), Math.min(...chartData1)] @@ -169,9 +170,8 @@ export async function updateGraphPBLine(): Promise { result.funbox ?? "none" ); if (lpb === 0) return; - const chartlpb = Misc.roundTo2(Config.alwaysShowCPM ? lpb * 5 : lpb).toFixed( - 2 - ); + const typingSpeedUnit = getTypingSpeedUnit(Config.typingSpeedUnit); + const chartlpb = Misc.roundTo2(typingSpeedUnit.convert(lpb)).toFixed(2); resultAnnotation.push({ display: true, type: "line", @@ -198,54 +198,49 @@ export async function updateGraphPBLine(): Promise { content: `PB: ${chartlpb}`, }, }); + const lpbRange = typingSpeedUnit.convert(20); if ( - maxChartVal >= parseFloat(chartlpb) - 20 && - maxChartVal <= parseFloat(chartlpb) + 20 + maxChartVal >= parseFloat(chartlpb) - lpbRange && + maxChartVal <= parseFloat(chartlpb) + lpbRange ) { - maxChartVal = parseFloat(chartlpb) + 15; + maxChartVal = Math.round(parseFloat(chartlpb) + lpbRange); } - resultScaleOptions["wpm"].max = Math.round(maxChartVal + 5); - resultScaleOptions["raw"].max = Math.round(maxChartVal + 5); + resultScaleOptions["wpm"].max = maxChartVal; + resultScaleOptions["raw"].max = maxChartVal; } function updateWpmAndAcc(): void { let inf = false; + const typingSpeedUnit = getTypingSpeedUnit(Config.typingSpeedUnit); if (result.wpm >= 1000) { inf = true; } + if (Config.alwaysShowDecimalPlaces) { - if (Config.alwaysShowCPM === false) { - $("#result .stats .wpm .top .text").text("wpm"); - if (inf) { - $("#result .stats .wpm .bottom").text("Infinite"); - } else { - $("#result .stats .wpm .bottom").text( - Misc.roundTo2(result.wpm).toFixed(2) - ); - } - $("#result .stats .raw .bottom").text( - Misc.roundTo2(result.rawWpm).toFixed(2) + $("#result .stats .wpm .top .text").text(Config.typingSpeedUnit); + if (inf) { + $("#result .stats .wpm .bottom").text("Infinite"); + } else { + $("#result .stats .wpm .bottom").text( + Misc.roundTo2(typingSpeedUnit.convert(result.wpm)).toFixed(2) ); + } + $("#result .stats .raw .bottom").text( + Misc.roundTo2(typingSpeedUnit.convert(result.rawWpm)).toFixed(2) + ); + + if (Config.typingSpeedUnit != "wpm") { $("#result .stats .wpm .bottom").attr( "aria-label", - Misc.roundTo2(result.wpm * 5).toFixed(2) + " cpm" + result.wpm.toFixed(2) + " wpm" + ); + $("#result .stats .raw .bottom").attr( + "aria-label", + result.rawWpm.toFixed(2) + " wpm" ); } else { - $("#result .stats .wpm .top .text").text("cpm"); - if (inf) { - $("#result .stats .wpm .bottom").text("Infinite"); - } else { - $("#result .stats .wpm .bottom").text( - Misc.roundTo2(result.wpm * 5).toFixed(2) - ); - } - $("#result .stats .raw .bottom").text( - Misc.roundTo2(result.rawWpm * 5).toFixed(2) - ); - $("#result .stats .wpm .bottom").attr( - "aria-label", - Misc.roundTo2(result.wpm).toFixed(2) + " wpm" - ); + $("#result .stats .wpm .bottom").removeAttr("aria-label"); + $("#result .stats .raw .bottom").removeAttr("aria-label"); } $("#result .stats .acc .bottom").text( @@ -256,7 +251,6 @@ function updateWpmAndAcc(): void { time = Misc.secondsToString(Misc.roundTo2(result.testDuration)); } $("#result .stats .time .bottom .text").text(time); - $("#result .stats .raw .bottom").removeAttr("aria-label"); // $("#result .stats .acc .bottom").removeAttr("aria-label"); $("#result .stats .acc .bottom").attr( @@ -265,34 +259,27 @@ function updateWpmAndAcc(): void { ); } else { //not showing decimal places - if (Config.alwaysShowCPM === false) { - $("#result .stats .wpm .top .text").text("wpm"); - $("#result .stats .wpm .bottom").attr( - "aria-label", - result.wpm + ` (${Misc.roundTo2(result.wpm * 5)} cpm)` - ); - if (inf) { - $("#result .stats .wpm .bottom").text("Infinite"); - } else { - $("#result .stats .wpm .bottom").text(Math.round(result.wpm)); - } - $("#result .stats .raw .bottom").text(Math.round(result.rawWpm)); - $("#result .stats .raw .bottom").attr("aria-label", result.rawWpm); - } else { - $("#result .stats .wpm .top .text").text("cpm"); - $("#result .stats .wpm .bottom").attr( - "aria-label", - Misc.roundTo2(result.wpm * 5) + ` (${Misc.roundTo2(result.wpm)} wpm)` - ); - if (inf) { - $("#result .stats .wpm .bottom").text("Infinite"); - } else { - $("#result .stats .wpm .bottom").text(Math.round(result.wpm * 5)); - } - $("#result .stats .raw .bottom").text(Math.round(result.rawWpm * 5)); - $("#result .stats .raw .bottom").attr("aria-label", result.rawWpm * 5); + let wpmHover = typingSpeedUnit.convertWithUnitSuffix(result.wpm); + let rawWpmHover = typingSpeedUnit.convertWithUnitSuffix(result.rawWpm); + if (Config.typingSpeedUnit != "wpm") { + wpmHover += " (" + result.wpm.toFixed(2) + " wpm)"; + rawWpmHover += " (" + result.rawWpm.toFixed(2) + " wpm)"; } + $("#result .stats .wpm .top .text").text(Config.typingSpeedUnit); + $("#result .stats .wpm .bottom").attr("aria-label", wpmHover); + if (inf) { + $("#result .stats .wpm .bottom").text("Infinite"); + } else { + $("#result .stats .wpm .bottom").text( + Math.round(typingSpeedUnit.convert(result.wpm)) + ); + } + $("#result .stats .raw .bottom").text( + Math.round(typingSpeedUnit.convert(result.rawWpm)) + ); + $("#result .stats .raw .bottom").attr("aria-label", rawWpmHover); + $("#result .stats .acc .bottom").text(Math.floor(result.acc) + "%"); $("#result .stats .acc .bottom").attr( "aria-label", @@ -424,10 +411,11 @@ export async function updateCrown(): Promise { Config.lazyMode, Config.funbox ); + const typingSpeedUnit = getTypingSpeedUnit(Config.typingSpeedUnit); pbDiff = Math.abs(result.wpm - lpb); $("#result .stats .wpm .crown").attr( "aria-label", - "+" + Misc.roundTo2(pbDiff) + "+" + Misc.roundTo2(typingSpeedUnit.convert(pbDiff)) ); } @@ -482,6 +470,7 @@ async function updateTags(dontSave: boolean): Promise { $("#result .stats .tags .bottom").append(`
${tag.display}
`); + const typingSpeedUnit = getTypingSpeedUnit(Config.typingSpeedUnit); if ( Config.mode !== "quote" && !dontSave && @@ -517,7 +506,7 @@ async function updateTags(dontSave: boolean): Promise { type: "line", id: "tpb", scaleID: "wpm", - value: Config.alwaysShowCPM ? tpb * 5 : tpb, + value: typingSpeedUnit.convert(tpb), borderColor: themecolors["sub"], borderWidth: 1, borderDash: [2, 2], @@ -537,7 +526,7 @@ async function updateTags(dontSave: boolean): Promise { xAdjust: labelAdjust, enabled: true, content: `${tag.display} PB: ${Misc.roundTo2( - Config.alwaysShowCPM ? tpb * 5 : tpb + typingSpeedUnit.convert(tpb) ).toFixed(2)}`, }, }); @@ -888,3 +877,22 @@ $(".pageTest #favoriteQuoteButton").on("click", async () => { } } }); + +ConfigEvent.subscribe(async (eventKey) => { + if (eventKey === "typingSpeedUnit" && TestUI.resultVisible) { + resultScaleOptions = ( + ChartController.result.options as ScaleChartOptions<"line" | "scatter"> + ).scales; + resultAnnotation = []; + + updateWpmAndAcc(); + await updateGraph(); + await updateGraphPBLine(); + + ((ChartController.result.options as PluginChartOptions<"line" | "scatter">) + .plugins.annotation.annotations as AnnotationOptions<"line">[]) = + resultAnnotation; + ChartController.result.updateColors(); + ChartController.result.resize(); + } +}); diff --git a/frontend/src/ts/test/test-ui.ts b/frontend/src/ts/test/test-ui.ts index 65230ff88..0aed1abfd 100644 --- a/frontend/src/ts/test/test-ui.ts +++ b/frontend/src/ts/test/test-ui.ts @@ -9,6 +9,7 @@ import * as Caret from "./caret"; import * as OutOfFocus from "./out-of-focus"; import * as Replay from "./replay"; import * as Misc from "../utils/misc"; +import { get as getTypingSpeedUnit } from "../utils/typing-speed-units"; import * as SlowTimer from "../states/slow-timer"; import * as CompositionState from "../states/composition"; import * as ConfigEvent from "../observables/config-event"; @@ -1244,9 +1245,9 @@ $(".pageTest #resultWordsHistory").on("mouseenter", ".words .word", (e) => { .replace(/>/g, ">")}
- ${Math.round(Config.alwaysShowCPM ? burst * 5 : burst)}${ - Config.alwaysShowCPM ? "cpm" : "wpm" - } + ${Math.round( + getTypingSpeedUnit(Config.typingSpeedUnit).convert(burst) + )}${Config.typingSpeedUnit}
` ); diff --git a/frontend/src/ts/types/types.d.ts b/frontend/src/ts/types/types.d.ts index d09a18091..1439a41a9 100644 --- a/frontend/src/ts/types/types.d.ts +++ b/frontend/src/ts/types/types.d.ts @@ -89,7 +89,7 @@ declare namespace MonkeyTypes { type KeymapShowTopRow = "always" | "layout" | "never"; - type ShowAverage = "off" | "wpm" | "acc" | "both"; + type ShowAverage = "off" | "speed" | "acc" | "both"; type SmoothCaretMode = "off" | "slow" | "medium" | "fast"; @@ -469,7 +469,7 @@ declare namespace MonkeyTypes { minWpm: MinimumWordsPerMinute; minWpmCustomSpeed: number; highlightMode: HighlightMode; - alwaysShowCPM: boolean; + typingSpeedUnit: TypingSpeedUnit; ads: Ads; hideExtraLetters: boolean; strictSpace: boolean; @@ -881,4 +881,13 @@ declare namespace MonkeyTypes { } type AllRewards = XpReward | BadgeReward; + + type TypingSpeedUnit = "wpm" | "cpm" | "wps" | "cps" | "wph"; + interface TypingSpeedUnitSettings { + convert: (number) => number; + convertWithUnitSuffix: (number) => string; + fullUnitString: string; + histogramDataBucketSize: number; + historyStepSize: number; + } } diff --git a/frontend/src/ts/utils/typing-speed-units.ts b/frontend/src/ts/utils/typing-speed-units.ts new file mode 100644 index 000000000..4d27ce2b6 --- /dev/null +++ b/frontend/src/ts/utils/typing-speed-units.ts @@ -0,0 +1,66 @@ +import { roundTo2 } from "../utils/misc"; +const typingSpeedUnits: Record< + MonkeyTypes.TypingSpeedUnit, + MonkeyTypes.TypingSpeedUnitSettings +> = { + wpm: { + convert: (wpm: number) => wpm, + convertWithUnitSuffix: (wpm: number) => { + return convertTypingSpeedWithUnitSuffix("wpm", wpm); + }, + + fullUnitString: "Words per Minute", + histogramDataBucketSize: 10, + historyStepSize: 10, + }, + cpm: { + convert: (wpm: number) => wpm * 5, + convertWithUnitSuffix: (wpm: number) => { + return convertTypingSpeedWithUnitSuffix("cpm", wpm); + }, + fullUnitString: "Characters per Minute", + histogramDataBucketSize: 50, + historyStepSize: 100, + }, + wps: { + convert: (wpm: number) => wpm / 60, + convertWithUnitSuffix: (wpm: number) => { + return convertTypingSpeedWithUnitSuffix("wps", wpm); + }, + fullUnitString: "Words per Second", + histogramDataBucketSize: 0.5, + historyStepSize: 2, + }, + cps: { + convert: (wpm: number) => (wpm * 5) / 60, + convertWithUnitSuffix: (wpm: number) => { + return convertTypingSpeedWithUnitSuffix("cps", wpm); + }, + fullUnitString: "Characters per Second", + histogramDataBucketSize: 5, + historyStepSize: 5, + }, + wph: { + convert: (wpm: number) => wpm * 60, + convertWithUnitSuffix: (wpm: number) => { + return convertTypingSpeedWithUnitSuffix("wph", wpm); + }, + + fullUnitString: "Words per Hour", + histogramDataBucketSize: 60, + historyStepSize: 1000, + }, +}; + +export function get( + unit: MonkeyTypes.TypingSpeedUnit +): MonkeyTypes.TypingSpeedUnitSettings { + return typingSpeedUnits[unit]; +} + +function convertTypingSpeedWithUnitSuffix( + unit: MonkeyTypes.TypingSpeedUnit, + wpm: number +): string { + return roundTo2(get(unit).convert(wpm)).toFixed(2) + " " + unit; +} diff --git a/frontend/static/html/pages/settings.html b/frontend/static/html/pages/settings.html index 31b7fbd84..7b01e0094 100644 --- a/frontend/static/html/pages/settings.html +++ b/frontend/static/html/pages/settings.html @@ -1584,31 +1584,44 @@
-
+
- always show cpm -
-
- Always shows characters per minute calculation instead of the default - words per minute calculation. + typing speed unit
+
Display typing speed in the specified unit.
- off + wpm
- on + cpm +
+
+ wps +
+
+ cps
@@ -2584,11 +2597,11 @@
- wpm + speed