srdusr
aboutsummaryrefslogtreecommitdiffstats
path: root/web/src/screens
diff options
context:
space:
mode:
Diffstat (limited to 'web/src/screens')
-rw-r--r--web/src/screens/endScreen.js20
-rw-r--r--web/src/screens/multiplayerScreen.js6
-rw-r--r--web/src/screens/typingGame.js6
3 files changed, 17 insertions, 15 deletions
diff --git a/web/src/screens/endScreen.js b/web/src/screens/endScreen.js
index aebd62b..fa3b188 100644
--- a/web/src/screens/endScreen.js
+++ b/web/src/screens/endScreen.js
@@ -3,7 +3,6 @@ import { escapeHtml } from '../util.js';
import { attachTooltips } from '../tooltip.js';
import { onThemeChange } from '../theme.js';
import { renderSiteFooter } from '../siteFooter.js';
-import { renderAdSlot } from '../adSlot.js';
import { recordResult } from '../pb.js';
import { recordTest } from '../profileStats.js';
import { renderCornerRail } from '../cornerRail.js';
@@ -11,7 +10,7 @@ import { renderTopRail } from '../topRail.js';
import { submitTestResult } from '../auth.js';
import { detectDeviceType } from '../deviceDetect.js';
-export function renderEndScreen(root, { stats, text, attribution, explanation, standings, onShowPrivacy, onStandingsUpdate, onLeaveRace, userInput, charTimings, keypressHistory, modeKey, onPlayAgain, onMainMenu, onShowStats, onShowPlaceholder, onShowAccount, onShowLeaderboard, onShowFriends, onShowMultiplayer, onShowStore }) {
+export function renderEndScreen(root, { stats, text, attribution, category, explanation, standings, onShowPrivacy, onStandingsUpdate, onLeaveRace, userInput, charTimings, keypressHistory, modeKey, onPlayAgain, onMainMenu, onShowStats, onShowPlaceholder, onShowAccount, onShowLeaderboard, onShowFriends, onShowMultiplayer, onShowStore }) {
// Monkeytype's four-way split. "Extra" is anything typed past the end of
// the passage, "missed" is passage left untyped - neither is visible in
// a plain correct/incorrect pair, and the two mean very different things
@@ -21,7 +20,13 @@ export function renderEndScreen(root, { stats, text, attribution, explanation, s
const missedChars = Math.max(0, (text || '').length - typedLen);
const graphPoints = buildGraphPoints(charTimings, stats, keypressHistory);
const consistency = calculateConsistency(graphPoints);
- const { isNewBest, previous } = recordResult(modeKey, stats.wpm);
+ const isRace = Array.isArray(standings) && standings.length > 0;
+ // A race has no mode of its own, so it had no modeKey, so recordResult
+ // never ran and PB was missing from the multiplayer results entirely.
+ // Every race shares one key: the passage is whatever the server dealt, so
+ // a per-passage best would never be beaten twice.
+ const pbKey = modeKey || (isRace ? 'multiplayer' : null);
+ const { isNewBest, previous } = recordResult(pbKey, stats.wpm);
recordTest({ wpm: stats.wpm, accuracy: stats.accuracy, time: stats.time });
// Same exclusion as recordResult above: custom text/Zen have no stable,
// repeatable challenge, so there's no meaningful leaderboard entry for
@@ -48,7 +53,7 @@ export function renderEndScreen(root, { stats, text, attribution, explanation, s
// nothing to have beaten yet, so it just silently becomes the baseline.
// Sits under WPM alongside RAW, in the same label-over-value shape as
// every other secondary figure rather than as a floating badge.
- const pbLine = !modeKey || previous == null ? '' : isNewBest
+ const pbLine = !pbKey || previous == null ? '' : isNewBest
? '<div class="end-stat pb-new"><div class="stat-label">PB</div><div class="stat-value">NEW BEST</div></div>'
: `<div class="end-stat"><div class="stat-label">PB</div><div class="stat-value">${Math.round(previous)}</div></div>`;
@@ -56,7 +61,7 @@ export function renderEndScreen(root, { stats, text, attribution, explanation, s
<div class="end-screen">
<div class="logo" data-action="menu">TyperPunk</div>
<div class="end-screen-text"><div class="text-display"></div></div>
- ${attribution ? `<div class="attribution end-screen-attribution">&mdash; ${escapeHtml(attribution)}</div>` : ''}
+ <div class="attribution end-screen-attribution">&mdash; ${escapeHtml(attribution || 'Unknown')}${category ? `<span class="attribution-pack">${escapeHtml(category)}</span>` : ''}</div>
${explanation ? `<div class="code-explainer"><span class="code-explainer-label">What this does</span>${escapeHtml(explanation)}</div>` : ''}
<div class="end-screen-graph-row">
<div class="endscreen-side-stat wpm">
@@ -68,7 +73,7 @@ export function renderEndScreen(root, { stats, text, attribution, explanation, s
<div class="endscreen-side-stat acc">
<div class="end-stat headline"><div class="stat-label">ACC</div><div class="stat-value">${Math.round(stats.accuracy)}%</div></div>
<div class="end-stat"><div class="stat-label">ERR</div><div class="stat-value">${stats.incorrectChars}</div></div>
- <div class="end-stat"><div class="stat-label">CONSISTENCY</div><div class="stat-value">${consistency}%</div></div>
+ <div class="end-stat"><div class="stat-label">CON</div><div class="stat-value">${consistency}%</div></div>
</div>
</div>
<div class="end-screen-stat-row">
@@ -83,7 +88,6 @@ export function renderEndScreen(root, { stats, text, attribution, explanation, s
<div class="mp-standings-heading">Standings</div>
<div class="mp-standings-rows"></div>
</div>` : ''}
- <div class="end-screen-ad"></div>
<div class="end-screen-buttons">
<button class="end-screen-button primary" data-action="again">Play Again</button>
</div>
@@ -149,7 +153,6 @@ export function renderEndScreen(root, { stats, text, attribution, explanation, s
const cleanupFooter = renderSiteFooter(root, { result: stats, onShowPrivacy });
// Below the results and above Play Again: read after the run is over,
// never during it.
- const cleanupAd = renderAdSlot(root.querySelector('.end-screen-ad'));
root.querySelectorAll('[data-action="menu"]').forEach(el => el.addEventListener('click', onMainMenu));
root.querySelector('[data-action="again"]').addEventListener('click', onPlayAgain);
@@ -239,7 +242,6 @@ export function renderEndScreen(root, { stats, text, attribution, explanation, s
onLeaveRace?.();
cleanupTheme();
cleanupFooter();
- cleanupAd();
window.removeEventListener('resize', redraw);
offTheme();
canvas.removeEventListener('mousemove', handleMove);
diff --git a/web/src/screens/multiplayerScreen.js b/web/src/screens/multiplayerScreen.js
index d3ae7d9..a69080d 100644
--- a/web/src/screens/multiplayerScreen.js
+++ b/web/src/screens/multiplayerScreen.js
@@ -230,7 +230,7 @@ export function renderMultiplayerScreen(root, { onBack, onFinish, onShowStats, o
// The passage arrives before the countdown so it can be read while the
// numbers run. The race view is built here, with typing locked; Start
// only unlocks it.
- const offRaceText = connection.on('raceText', (text, attribution) => startRace(text, attribution));
+ const offRaceText = connection.on('raceText', (text, attribution, category) => startRace(text, attribution, category));
const offStart = connection.on('start', () => releaseRace?.());
// The server closes the connection right after an Error (e.g. a
// desktop-only room rejecting a mobile joiner) - 'close' always
@@ -255,7 +255,7 @@ export function renderMultiplayerScreen(root, { onBack, onFinish, onShowStats, o
};
}
- async function startRace(text, attribution) {
+ async function startRace(text, attribution, category) {
teardownInner();
try {
game = await createGame();
@@ -337,7 +337,7 @@ export function renderMultiplayerScreen(root, { onBack, onFinish, onShowStats, o
});
const cleanupTyping = renderTypingGame(root, {
- game, text, attribution, modeKey: undefined,
+ game, text, attribution, category, modeKey: undefined,
multiplayer: { connection },
onFinish: result => {
freeGame(game);
diff --git a/web/src/screens/typingGame.js b/web/src/screens/typingGame.js
index d65d254..6e2ac66 100644
--- a/web/src/screens/typingGame.js
+++ b/web/src/screens/typingGame.js
@@ -10,7 +10,7 @@ import { recordKeystroke } from '../keyStats.js';
import { getUser } from '../auth.js';
import { api } from '../api.js';
-export function renderTypingGame(root, { game, text, attribution, explanation, language, progress, timeLimit, modeKey, zenMode, multiplayer, onFinish, onMainMenu, onRestart, onShowStats, onShowPlaceholder, onShowAccount, onShowLeaderboard, onShowFriends, onShowMultiplayer, onShowStore }) {
+export function renderTypingGame(root, { game, text, attribution, category, explanation, language, progress, timeLimit, modeKey, zenMode, multiplayer, onFinish, onMainMenu, onRestart, onShowStats, onShowPlaceholder, onShowAccount, onShowLeaderboard, onShowFriends, onShowMultiplayer, onShowStore }) {
const syntaxClasses = language ? highlightClasses(text, language) : null;
const { hideLiveStats, caretBlink, blindMode } = getSettings();
root.innerHTML = `
@@ -21,7 +21,7 @@ export function renderTypingGame(root, { game, text, attribution, explanation, l
<div class="text-display${language ? ' code' : ''}"></div>
<input class="typing-input" type="text" autocomplete="off" autocapitalize="off" autocorrect="off" spellcheck="false" />
</div>
- ${attribution ? `<div class="attribution">- ${escapeHtml(attribution)}</div>` : ''}
+ <div class="attribution">- ${escapeHtml(attribution || 'Unknown')}</div>
${explanation && !multiplayer ? `<div class="code-explainer"><span class="code-explainer-label">What this does</span>${escapeHtml(explanation)}</div>` : ''}
<div class="wpm-stat"><div class="stat-label">WPM</div><div class="stat-value" data-field="wpm">0</div></div>
<div class="acc-stat"><div class="stat-label">ACC</div><div class="stat-value" data-field="acc">100%</div></div>
@@ -180,7 +180,7 @@ export function renderTypingGame(root, { game, text, attribution, explanation, l
stats.accuracy = accuracy;
stats.incorrectChars = mistakes;
if (multiplayer) multiplayer.connection.sendFinish(stats.wpm, accuracy, elapsed);
- onFinish({ stats, text: finalText, attribution, explanation, userInput: finalInput, charTimings, keypressHistory: keystrokes, modeKey });
+ onFinish({ stats, text: finalText, attribution, category, explanation, userInput: finalInput, charTimings, keypressHistory: keystrokes, modeKey });
}
function handleChange() {