ui: change to ts & tsx

This commit is contained in:
undefined committed 2021-10-11 23:33:54 +08:00
1 parent 36cce6d1bd
commit d10fec361c
29 files changed
+165 -221

No files matched your search

+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "hydrooj", "name": "hydrooj",
"version": "2.39.18", "version": "2.39.19",
"bin": "bin/hydrooj.js", "bin": "bin/hydrooj.js",
"main": "src/loader", "main": "src/loader",
"module": "src/loader", "module": "src/loader",
+1 -1
View File
@@ -47,7 +47,7 @@ export const ForbiddenError = Err('ForbiddenError', UserFacingError, 'ForbiddenE
export const NotFoundError = Err('NotFoundError', UserFacingError, 'NotFoundError', 404); export const NotFoundError = Err('NotFoundError', UserFacingError, 'NotFoundError', 404);
export const MethodNotAllowedError = Err('MethodNotAllowedError', UserFacingError, 'MethodNotAllowedError', 405); export const MethodNotAllowedError = Err('MethodNotAllowedError', UserFacingError, 'MethodNotAllowedError', 405);
export const RemoteOnlineJudgeError = Err('RemoteOnlineJudgeError', UserFacingError, 'RemoteOnlineJudgeError', 500); export const RemoteOnlineJudgeError = Err('RemoteOnlineJudgeError', UserFacingError, 'RemoteOnlineJudgeError', 500);
export const SendMailError = Err('SendMailError', UserFacingError, 'Failed to send mail to {0}.', 500); export const SendMailError = Err('SendMailError', UserFacingError, 'Failed to send mail to {0}. (1)', 500);
export const AlreadyVotedError = Err('AlreadyVotedError', ForbiddenError, "You've already voted."); export const AlreadyVotedError = Err('AlreadyVotedError', ForbiddenError, "You've already voted.");
export const LoginError = Err('LoginError', ForbiddenError, 'Invalid password for user {0}.'); export const LoginError = Err('LoginError', ForbiddenError, 'Invalid password for user {0}.');
-15
View File
@@ -49,11 +49,6 @@ function set(key: string, value: any) {
class SystemHandler extends Handler { class SystemHandler extends Handler {
async prepare() { async prepare() {
this.checkPriv(PRIV.PRIV_EDIT_SYSTEM); this.checkPriv(PRIV.PRIV_EDIT_SYSTEM);
this.response.body = {
path: [
['Hydro', 'homepage'],
],
};
} }
} }
@@ -86,12 +81,6 @@ class SystemCheckConnHandler extends ConnectionHandler {
class SystemDashboardHandler extends SystemHandler { class SystemDashboardHandler extends SystemHandler {
async get() { async get() {
this.response.template = 'manage_dashboard.html'; this.response.template = 'manage_dashboard.html';
this.response.body.path.push(['manage_dashboard', null]);
}
async postRestart() {
// TODO handle restart
this.back();
} }
} }
@@ -99,7 +88,6 @@ class SystemScriptHandler extends SystemHandler {
async get() { async get() {
this.response.template = 'manage_script.html'; this.response.template = 'manage_script.html';
this.response.body.scripts = global.Hydro.script; this.response.body.scripts = global.Hydro.script;
this.response.body.path.push(['manage_script', null]);
} }
@param('id', Types.Name) @param('id', Types.Name)
@@ -147,7 +135,6 @@ class SystemScriptHandler extends SystemHandler {
class SystemSettingHandler extends SystemHandler { class SystemSettingHandler extends SystemHandler {
async get() { async get() {
this.response.template = 'manage_setting.html'; this.response.template = 'manage_setting.html';
this.response.body.path.push(['manage_setting', null]);
this.response.body.current = {}; this.response.body.current = {};
this.response.body.settings = setting.SYSTEM_SETTINGS; this.response.body.settings = setting.SYSTEM_SETTINGS;
for (const s of this.response.body.settings) { for (const s of this.response.body.settings) {
@@ -185,7 +172,6 @@ class SystemSettingHandler extends SystemHandler {
class SystemUserImportHandler extends SystemHandler { class SystemUserImportHandler extends SystemHandler {
async get() { async get() {
this.response.body.users = []; this.response.body.users = [];
this.response.body.path.push(['manage_user_import']);
this.response.template = 'manage_user_import.html'; this.response.template = 'manage_user_import.html';
} }
@@ -232,7 +218,6 @@ class SystemUserImportHandler extends SystemHandler {
} }
} }
} }
this.response.body.path.push(['manage_user_import']);
this.response.body.users = udocs; this.response.body.users = udocs;
this.response.body.messages = messages; this.response.body.messages = messages;
} }
-30
View File
@@ -13,35 +13,6 @@ import {
} from '../service/server'; } from '../service/server';
import { sortFiles } from '../utils'; import { sortFiles } from '../utils';
class CheckInHandler extends Handler {
async prepare() {
const { checkincnt, lastcheckin } = this.user;
const today = new Date();
today.setHours(0);
today.setMinutes(0);
today.setSeconds(0);
today.setMilliseconds(0);
if (lastcheckin === today.getTime()) throw new BadRequestError("You've already checked in today!");
this.user.checkincnt++;
this.user.lastcheckin = today.getTime();
await Promise.all([
user.setById(this.user._id, { checkincnt, lastcheckin }),
user.inc(this.user._id, 'rpdelta', Math.round(Math.sqrt(checkincnt))),
]);
}
async get() {
this.response.redirect = '/';
}
async post() {
this.response.body = {
cnt: this.user.checkincnt,
last: this.user.lastcheckin,
};
}
}
class SwitchLanguageHandler extends Handler { class SwitchLanguageHandler extends Handler {
@param('lang', Types.Name) @param('lang', Types.Name)
async get(domainId: string, lang: string) { async get(domainId: string, lang: string) {
@@ -132,7 +103,6 @@ export class FSDownloadHandler extends Handler {
} }
export async function apply() { export async function apply() {
Route('check_in', '/checkin', CheckInHandler, PRIV.PRIV_USER_PROFILE);
Route('switch_language', '/language/:lang', SwitchLanguageHandler); Route('switch_language', '/language/:lang', SwitchLanguageHandler);
Route('home_files', '/file', FilesHandler, PRIV.PRIV_CREATE_FILE); Route('home_files', '/file', FilesHandler, PRIV.PRIV_CREATE_FILE);
Route('fs_download', '/file/:uid/:filename', FSDownloadHandler); Route('fs_download', '/file/:uid/:filename', FSDownloadHandler);
+2
View File
@@ -677,6 +677,8 @@ declare global {
addons: string[], addons: string[],
} }
} }
var Hydro: HydroGlobal; // eslint-disable-line
var addons: string[]; // eslint-disable-line
} }
declare module 'koa' { declare module 'koa' {
+1 -1
View File
@@ -33,7 +33,7 @@ export async function sendMail(to: string, subject: string, text: string, html:
}); });
} catch (e) { } catch (e) {
logger.error(e); logger.error(e);
throw new SendMailError(to); throw new SendMailError(to, e.message);
} }
return t; return t;
} }
-2
View File
@@ -131,8 +131,6 @@ AccountSetting(
'/components/profile/backgrounds/1.jpg', 'text', 'Profile Background Image', '/components/profile/backgrounds/1.jpg', 'text', 'Profile Background Image',
'Choose the background image in your profile page.'), 'Choose the background image in your profile page.'),
Setting('setting_storage', 'unreadMsg', 0, 'number', 'Unread Message Count', null, FLAG_DISABLED | FLAG_HIDDEN), Setting('setting_storage', 'unreadMsg', 0, 'number', 'Unread Message Count', null, FLAG_DISABLED | FLAG_HIDDEN),
Setting('setting_storage', 'checkincnt', 0, 'number', 'Check In Counter', null, FLAG_DISABLED | FLAG_HIDDEN),
Setting('setting_storage', 'lastcheckin', 0, 'number', 'Last checkin time', null, FLAG_DISABLED | FLAG_HIDDEN),
Setting('setting_storage', 'badge', '', 'text', 'badge info', null, FLAG_DISABLED | FLAG_HIDDEN), Setting('setting_storage', 'badge', '', 'text', 'badge info', null, FLAG_DISABLED | FLAG_HIDDEN),
); );
+11 -3
View File
@@ -62,6 +62,16 @@ module.exports = {
'no-unused-vars': 'off', 'no-unused-vars': 'off',
'@typescript-eslint/no-unused-vars': 'warn', '@typescript-eslint/no-unused-vars': 'warn',
'@typescript-eslint/lines-between-class-members': [
'error',
'always',
{ exceptAfterSingleLine: true },
],
'lines-between-class-members': [
'error',
'always',
{ exceptAfterSingleLine: true },
],
'comma-dangle': [ 'comma-dangle': [
'error', 'error',
'always-multiline', 'always-multiline',
@@ -69,9 +79,7 @@ module.exports = {
indent: [ indent: [
'error', 'error',
2, 2,
{ { SwitchCase: 0 },
SwitchCase: 0,
},
], ],
'max-len': ['error', 150], 'max-len': ['error', 150],
quotes: 'warn', quotes: 'warn',
@@ -2,20 +2,33 @@ import _ from 'lodash';
import DOMAttachedObject from 'vj/components/DOMAttachedObject'; import DOMAttachedObject from 'vj/components/DOMAttachedObject';
import zIndexManager from 'vj/utils/zIndexManager'; import zIndexManager from 'vj/utils/zIndexManager';
export interface DialogOptions {
classes: string;
$body: HTMLElement;
$action: any;
width?: string;
height?: string;
cancelByClickingBack?: boolean;
cancelByEsc?: boolean;
canCancel?: boolean;
onDispatch?: (data: any) => any;
}
export default class DomDialog extends DOMAttachedObject { export default class DomDialog extends DOMAttachedObject {
static DOMAttachKey = 'vjDomDialogInstance'; static DOMAttachKey = 'vjDomDialogInstance';
isShown = false;
isAnimating = false;
options: DialogOptions;
_resolve: (value: string | PromiseLike<string>) => void;
constructor($dom, options = {}) { constructor($dom, options: Partial<DialogOptions> = {}) {
super($dom); super($dom);
this.isShown = false;
this.isAnimating = false;
this.options = { this.options = {
cancelByClickingBack: false, cancelByClickingBack: false,
cancelByEsc: false, cancelByEsc: false,
onDispatch: () => { }, onDispatch: () => { },
...options, ...options as any,
}; };
this._defer = null;
} }
async _show() { async _show() {
@@ -70,9 +83,7 @@ export default class DomDialog extends DOMAttachedObject {
$(document).off(`keyup.${this.eventNS}`); $(document).off(`keyup.${this.eventNS}`);
this.$dom.off(`click.${this.eventNS}`); this.$dom.off(`click.${this.eventNS}`);
this.$dom.css({ this.$dom.css({ opacity: 1 });
opacity: 1,
});
this.$dom.transition({ this.$dom.transition({
opacity: 0, opacity: 0,
}, { }, {
@@ -80,17 +91,15 @@ export default class DomDialog extends DOMAttachedObject {
}); });
const $dgContent = this.$dom.find('.dialog__content'); const $dgContent = this.$dom.find('.dialog__content');
$dgContent.css({ $dgContent.css({ scale: 1 });
scale: 1,
});
await $dgContent await $dgContent
.transition({ .transition(
scale: 0.8, { scale: 0.8 },
}, { {
duration: 200, duration: 200,
easing: 'easeOutCubic', easing: 'easeOutCubic',
}) },
.promise(); ).promise();
this.$dom.css('display', 'none'); this.$dom.css('display', 'none');
@@ -103,28 +112,23 @@ export default class DomDialog extends DOMAttachedObject {
if (this.isShown || this.isAnimating) { if (this.isShown || this.isAnimating) {
return Promise.reject(); return Promise.reject();
} }
this._defer = new $.Deferred(); const promise = new Promise<string>((resolve) => {
this._resolve = resolve;
});
this._show(); this._show();
return this._defer.promise(); return promise;
} }
hide() { hide() {
if (!this.isShown || this.isAnimating) { if (!this.isShown || this.isAnimating) return false;
return false; this._resolve('cancel');
}
if (this._defer.state() === 'pending') {
this._defer.resolve('cancel');
}
this._defer = null;
this._hide(); this._hide();
return true; return true;
} }
dispatchAction(data) { dispatchAction(data: string) {
if (this.options.onDispatch(data) === false) { if (this.options.onDispatch(data) === false) return;
return; this._resolve(data);
}
this._defer.resolve(data);
this.hide(); this.hide();
} }
@@ -1,9 +1,13 @@
import tpl from 'vj/utils/tpl'; import tpl from 'vj/utils/tpl';
import i18n from 'vj/utils/i18n'; import i18n from 'vj/utils/i18n';
import DomDialog from './DomDialog'; import DomDialog, { DialogOptions } from './DomDialog';
export class Dialog { export class Dialog {
constructor(options = {}) { options: DialogOptions;
$dom: JQuery<any>;
domDialogInstance: DomDialog;
constructor(options: Partial<DialogOptions> = {}) {
this.options = { this.options = {
classes: '', classes: '',
$body: null, $body: null,
@@ -60,7 +64,7 @@ const buttonYes = tpl`<button class="primary rounded button" data-action="yes">$
const buttonNo = tpl`<button class="rounded button" data-action="no">${i18n('No')}</button>`; const buttonNo = tpl`<button class="rounded button" data-action="no">${i18n('No')}</button>`;
export class InfoDialog extends Dialog { export class InfoDialog extends Dialog {
constructor(options = {}) { constructor(options: Partial<DialogOptions> = {}) {
super({ super({
$action: buttonOk, $action: buttonOk,
cancelByClickingBack: true, cancelByClickingBack: true,
@@ -71,7 +75,7 @@ export class InfoDialog extends Dialog {
} }
export class ActionDialog extends Dialog { export class ActionDialog extends Dialog {
constructor(options = {}) { constructor(options: Partial<DialogOptions> = {}) {
super({ super({
$action: [buttonCancel, buttonOk].join('\n'), $action: [buttonCancel, buttonOk].join('\n'),
cancelByClickingBack: true, cancelByClickingBack: true,
@@ -82,7 +86,7 @@ export class ActionDialog extends Dialog {
} }
export class ConfirmDialog extends Dialog { export class ConfirmDialog extends Dialog {
constructor(options = {}) { constructor(options: Partial<DialogOptions> = {}) {
let buttons = []; let buttons = [];
if (options.canCancel) { if (options.canCancel) {
buttons = [buttonCancel, buttonNo, buttonYes]; buttons = [buttonCancel, buttonNo, buttonYes];
@@ -5,6 +5,7 @@ import pipeStream from 'vj/utils/pipeStream';
import i18n from 'vj/utils/i18n'; import i18n from 'vj/utils/i18n';
import request from 'vj/utils/request'; import request from 'vj/utils/request';
import Notification from 'vj/components/notification'; import Notification from 'vj/components/notification';
import api from 'vj/utils/api';
let isBeforeUnloadTriggeredByLibrary = !window.isSecureContext; let isBeforeUnloadTriggeredByLibrary = !window.isSecureContext;
function onBeforeUnload(e) { function onBeforeUnload(e) {
@@ -67,7 +68,24 @@ export async function downloadProblemSet(pids, name = 'Export') {
const targets = []; const targets = [];
try { try {
for (const pid of pids) { for (const pid of pids) {
const { pdoc } = await request.get(`/d/${UiContext.domainId}/p/${pid}`); const { pdoc } = await request.get(`/d/${UiContext.domainId}/api`);
await api(gql`
problem(id: ${pid}) {
pid
owner
title
content
tag
nSubmit
nAccept
data {
name
}
additional_file {
name
}
}
`);
targets.push({ targets.push({
filename: `${pid}/problem.yaml`, filename: `${pid}/problem.yaml`,
content: dump({ content: dump({
@@ -77,7 +95,6 @@ export async function downloadProblemSet(pids, name = 'Export') {
tag: pdoc.tag, tag: pdoc.tag,
nSubmit: pdoc.nSubmit, nSubmit: pdoc.nSubmit,
nAccept: pdoc.nAccept, nAccept: pdoc.nAccept,
difficulty: pdoc.difficulty,
}), }),
}); });
targets.push({ targets.push({
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@hydrooj/ui-default", "name": "@hydrooj/ui-default",
"version": "4.24.5", "version": "4.25.0",
"author": "undefined <i@undefined.moe>", "author": "undefined <i@undefined.moe>",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"main": "hydro.js", "main": "hydro.js",
@@ -1,9 +1,8 @@
import { NamedPage } from 'vj/misc/Page'; import { NamedPage } from 'vj/misc/Page';
const page = new NamedPage('contest_main', () => { const page = new NamedPage('contest_main', () => {
// Contest Filter $('[name="filter-form"] [name="rule"]').on('change', () => {
$('[name="filter-form"] [name="rule"]').change(() => { $('[name="filter-form"]').trigger('submit');
$('[name="filter-form"]').submit();
}); });
}); });
@@ -1,10 +0,0 @@
import { NamedPage } from 'vj/misc/Page';
const page = new NamedPage('discussion_detail', async () => {
$(document).on('click', '[name="problem-sidebar__show-category"]', (ev) => {
$(ev.currentTarget).hide();
$('[name="problem-sidebar__categories"]').show();
});
});
export default page;
@@ -1,10 +0,0 @@
import { NamedPage } from 'vj/misc/Page';
const page = new NamedPage('discussion_node', async () => {
$(document).on('click', '[name="problem-sidebar__show-category"]', (ev) => {
$(ev.currentTarget).hide();
$('[name="problem-sidebar__categories"]').show();
});
});
export default page;
@@ -6,13 +6,13 @@ const page = new NamedPage('domain_join_applications', () => {
const $expire = $('[name="expire"]'); const $expire = $('[name="expire"]');
const $code = $('[name="invitationCode"]'); const $code = $('[name="invitationCode"]');
function updateFormState() { function updateFormState() {
const method = parseInt($('[name="method"]').val(), 10); const method = +$('[name="method"]').val();
$role.prop('disabled', method === domainEnum.JOIN_METHOD_NONE).trigger('vjFormDisableUpdate'); $role.prop('disabled', method === domainEnum.JOIN_METHOD_NONE).trigger('vjFormDisableUpdate');
$expire.prop('disabled', method === domainEnum.JOIN_METHOD_NONE).trigger('vjFormDisableUpdate'); $expire.prop('disabled', method === domainEnum.JOIN_METHOD_NONE).trigger('vjFormDisableUpdate');
$code.prop('disabled', method !== domainEnum.JOIN_METHOD_CODE).trigger('vjFormDisableUpdate'); $code.prop('disabled', method !== domainEnum.JOIN_METHOD_CODE).trigger('vjFormDisableUpdate');
} }
updateFormState(); updateFormState();
$('[name="method"]').change(() => updateFormState()); $('[name="method"]').on('change', updateFormState);
}); });
export default page; export default page;
@@ -40,9 +40,7 @@ const page = new NamedPage('domain_role', () => {
async function handleClickCreateRole() { async function handleClickCreateRole() {
const action = await createRoleDialog.clear().open(); const action = await createRoleDialog.clear().open();
if (action !== 'ok') { if (action !== 'ok') return;
return;
}
const role = createRoleDialog.$dom.find('[name="role"]').val(); const role = createRoleDialog.$dom.find('[name="role"]').val();
try { try {
await request.post('', { await request.post('', {
@@ -1,9 +1,6 @@
import yaml from 'js-yaml'; import yaml from 'js-yaml';
import { getScoreColor } from '@hydrooj/utils/lib/status'; import { getScoreColor } from '@hydrooj/utils/lib/status';
import { NamedPage } from 'vj/misc/Page'; import { NamedPage } from 'vj/misc/Page';
import tpl from 'vj/utils/tpl';
import i18n from 'vj/utils/i18n';
import { ConfirmDialog } from 'vj/components/dialog';
import { downloadProblemSet } from 'vj/components/zipDownloader'; import { downloadProblemSet } from 'vj/components/zipDownloader';
import loadReactRedux from 'vj/utils/loadReactRedux'; import loadReactRedux from 'vj/utils/loadReactRedux';
import delay from 'vj/utils/delay'; import delay from 'vj/utils/delay';
@@ -263,15 +260,7 @@ const page = new NamedPage(['problem_detail', 'contest_detail_problem', 'homewor
leaveScratchpadMode(); leaveScratchpadMode();
ev.preventDefault(); ev.preventDefault();
}); });
$(document).on('click', '[name="problem-sidebar__rejudge"]', (ev) => {
ev.preventDefault();
new ConfirmDialog({
$body: tpl.typoMsg(i18n('Confirm rejudge this problem?')),
}).open().then((action) => {
if (action !== 'yes') return;
$(ev.currentTarget).closest('form').trigger('submit');
});
});
$(document).on('click', '[name="show_tags"]', (ev) => { $(document).on('click', '[name="show_tags"]', (ev) => {
$(ev.currentTarget).hide(); $(ev.currentTarget).hide();
$('span.tags').css('display', 'inline-block'); $('span.tags').css('display', 'inline-block');
@@ -144,10 +144,6 @@ function buildCategoryFilter() {
} }
export default new NamedPage(['problem_create', 'problem_edit'], (pagename) => { export default new NamedPage(['problem_create', 'problem_edit'], (pagename) => {
$(document).on('click', '[name="problem-sidebar__show-category"]', (ev) => {
$(ev.currentTarget).hide();
$('[name="problem-sidebar__categories"]').show();
});
let confirmed = false; let confirmed = false;
$(document).on('click', '[name="operation"]', (ev) => { $(document).on('click', '[name="operation"]', (ev) => {
ev.preventDefault(); ev.preventDefault();
@@ -178,7 +174,7 @@ export default new NamedPage(['problem_create', 'problem_edit'], (pagename) => {
$('img').each(function () { $('img').each(function () {
if (this.src.startsWith('file://')) this.setAttribute('src', this.src.replace('file://', './file/')); if (this.src.startsWith('file://')) this.setAttribute('src', this.src.replace('file://', './file/'));
}); });
}, 50); }, 200);
const $main = $('textarea[data-editor]'); const $main = $('textarea[data-editor]');
const $field = $('textarea[data-markdown-upload]'); const $field = $('textarea[data-markdown-upload]');
@@ -235,16 +235,6 @@ const page = new NamedPage('problem_files', () => {
await pjax.request({ push: false }); await pjax.request({ push: false });
} }
$(document).on('click', '[name="problem-sidebar__rejudge"]', (ev) => {
ev.preventDefault();
new ConfirmDialog({
$body: tpl.typoMsg(i18n('Confirm rejudge this problem?')),
}).open().then((action) => {
if (action !== 'yes') return;
$(ev.currentTarget).closest('form').trigger('submit');
});
});
if ($('[name="upload_testdata"]').length) { if ($('[name="upload_testdata"]').length) {
$(document).on('click', '.problem-files-testdata .col--name', (ev) => handleEdit('testdata', ev)); $(document).on('click', '.problem-files-testdata .col--name', (ev) => handleEdit('testdata', ev));
$(document).on('click', '.problem-files-additional_file .col--name', (ev) => handleEdit('additional_file', ev)); $(document).on('click', '.problem-files-additional_file .col--name', (ev) => handleEdit('additional_file', ev));
@@ -0,0 +1,25 @@
import { NamedPage } from 'vj/misc/Page';
import tpl from 'vj/utils/tpl';
import i18n from 'vj/utils/i18n';
import { ConfirmDialog } from 'vj/components/dialog';
const page = new NamedPage([
'problem_create', 'problem_edit', 'problem_solution', 'problem_submit',
'problem_files', 'discussion_node', 'discussion_detail',
], () => {
$(document).on('click', '[name="problem-sidebar__show-category"]', (ev) => {
$(ev.currentTarget).hide();
$('[name="problem-sidebar__categories"]').show();
});
$(document).on('click', '[name="problem-sidebar__rejudge"]', (ev) => {
ev.preventDefault();
new ConfirmDialog({
$body: tpl.typoMsg(i18n('Confirm rejudge this problem?')),
}).open().then((action) => {
if (action !== 'yes') return;
$(ev.currentTarget).closest('form').trigger('submit');
});
});
});
export default page;
@@ -1,10 +0,0 @@
import { NamedPage } from 'vj/misc/Page';
const page = new NamedPage('problem_solution', async () => {
$(document).on('click', '[name="problem-sidebar__show-category"]', (ev) => {
$(ev.currentTarget).hide();
$('[name="problem-sidebar__categories"]').show();
});
});
export default page;
@@ -8,11 +8,6 @@ function setOptions($el, options) {
} }
const page = new NamedPage(['problem_submit', 'contest_detail_problem_submit', 'homework_detail_problem_submit'], async () => { const page = new NamedPage(['problem_submit', 'contest_detail_problem_submit', 'homework_detail_problem_submit'], async () => {
$(document).on('click', '[name="problem-sidebar__show-category"]', (ev) => {
$(ev.currentTarget).hide();
$('[name="problem-sidebar__categories"]').show();
});
const { domainId } = UiContext.pdocConfig; const { domainId } = UiContext.pdocConfig;
function onChangeMain(update = true) { function onChangeMain(update = true) {
@@ -1,8 +1,10 @@
import { NamedPage } from 'vj/misc/Page'; import { NamedPage } from 'vj/misc/Page';
const page = new NamedPage('record_detail', async () => { const page = new NamedPage('record_detail', async () => {
const { default: SockJs } = await import('../components/socket'); const [{ default: SockJs }, { DiffDOM }] = await Promise.all([
const { DiffDOM } = await import('diff-dom'); import('../components/socket'),
import('diff-dom'),
]);
const sock = new SockJs(UiContext.socketUrl); const sock = new SockJs(UiContext.socketUrl);
const dd = new DiffDOM(); const dd = new DiffDOM();
@@ -3,14 +3,18 @@ import UserSelectAutoComplete from 'vj/components/autocomplete/UserSelectAutoCom
import ProblemSelectAutoComplete from 'vj/components/autocomplete/ProblemSelectAutoComplete'; import ProblemSelectAutoComplete from 'vj/components/autocomplete/ProblemSelectAutoComplete';
const page = new NamedPage('record_main', async () => { const page = new NamedPage('record_main', async () => {
const { default: SockJs } = await import('../components/socket'); const [{ default: SockJs }, { DiffDOM }] = await Promise.all([
const { DiffDOM } = await import('diff-dom'); import('../components/socket'),
import('diff-dom'),
]);
const sock = new SockJs(UiContext.socketUrl); const sock = new SockJs(UiContext.socketUrl);
const dd = new DiffDOM(); const dd = new DiffDOM();
let firstLoad = true;
sock.onopen = () => { sock.onopen = () => {
sock.send(JSON.stringify({ rids: UiContext.rids })); if (firstLoad) sock.send(JSON.stringify({ rids: UiContext.rids }));
firstLoad = false;
}; };
sock.onmessage = (message) => { sock.onmessage = (message) => {
const msg = JSON.parse(message.data); const msg = JSON.parse(message.data);
@@ -1,35 +0,0 @@
import { NamedPage } from 'vj/misc/Page';
import { slideDown, slideUp } from 'vj/utils/slide';
async function handleSectionExpand(ev) {
const $section = $(ev.currentTarget).closest('.training__section');
if ($section.is('.expanded, .animating')) {
return;
}
$section.addClass('animating');
const $detail = $section.find('.training__section__detail');
await slideDown($detail, 300, { opacity: 0 }, { opacity: 1 });
$section.addClass('expanded');
$section.removeClass('collapsed');
$section.removeClass('animating');
}
async function handleSectionCollapse(ev) {
const $section = $(ev.currentTarget).closest('.training__section');
if ($section.is('.collapsed, .animating')) {
return;
}
$section.addClass('animating');
const $detail = $section.find('.training__section__detail');
await slideUp($detail, 300, { opacity: 1 }, { opacity: 0 });
$section.addClass('collapsed');
$section.removeClass('expanded');
$section.removeClass('animating');
}
const page = new NamedPage('training_detail', async () => {
$(document).on('click', '[name="training__section__expand"]', handleSectionExpand);
$(document).on('click', '[name="training__section__collapse"]', handleSectionCollapse);
});
export default page;
@@ -0,0 +1,24 @@
import { NamedPage } from 'vj/misc/Page';
import { slideDown, slideUp } from 'vj/utils/slide';
async function handleSection(ev, type: string) {
const $section = $(ev.currentTarget).closest('.training__section');
if ($section.is(`.${type}d, .animating`)) return;
$section.addClass('animating');
const $detail = $section.find('.training__section__detail');
if (type === 'expand') {
await slideDown($detail, 300, { opacity: 0 }, { opacity: 1 });
} else {
await slideUp($detail, 300, { opacity: 1 }, { opacity: 0 });
}
$section.addClass(`${type}d`);
$section.removeClass(type === 'expand' ? 'collapsed' : 'expanded');
$section.removeClass('animating');
}
const page = new NamedPage('training_detail', () => {
$(document).on('click', '[name="training__section__expand"]', (ev) => handleSection(ev, 'expand'));
$(document).on('click', '[name="training__section__collapse"]', (ev) => handleSection(ev, 'collapse'));
});
export default page;
@@ -15,10 +15,7 @@ export default new NamedPage('training_edit', () => {
} }
const message = 'Confirm deleting this training? Its status will be deleted as well.'; const message = 'Confirm deleting this training? Its status will be deleted as well.';
return new ConfirmDialog({ return new ConfirmDialog({
$body: tpl` $body: tpl.typoMsg(i18n(message)),
<div class="typo">
<p>${i18n(message)}</p>
</div>`,
}).open().then((action) => { }).open().then((action) => {
if (action !== 'yes') return; if (action !== 'yes') return;
confirmed = true; confirmed = true;
+16 -14
View File
@@ -33,11 +33,11 @@
<div class="section__body"> <div class="section__body">
<a href="{{ url('contest_detail', tid=tdoc.docId) }}" class="detail-button">{{ _('View Details') }}</a> <a href="{{ url('contest_detail', tid=tdoc.docId) }}" class="detail-button">{{ _('View Details') }}</a>
<div class="typo text-center"> <div class="typo text-center">
{% if tsdict[tdoc.docId].attend %} {% if tsdict[tdoc.docId].attend %}
<p><span class="icon icon-check"></span> {{ _('Attended') }}</p> <p><span class="icon icon-check"></span> {{ _('Attended') }}</p>
{% else %} {% else %}
<p><span class="icon icon-schedule"></span> {{ _('Not Attended') }}</p> <p><span class="icon icon-schedule"></span> {{ _('Not Attended') }}</p>
{% endif %} {% endif %}
</div> </div>
</div> </div>
</div> </div>
@@ -48,23 +48,25 @@
<div class="row"> <div class="row">
<div class="medium-9 columns"> <div class="medium-9 columns">
{%- for tdoc in tdocs -%} {%- for tdoc in tdocs -%}
{% if model.contest.isOngoing(tdoc) %} {% if model.contest.isOngoing(tdoc) %}
{{ render_important_contest('live', 'Live...', tdoc) }} {{ render_important_contest('live', 'Live...', tdoc) }}
{% elif model.contest.isUpcoming(tdoc) %} {% elif model.contest.isUpcoming(tdoc) %}
{{ render_important_contest('ready', 'Ready (☆▽☆)', tdoc) }} {{ render_important_contest('ready', 'Ready (☆▽☆)', tdoc) }}
{% endif %} {% endif %}
{%- endfor -%} {%- endfor -%}
<div class="section"> <div class="section">
<div class="section__header"> <div class="section__header">
<h1 class="section__title">{% if not rule %}{{ _('All Contests') }}{% else %}{{ _('All {0} Contests').format(_(model.contest.RULES[rule].TEXT)) }}{% endif %}</h1> <h1 class="section__title">{{ _('All Contests') if not rule else _('All {0} Contests').format(model.contest.RULES[rule].TEXT) }}</h1>
<div class="section__tools"> <div class="section__tools">
<form method="get" name="filter-form"> <form method="get" name="filter-form">
{{ _('Show') }}: {{ _('Show') }}:
<select class="inline compact select" name="rule"> <select class="inline compact select" name="rule">
<option value="0"{% if not rule %} selected{% endif %}>{{ _('All') }}</option> <option value="0"{% if not rule %} selected{% endif %}>{{ _('All') }}</option>
{%- for k, v in model.contest.RULES -%} {%- for k, v in model.contest.RULES -%}
<option value="{{ k }}"{% if rule == k %} selected{% endif %}>{{ _('All {0} Contests').format(_(v.TEXT)) }}</option> {% if k != 'homework' %}
{%- endfor -%} <option value="{{ k }}"{% if rule == k %} selected{% endif %}>{{ _('All {0} Contests').format(v.TEXT) }}</option>
{% endif %}
{%- endfor -%}
</select> </select>
<button type="submit" class="button hasjs--hide"> <button type="submit" class="button hasjs--hide">
{{ _('Search') }} {{ _('Search') }}