diff --git a/ui/recap/src/ui.ts b/ui/recap/src/ui.ts index b9649472221fd..f23b8a3d4f672 100644 --- a/ui/recap/src/ui.ts +++ b/ui/recap/src/ui.ts @@ -1,5 +1,5 @@ import Lpv from 'lichess-pgn-viewer'; -import PgnViewer from 'lichess-pgn-viewer/pgnViewer'; +import type PgnViewer from 'lichess-pgn-viewer/pgnViewer'; import { Opening } from './interfaces'; const numberFormat = window.Intl && Intl.NumberFormat ? new Intl.NumberFormat() : null; diff --git a/ui/recap/src/util.ts b/ui/recap/src/util.ts new file mode 100644 index 0000000000000..a8da0f5d9d313 --- /dev/null +++ b/ui/recap/src/util.ts @@ -0,0 +1,22 @@ +export function showDuration(seconds: number): string { + const d = Math.floor(seconds / (24 * 3600)); + const h = Math.floor((seconds % (24 * 3600)) / 3600); + const m = Math.floor((seconds % 3600) / 60); + + let result: string[] = []; + if (d > 0) { + result.push(simplePlural(d, 'day')); + } + if (h > 0) { + result.push(simplePlural(h, 'hour')); + } + if (m > 0 || seconds < 60) { + result.push(simplePlural(m, 'minute')); + } + + return result.slice(0, 2).join(' and '); +} + +function simplePlural(n: number, unit: string): string { + return `${n} ${unit}${n === 1 ? '' : 's'}`; +} diff --git a/ui/recap/src/view.ts b/ui/recap/src/view.ts index 999a9317c7a6d..ec9d09961a175 100644 --- a/ui/recap/src/view.ts +++ b/ui/recap/src/view.ts @@ -5,6 +5,7 @@ import { onInsert } from 'common/snabbdom'; import { loadOpeningLpv } from './ui'; import { fullName } from 'common/userLink'; import { spinnerVdom } from 'common/spinner'; +import { showDuration } from './util'; export function awaiter(user: LightUser): VNode { return h('div#recap-swiper.swiper.swiper-initialized', [ @@ -142,26 +143,3 @@ const animateNumber = (n: number) => h('span.animated-number', { attrs: { 'data- const showGrams = (g: number) => g > 20_000 ? h('span', [animateNumber(g / 1000), ' Kilograms']) : h('span', [animateNumber(g), ' grams']); - -function showDuration(seconds: number): string { - const days = Math.floor(seconds / (24 * 3600)); - seconds %= 24 * 3600; - const hours = Math.floor(seconds / 3600); - seconds %= 3600; - const minutes = Math.floor(seconds / 60); - - let result = ''; - if (days > 0) { - result += `${days} days`; - } - if (hours > 0) { - if (result) result += ' and '; - result += `${hours} hours`; - } - if (days == 0) { - if (result) result += ' and '; - result += `and ${minutes} minutes`; - } - - return result; -} diff --git a/ui/recap/tests/duration.test.ts b/ui/recap/tests/duration.test.ts new file mode 100644 index 0000000000000..c1104f1e1f1af --- /dev/null +++ b/ui/recap/tests/duration.test.ts @@ -0,0 +1,25 @@ +import { expect, test } from 'vitest'; +import { showDuration } from '../src/util'; + +test('singular and plural time units', () => { + expect(showDuration(0)).toBe('0 minutes'); + expect(showDuration(1)).toBe('0 minutes'); + expect(showDuration(2)).toBe('0 minutes'); + + expect(showDuration(60)).toBe('1 minute'); + expect(showDuration(120)).toBe('2 minutes'); + + expect(showDuration(60 * 60 * 1)).toBe('1 hour'); + expect(showDuration(60 * 60 * 2)).toBe('2 hours'); + + expect(showDuration(60 * 60 * 1 + 60 * 1)).toBe('1 hour and 1 minute'); + expect(showDuration(60 * 60 * 1 + 60 * 2)).toBe('1 hour and 2 minutes'); + expect(showDuration(60 * 60 * 2 + 60 * 1)).toBe('2 hours and 1 minute'); + expect(showDuration(60 * 60 * 2 + 60 * 2)).toBe('2 hours and 2 minutes'); + + expect(showDuration(60 * 60 * 24 * 1)).toBe('1 day'); + expect(showDuration(60 * 60 * 24 * 2)).toBe('2 days'); + + expect(showDuration(60 * 60 * 24 * 1 + 60 * 60 * 1 + 60 * 1)).toBe('1 day and 1 hour'); + expect(showDuration(60 * 60 * 24 * 2 + 60 * 60 * 2 + 60 * 2)).toBe('2 days and 2 hours'); +});