mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-18 09:36:08 +08:00
ui: change to ts & tsx
This commit is contained in:
1 parent
36cce6d1bd
commit
d10fec361c
29 files changed
+165
-221
No files matched your search
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "hydrooj",
|
||||
"version": "2.39.18",
|
||||
"version": "2.39.19",
|
||||
"bin": "bin/hydrooj.js",
|
||||
"main": "src/loader",
|
||||
"module": "src/loader",
|
||||
|
||||
@@ -47,7 +47,7 @@ export const ForbiddenError = Err('ForbiddenError', UserFacingError, 'ForbiddenE
|
||||
export const NotFoundError = Err('NotFoundError', UserFacingError, 'NotFoundError', 404);
|
||||
export const MethodNotAllowedError = Err('MethodNotAllowedError', UserFacingError, 'MethodNotAllowedError', 405);
|
||||
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 LoginError = Err('LoginError', ForbiddenError, 'Invalid password for user {0}.');
|
||||
|
||||
@@ -49,11 +49,6 @@ function set(key: string, value: any) {
|
||||
class SystemHandler extends Handler {
|
||||
async prepare() {
|
||||
this.checkPriv(PRIV.PRIV_EDIT_SYSTEM);
|
||||
this.response.body = {
|
||||
path: [
|
||||
['Hydro', 'homepage'],
|
||||
],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,12 +81,6 @@ class SystemCheckConnHandler extends ConnectionHandler {
|
||||
class SystemDashboardHandler extends SystemHandler {
|
||||
async get() {
|
||||
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() {
|
||||
this.response.template = 'manage_script.html';
|
||||
this.response.body.scripts = global.Hydro.script;
|
||||
this.response.body.path.push(['manage_script', null]);
|
||||
}
|
||||
|
||||
@param('id', Types.Name)
|
||||
@@ -147,7 +135,6 @@ class SystemScriptHandler extends SystemHandler {
|
||||
class SystemSettingHandler extends SystemHandler {
|
||||
async get() {
|
||||
this.response.template = 'manage_setting.html';
|
||||
this.response.body.path.push(['manage_setting', null]);
|
||||
this.response.body.current = {};
|
||||
this.response.body.settings = setting.SYSTEM_SETTINGS;
|
||||
for (const s of this.response.body.settings) {
|
||||
@@ -185,7 +172,6 @@ class SystemSettingHandler extends SystemHandler {
|
||||
class SystemUserImportHandler extends SystemHandler {
|
||||
async get() {
|
||||
this.response.body.users = [];
|
||||
this.response.body.path.push(['manage_user_import']);
|
||||
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.messages = messages;
|
||||
}
|
||||
|
||||
@@ -13,35 +13,6 @@ import {
|
||||
} from '../service/server';
|
||||
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 {
|
||||
@param('lang', Types.Name)
|
||||
async get(domainId: string, lang: string) {
|
||||
@@ -132,7 +103,6 @@ export class FSDownloadHandler extends Handler {
|
||||
}
|
||||
|
||||
export async function apply() {
|
||||
Route('check_in', '/checkin', CheckInHandler, PRIV.PRIV_USER_PROFILE);
|
||||
Route('switch_language', '/language/:lang', SwitchLanguageHandler);
|
||||
Route('home_files', '/file', FilesHandler, PRIV.PRIV_CREATE_FILE);
|
||||
Route('fs_download', '/file/:uid/:filename', FSDownloadHandler);
|
||||
|
||||
@@ -677,6 +677,8 @@ declare global {
|
||||
addons: string[],
|
||||
}
|
||||
}
|
||||
var Hydro: HydroGlobal; // eslint-disable-line
|
||||
var addons: string[]; // eslint-disable-line
|
||||
}
|
||||
|
||||
declare module 'koa' {
|
||||
|
||||
@@ -33,7 +33,7 @@ export async function sendMail(to: string, subject: string, text: string, html:
|
||||
});
|
||||
} catch (e) {
|
||||
logger.error(e);
|
||||
throw new SendMailError(to);
|
||||
throw new SendMailError(to, e.message);
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
@@ -131,8 +131,6 @@ AccountSetting(
|
||||
'/components/profile/backgrounds/1.jpg', 'text', 'Profile Background Image',
|
||||
'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', '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),
|
||||
);
|
||||
|
||||
|
||||
@@ -62,6 +62,16 @@ module.exports = {
|
||||
'no-unused-vars': 'off',
|
||||
'@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': [
|
||||
'error',
|
||||
'always-multiline',
|
||||
@@ -69,9 +79,7 @@ module.exports = {
|
||||
indent: [
|
||||
'error',
|
||||
2,
|
||||
{
|
||||
SwitchCase: 0,
|
||||
},
|
||||
{ SwitchCase: 0 },
|
||||
],
|
||||
'max-len': ['error', 150],
|
||||
quotes: 'warn',
|
||||
|
||||
+36
-32
@@ -2,20 +2,33 @@ import _ from 'lodash';
|
||||
import DOMAttachedObject from 'vj/components/DOMAttachedObject';
|
||||
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 {
|
||||
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);
|
||||
this.isShown = false;
|
||||
this.isAnimating = false;
|
||||
this.options = {
|
||||
cancelByClickingBack: false,
|
||||
cancelByEsc: false,
|
||||
onDispatch: () => { },
|
||||
...options,
|
||||
...options as any,
|
||||
};
|
||||
this._defer = null;
|
||||
}
|
||||
|
||||
async _show() {
|
||||
@@ -70,9 +83,7 @@ export default class DomDialog extends DOMAttachedObject {
|
||||
$(document).off(`keyup.${this.eventNS}`);
|
||||
this.$dom.off(`click.${this.eventNS}`);
|
||||
|
||||
this.$dom.css({
|
||||
opacity: 1,
|
||||
});
|
||||
this.$dom.css({ opacity: 1 });
|
||||
this.$dom.transition({
|
||||
opacity: 0,
|
||||
}, {
|
||||
@@ -80,17 +91,15 @@ export default class DomDialog extends DOMAttachedObject {
|
||||
});
|
||||
|
||||
const $dgContent = this.$dom.find('.dialog__content');
|
||||
$dgContent.css({
|
||||
scale: 1,
|
||||
});
|
||||
$dgContent.css({ scale: 1 });
|
||||
await $dgContent
|
||||
.transition({
|
||||
scale: 0.8,
|
||||
}, {
|
||||
duration: 200,
|
||||
easing: 'easeOutCubic',
|
||||
})
|
||||
.promise();
|
||||
.transition(
|
||||
{ scale: 0.8 },
|
||||
{
|
||||
duration: 200,
|
||||
easing: 'easeOutCubic',
|
||||
},
|
||||
).promise();
|
||||
|
||||
this.$dom.css('display', 'none');
|
||||
|
||||
@@ -103,28 +112,23 @@ export default class DomDialog extends DOMAttachedObject {
|
||||
if (this.isShown || this.isAnimating) {
|
||||
return Promise.reject();
|
||||
}
|
||||
this._defer = new $.Deferred();
|
||||
const promise = new Promise<string>((resolve) => {
|
||||
this._resolve = resolve;
|
||||
});
|
||||
this._show();
|
||||
return this._defer.promise();
|
||||
return promise;
|
||||
}
|
||||
|
||||
hide() {
|
||||
if (!this.isShown || this.isAnimating) {
|
||||
return false;
|
||||
}
|
||||
if (this._defer.state() === 'pending') {
|
||||
this._defer.resolve('cancel');
|
||||
}
|
||||
this._defer = null;
|
||||
if (!this.isShown || this.isAnimating) return false;
|
||||
this._resolve('cancel');
|
||||
this._hide();
|
||||
return true;
|
||||
}
|
||||
|
||||
dispatchAction(data) {
|
||||
if (this.options.onDispatch(data) === false) {
|
||||
return;
|
||||
}
|
||||
this._defer.resolve(data);
|
||||
dispatchAction(data: string) {
|
||||
if (this.options.onDispatch(data) === false) return;
|
||||
this._resolve(data);
|
||||
this.hide();
|
||||
}
|
||||
|
||||
+9
-5
@@ -1,9 +1,13 @@
|
||||
import tpl from 'vj/utils/tpl';
|
||||
import i18n from 'vj/utils/i18n';
|
||||
import DomDialog from './DomDialog';
|
||||
import DomDialog, { DialogOptions } from './DomDialog';
|
||||
|
||||
export class Dialog {
|
||||
constructor(options = {}) {
|
||||
options: DialogOptions;
|
||||
$dom: JQuery<any>;
|
||||
domDialogInstance: DomDialog;
|
||||
|
||||
constructor(options: Partial<DialogOptions> = {}) {
|
||||
this.options = {
|
||||
classes: '',
|
||||
$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>`;
|
||||
|
||||
export class InfoDialog extends Dialog {
|
||||
constructor(options = {}) {
|
||||
constructor(options: Partial<DialogOptions> = {}) {
|
||||
super({
|
||||
$action: buttonOk,
|
||||
cancelByClickingBack: true,
|
||||
@@ -71,7 +75,7 @@ export class InfoDialog extends Dialog {
|
||||
}
|
||||
|
||||
export class ActionDialog extends Dialog {
|
||||
constructor(options = {}) {
|
||||
constructor(options: Partial<DialogOptions> = {}) {
|
||||
super({
|
||||
$action: [buttonCancel, buttonOk].join('\n'),
|
||||
cancelByClickingBack: true,
|
||||
@@ -82,7 +86,7 @@ export class ActionDialog extends Dialog {
|
||||
}
|
||||
|
||||
export class ConfirmDialog extends Dialog {
|
||||
constructor(options = {}) {
|
||||
constructor(options: Partial<DialogOptions> = {}) {
|
||||
let buttons = [];
|
||||
if (options.canCancel) {
|
||||
buttons = [buttonCancel, buttonNo, buttonYes];
|
||||
@@ -5,6 +5,7 @@ import pipeStream from 'vj/utils/pipeStream';
|
||||
import i18n from 'vj/utils/i18n';
|
||||
import request from 'vj/utils/request';
|
||||
import Notification from 'vj/components/notification';
|
||||
import api from 'vj/utils/api';
|
||||
|
||||
let isBeforeUnloadTriggeredByLibrary = !window.isSecureContext;
|
||||
function onBeforeUnload(e) {
|
||||
@@ -67,7 +68,24 @@ export async function downloadProblemSet(pids, name = 'Export') {
|
||||
const targets = [];
|
||||
try {
|
||||
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({
|
||||
filename: `${pid}/problem.yaml`,
|
||||
content: dump({
|
||||
@@ -77,7 +95,6 @@ export async function downloadProblemSet(pids, name = 'Export') {
|
||||
tag: pdoc.tag,
|
||||
nSubmit: pdoc.nSubmit,
|
||||
nAccept: pdoc.nAccept,
|
||||
difficulty: pdoc.difficulty,
|
||||
}),
|
||||
});
|
||||
targets.push({
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@hydrooj/ui-default",
|
||||
"version": "4.24.5",
|
||||
"version": "4.25.0",
|
||||
"author": "undefined <i@undefined.moe>",
|
||||
"license": "AGPL-3.0",
|
||||
"main": "hydro.js",
|
||||
|
||||
+2
-3
@@ -1,9 +1,8 @@
|
||||
import { NamedPage } from 'vj/misc/Page';
|
||||
|
||||
const page = new NamedPage('contest_main', () => {
|
||||
// Contest Filter
|
||||
$('[name="filter-form"] [name="rule"]').change(() => {
|
||||
$('[name="filter-form"]').submit();
|
||||
$('[name="filter-form"] [name="rule"]').on('change', () => {
|
||||
$('[name="filter-form"]').trigger('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;
|
||||
+2
-2
@@ -6,13 +6,13 @@ const page = new NamedPage('domain_join_applications', () => {
|
||||
const $expire = $('[name="expire"]');
|
||||
const $code = $('[name="invitationCode"]');
|
||||
function updateFormState() {
|
||||
const method = parseInt($('[name="method"]').val(), 10);
|
||||
const method = +$('[name="method"]').val();
|
||||
$role.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');
|
||||
}
|
||||
updateFormState();
|
||||
$('[name="method"]').change(() => updateFormState());
|
||||
$('[name="method"]').on('change', updateFormState);
|
||||
});
|
||||
|
||||
export default page;
|
||||
@@ -40,9 +40,7 @@ const page = new NamedPage('domain_role', () => {
|
||||
|
||||
async function handleClickCreateRole() {
|
||||
const action = await createRoleDialog.clear().open();
|
||||
if (action !== 'ok') {
|
||||
return;
|
||||
}
|
||||
if (action !== 'ok') return;
|
||||
const role = createRoleDialog.$dom.find('[name="role"]').val();
|
||||
try {
|
||||
await request.post('', {
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
import yaml from 'js-yaml';
|
||||
import { getScoreColor } from '@hydrooj/utils/lib/status';
|
||||
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 loadReactRedux from 'vj/utils/loadReactRedux';
|
||||
import delay from 'vj/utils/delay';
|
||||
@@ -263,15 +260,7 @@ const page = new NamedPage(['problem_detail', 'contest_detail_problem', 'homewor
|
||||
leaveScratchpadMode();
|
||||
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) => {
|
||||
$(ev.currentTarget).hide();
|
||||
$('span.tags').css('display', 'inline-block');
|
||||
|
||||
@@ -144,10 +144,6 @@ function buildCategoryFilter() {
|
||||
}
|
||||
|
||||
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;
|
||||
$(document).on('click', '[name="operation"]', (ev) => {
|
||||
ev.preventDefault();
|
||||
@@ -178,7 +174,7 @@ export default new NamedPage(['problem_create', 'problem_edit'], (pagename) => {
|
||||
$('img').each(function () {
|
||||
if (this.src.startsWith('file://')) this.setAttribute('src', this.src.replace('file://', './file/'));
|
||||
});
|
||||
}, 50);
|
||||
}, 200);
|
||||
|
||||
const $main = $('textarea[data-editor]');
|
||||
const $field = $('textarea[data-markdown-upload]');
|
||||
|
||||
@@ -235,16 +235,6 @@ const page = new NamedPage('problem_files', () => {
|
||||
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) {
|
||||
$(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));
|
||||
|
||||
@@ -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 () => {
|
||||
$(document).on('click', '[name="problem-sidebar__show-category"]', (ev) => {
|
||||
$(ev.currentTarget).hide();
|
||||
$('[name="problem-sidebar__categories"]').show();
|
||||
});
|
||||
|
||||
const { domainId } = UiContext.pdocConfig;
|
||||
|
||||
function onChangeMain(update = true) {
|
||||
|
||||
+4
-2
@@ -1,8 +1,10 @@
|
||||
import { NamedPage } from 'vj/misc/Page';
|
||||
|
||||
const page = new NamedPage('record_detail', async () => {
|
||||
const { default: SockJs } = await import('../components/socket');
|
||||
const { DiffDOM } = await import('diff-dom');
|
||||
const [{ default: SockJs }, { DiffDOM }] = await Promise.all([
|
||||
import('../components/socket'),
|
||||
import('diff-dom'),
|
||||
]);
|
||||
|
||||
const sock = new SockJs(UiContext.socketUrl);
|
||||
const dd = new DiffDOM();
|
||||
+7
-3
@@ -3,14 +3,18 @@ import UserSelectAutoComplete from 'vj/components/autocomplete/UserSelectAutoCom
|
||||
import ProblemSelectAutoComplete from 'vj/components/autocomplete/ProblemSelectAutoComplete';
|
||||
|
||||
const page = new NamedPage('record_main', async () => {
|
||||
const { default: SockJs } = await import('../components/socket');
|
||||
const { DiffDOM } = await import('diff-dom');
|
||||
const [{ default: SockJs }, { DiffDOM }] = await Promise.all([
|
||||
import('../components/socket'),
|
||||
import('diff-dom'),
|
||||
]);
|
||||
|
||||
const sock = new SockJs(UiContext.socketUrl);
|
||||
const dd = new DiffDOM();
|
||||
|
||||
let firstLoad = true;
|
||||
sock.onopen = () => {
|
||||
sock.send(JSON.stringify({ rids: UiContext.rids }));
|
||||
if (firstLoad) sock.send(JSON.stringify({ rids: UiContext.rids }));
|
||||
firstLoad = false;
|
||||
};
|
||||
sock.onmessage = (message) => {
|
||||
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;
|
||||
+1
-4
@@ -15,10 +15,7 @@ export default new NamedPage('training_edit', () => {
|
||||
}
|
||||
const message = 'Confirm deleting this training? Its status will be deleted as well.';
|
||||
return new ConfirmDialog({
|
||||
$body: tpl`
|
||||
<div class="typo">
|
||||
<p>${i18n(message)}</p>
|
||||
</div>`,
|
||||
$body: tpl.typoMsg(i18n(message)),
|
||||
}).open().then((action) => {
|
||||
if (action !== 'yes') return;
|
||||
confirmed = true;
|
||||
@@ -33,11 +33,11 @@
|
||||
<div class="section__body">
|
||||
<a href="{{ url('contest_detail', tid=tdoc.docId) }}" class="detail-button">{{ _('View Details') }}</a>
|
||||
<div class="typo text-center">
|
||||
{% if tsdict[tdoc.docId].attend %}
|
||||
<p><span class="icon icon-check"></span> {{ _('Attended') }}</p>
|
||||
{% else %}
|
||||
<p><span class="icon icon-schedule"></span> {{ _('Not Attended') }}</p>
|
||||
{% endif %}
|
||||
{% if tsdict[tdoc.docId].attend %}
|
||||
<p><span class="icon icon-check"></span> {{ _('Attended') }}</p>
|
||||
{% else %}
|
||||
<p><span class="icon icon-schedule"></span> {{ _('Not Attended') }}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -48,23 +48,25 @@
|
||||
<div class="row">
|
||||
<div class="medium-9 columns">
|
||||
{%- for tdoc in tdocs -%}
|
||||
{% if model.contest.isOngoing(tdoc) %}
|
||||
{{ render_important_contest('live', 'Live...', tdoc) }}
|
||||
{% elif model.contest.isUpcoming(tdoc) %}
|
||||
{{ render_important_contest('ready', 'Ready (☆▽☆)', tdoc) }}
|
||||
{% endif %}
|
||||
{% if model.contest.isOngoing(tdoc) %}
|
||||
{{ render_important_contest('live', 'Live...', tdoc) }}
|
||||
{% elif model.contest.isUpcoming(tdoc) %}
|
||||
{{ render_important_contest('ready', 'Ready (☆▽☆)', tdoc) }}
|
||||
{% endif %}
|
||||
{%- endfor -%}
|
||||
<div class="section">
|
||||
<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">
|
||||
<form method="get" name="filter-form">
|
||||
{{ _('Show') }}:
|
||||
<select class="inline compact select" name="rule">
|
||||
<option value="0"{% if not rule %} selected{% endif %}>{{ _('All') }}</option>
|
||||
{%- for k, v in model.contest.RULES -%}
|
||||
<option value="{{ k }}"{% if rule == k %} selected{% endif %}>{{ _('All {0} Contests').format(_(v.TEXT)) }}</option>
|
||||
{%- endfor -%}
|
||||
{%- for k, v in model.contest.RULES -%}
|
||||
{% if k != 'homework' %}
|
||||
<option value="{{ k }}"{% if rule == k %} selected{% endif %}>{{ _('All {0} Contests').format(v.TEXT) }}</option>
|
||||
{% endif %}
|
||||
{%- endfor -%}
|
||||
</select>
|
||||
<button type="submit" class="button hasjs--hide">
|
||||
{{ _('Search') }}
|
||||
|
||||
Reference in new issue
Block a user