diff --git a/packages/ui-default/components/contest/contest.page.styl b/packages/ui-default/components/contest/contest.page.styl index 5d8b4e8d8..209109d00 100644 --- a/packages/ui-default/components/contest/contest.page.styl +++ b/packages/ui-default/components/contest/contest.page.styl @@ -41,6 +41,14 @@ for key, value in $contest-color background: $primary-color border-color: $primary-color +.contest-timer + position: fixed; + left: 0; + top: 48px; + z-index: 10000; + +mobile() + visibility: hidden !important; + .contest-problems font-size: 0.9375rem; font-weight: 400; diff --git a/packages/ui-default/pages/contest.page.ts b/packages/ui-default/pages/contest.page.ts index 956f9572e..954705d93 100644 --- a/packages/ui-default/pages/contest.page.ts +++ b/packages/ui-default/pages/contest.page.ts @@ -1,5 +1,10 @@ +import { formatSeconds } from '@hydrooj/utils/lib/common'; import NProgress from 'nprogress'; import { NamedPage } from 'vj/misc/Page'; +import tpl from 'vj/utils/tpl'; + +const contestTimer = $(tpl``); +contestTimer.appendTo(document.body); export default new NamedPage(['contest_detail', 'contest_detail_problem', 'contest_scoreboard'], () => { const beginAt = new Date(UiContext.tdoc.beginAt).getTime(); @@ -7,11 +12,17 @@ export default new NamedPage(['contest_detail', 'contest_detail_problem', 'conte NProgress.configure({ trickle: false, showSpinner: false, minimum: 0 }); function updateProgress() { const now = Date.now(); - if (beginAt <= now && now <= endAt) NProgress.set((now - beginAt) / (endAt - beginAt)); - else if (now > endAt) NProgress.set(1); - else NProgress.set(0); + if (beginAt <= now && now <= endAt) { + NProgress.set((now - beginAt) / (endAt - beginAt)); + contestTimer.show(); + contestTimer.text(formatSeconds(Math.floor((endAt - now) / 1000))); + } else { + if (now > endAt) NProgress.set(1); + else NProgress.set(0); + contestTimer.hide(); + } } NProgress.start(); updateProgress(); - setInterval(updateProgress, 5000); + setInterval(updateProgress, 1000); }); diff --git a/packages/utils/lib/common.ts b/packages/utils/lib/common.ts index 0e6c9dc04..eaa9ef4b7 100644 --- a/packages/utils/lib/common.ts +++ b/packages/utils/lib/common.ts @@ -149,6 +149,20 @@ export function parseMemoryMB(str: string | number, throwOnError = true) { return Math.ceil(parseFloat(match[1]) * MEMORY_UNITS[match[2].toLowerCase()]); } +function _digit2(number: number) { + return number < 10 ? `0${number}` : number.toString(); +} + +export function formatSeconds(_seconds: string | number = '0', showSeconds = true) { + const seconds = +_seconds; + let res = '{0}:{1}'.format( + showSeconds ? _digit2(Math.floor(seconds / 3600)) : Math.floor(seconds / 3600), + _digit2(Math.floor((seconds % 3600) / 60)), + ); + if (showSeconds) res += `:${_digit2(seconds % 60)}`; + return res; +} + export function size(s: number, base = 1) { s *= base; const unit = 1024; diff --git a/packages/utils/lib/utils.ts b/packages/utils/lib/utils.ts index 6f4c1cedd..83cd47d43 100644 --- a/packages/utils/lib/utils.ts +++ b/packages/utils/lib/utils.ts @@ -190,20 +190,6 @@ export function errorMessage(err: Error | string) { return err; } -function _digit2(number: number) { - return number < 10 ? `0${number}` : number.toString(); -} - -export function formatSeconds(_seconds: string | number = '0', showSeconds = true) { - const seconds = +_seconds; - let res = '{0}:{1}'.format( - showSeconds ? _digit2(Math.floor(seconds / 3600)) : Math.floor(seconds / 3600), - _digit2(Math.floor((seconds % 3600) / 60)), - ); - if (showSeconds) res += `:${_digit2(seconds % 60)}`; - return res; -} - export function changeErrorType(err: any, Err: any) { const e = new Err(err.message); e.stack = err.stack;