diff --git a/packages/hydrooj/src/handler/contest.ts b/packages/hydrooj/src/handler/contest.ts index 104aa2ae7..31522f3eb 100644 --- a/packages/hydrooj/src/handler/contest.ts +++ b/packages/hydrooj/src/handler/contest.ts @@ -108,7 +108,7 @@ export class ContestDetailHandler extends Handler { const psdict = {}; let rdict = {}; if (tsdoc) { - if (tsdoc.attend && !tsdoc.startAt) { + if (tsdoc.attend && !tsdoc.startAt && contest.isOngoing(tdoc)) { await contest.setStatus(domainId, tid, this.user._id, { startAt: new Date() }); tsdoc.startAt = new Date(); } @@ -240,13 +240,12 @@ export class ContestEditHandler extends Handler { for (const i in contest.RULES) rules[i] = contest.RULES[i].TEXT; let ts = Date.now(); ts = ts - (ts % (15 * Time.minute)) + 15 * Time.minute; - const dt = this.tdoc?.beginAt || new Date(ts); this.response.body = { rules, tdoc: this.tdoc, duration: tid ? (this.tdoc.endAt.getTime() - this.tdoc.beginAt.getTime()) / Time.hour : 2, pids: tid ? this.tdoc.pids.join(',') : '', - startAt: dt, + beginAt: this.tdoc?.beginAt || new Date(ts), page_name: tid ? 'contest_edit' : 'contest_create', }; } @@ -261,13 +260,15 @@ export class ContestEditHandler extends Handler { @param('pids', Types.Content) @param('rated', Types.Boolean) @param('code', Types.String, true) - @param('autoHide', Types.String, true) + @param('autoHide', Types.Boolean, true) @param('assign', Types.CommaSeperatedArray, true) @param('lock', Types.UnsignedInt, true) + @param('contestDuration', Types.Float, true) async post( domainId: string, tid: ObjectID, beginAtDate: string, beginAtTime: string, duration: number, title: string, content: string, rule: string, _pids: string, rated = false, _code = '', autoHide = false, assign: string[] = null, lock: number = null, + contestDuration?: number, ) { if (autoHide) this.checkPerm(PERM.PERM_EDIT_PROBLEM); const pids = _pids.replace(/,/g, ',').split(',').map((i) => +i).filter((i) => i); @@ -277,10 +278,11 @@ export class ContestEditHandler extends Handler { if (beginAtMoment.isSameOrAfter(endAt)) throw new ValidationError('duration'); const beginAt = beginAtMoment.toDate(); const lockAt = lock ? moment(endAt).add(-lock, 'minutes').toDate() : null; + contestDuration ||= duration; await problem.getList(domainId, pids, this.user.hasPerm(PERM.PERM_VIEW_PROBLEM_HIDDEN) || this.user._id, this.user.group, true); if (tid) { await contest.edit(domainId, tid, { - title, content, rule, beginAt, endAt, pids, rated, + title, content, rule, beginAt, endAt, pids, rated, duration: contestDuration, }); if (this.tdoc.beginAt !== beginAt || this.tdoc.endAt !== endAt || Array.isDiff(this.tdoc.pids, pids) || this.tdoc.rule !== rule @@ -288,7 +290,7 @@ export class ContestEditHandler extends Handler { await contest.recalcStatus(domainId, this.tdoc.docId); } } else { - tid = await contest.add(domainId, title, content, this.user._id, rule, beginAt, endAt, pids, rated); + tid = await contest.add(domainId, title, content, this.user._id, rule, beginAt, endAt, pids, rated, { duration: contestDuration }); } const task = { type: 'schedule', subType: 'contest', domainId, tid, diff --git a/packages/hydrooj/src/handler/problem.ts b/packages/hydrooj/src/handler/problem.ts index 778bd998f..43aafe94a 100644 --- a/packages/hydrooj/src/handler/problem.ts +++ b/packages/hydrooj/src/handler/problem.ts @@ -452,7 +452,7 @@ export class ProblemDetailHandler extends ProblemHandler { export class ProblemSubmitHandler extends ProblemDetailHandler { @param('tid', Types.ObjectID, true) async prepare(domainId: string, tid?: ObjectID) { - if (tid && !contest.isOngoing(this.tdoc)) throw new ContestNotLiveError(this.tdoc.docId); + if (tid && !contest.isOngoing(this.tdoc, this.tsdoc)) throw new ContestNotLiveError(this.tdoc.docId); } async get() { diff --git a/packages/hydrooj/src/model/contest.ts b/packages/hydrooj/src/model/contest.ts index 37f7cbc64..9c5efc170 100644 --- a/packages/hydrooj/src/model/contest.ts +++ b/packages/hydrooj/src/model/contest.ts @@ -546,23 +546,24 @@ export function isNew(tdoc: Tdoc, days = 1) { } export function isUpcoming(tdoc: Tdoc, days = 7) { - const now = new Date().getTime(); + const now = Date.now(); const readyAt = tdoc.beginAt.getTime(); - return (now > readyAt - days * Time.day && now < tdoc.beginAt.getTime()); + return (now > readyAt - days * Time.day && now < readyAt); } export function isNotStarted(tdoc: Tdoc) { return (new Date()) < tdoc.beginAt; } -export function isOngoing(tdoc: Tdoc) { +export function isOngoing(tdoc: Tdoc, tsdoc?: any) { const now = new Date(); + if (tsdoc && tsdoc?.startAt <= new Date(Date.now() - Math.floor(tdoc.duration * Time.hour))) return false; return (tdoc.beginAt <= now && now < tdoc.endAt); } export function isDone(tdoc: Tdoc, tsdoc?: any) { if (tdoc.endAt <= new Date()) return true; - if (tsdoc?.startAt <= new Date(Date.now() + tdoc.duration * Time.hour)) return true; + if (tsdoc && tsdoc?.startAt <= new Date(Date.now() - Math.floor(tdoc.duration * Time.hour))) return true; return false; } @@ -658,22 +659,15 @@ export async function getScoreboard( return [tdoc, rows, udict, pdict, nPages]; } -export const statusText = (tdoc: Tdoc) => ( +export const statusText = (tdoc: Tdoc, tsdoc?: any) => ( isNew(tdoc) ? 'New' : isUpcoming(tdoc) ? 'Ready (☆▽☆)' - : isOngoing(tdoc) + : isOngoing(tdoc, tsdoc) ? 'Live...' : 'Done'); -export const getStatusText = (tdoc: Tdoc) => ( - isNotStarted(tdoc) - ? 'not_started' - : isOngoing(tdoc) - ? 'ongoing' - : 'finished'); - global.Hydro.model.contest = { RULES, add, @@ -704,5 +698,4 @@ global.Hydro.model.contest = { isLocked, isExtended, statusText, - getStatusText, }; diff --git a/packages/ui-default/package.json b/packages/ui-default/package.json index 2f0bfb36e..403a2fc24 100644 --- a/packages/ui-default/package.json +++ b/packages/ui-default/package.json @@ -25,6 +25,7 @@ "@types/json-schema": "^7.0.11", "@types/katex": "^0.14.0", "@types/markdown-it": "^12.2.3", + "@types/pickadate": "^3.5.32", "@types/qrcode": "^1.4.2", "@types/react-dom": "^18.0.4", "@types/redux-logger": "^3.0.9", diff --git a/packages/ui-default/templates/components/form.html b/packages/ui-default/templates/components/form.html index 44e8aefdb..47d3fc122 100644 --- a/packages/ui-default/templates/components/form.html +++ b/packages/ui-default/templates/components/form.html @@ -53,11 +53,13 @@ {% set extra_attr = args.extra_attr %} {% set date = args.date or false %} {% set time = args.time or false %} +{% set datetime = args.datetime or false %} class="{{ form_class }}{% if extra_class %} {{ extra_class }}{% endif %}" {% if disabled %}disabled{% endif %} {% if required %}required{% endif %} {% if autofocus %}autofocus data-autofocus{% endif %} {% if noAutocomplete %}autocomplete="off"{% endif %} + {% if datetime %}data-pick-datetime{% endif %} {% if date %}data-pick-date{% endif %} {% if time %}data-pick-time{% endif %} {% if extra_attr %}{{ extra_attr }}{% endif %} diff --git a/packages/ui-default/templates/contest_detail.html b/packages/ui-default/templates/contest_detail.html index fafb2d744..811e1ab7d 100644 --- a/packages/ui-default/templates/contest_detail.html +++ b/packages/ui-default/templates/contest_detail.html @@ -11,7 +11,7 @@
{% endif %}{{ _('We will check code similarity after this contest.') }}