From 900e19382bb38b8d23b9f0037ef757bb546f9b5c Mon Sep 17 00:00:00 2001 From: undefined Date: Tue, 14 Jul 2020 11:11:20 +0800 Subject: [PATCH] =?UTF-8?q?=E7=AB=99=E5=86=85=E6=B6=88=E6=81=AF=E5=8D=B3?= =?UTF-8?q?=E6=97=B6=E9=80=9A=E7=9F=A5=EF=BC=8C=E6=B5=8B=E8=AF=95=E6=96=AD?= =?UTF-8?q?=E7=BA=BF=E9=87=8D=E8=BF=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TODO | 3 +- hydro/handler/home.ts | 14 ++--- hydro/handler/import.ts | 6 +- hydro/handler/problem.ts | 6 +- hydro/model/problem.ts | 11 ++-- hydro/model/setting.ts | 6 +- hydro/model/user.ts | 2 +- hydro/script/install.ts | 3 +- locales/zh_CN.yaml | 1 + ui/components/header/header.page.styl | 13 +++- ui/components/message/index.page.js | 29 +++++++++ ui/components/navigation/navigation.page.styl | 6 ++ ui/components/notification/index.js | 59 ++++++++++++------- ui/components/profile/profile.page.styl | 7 +++ 14 files changed, 120 insertions(+), 46 deletions(-) create mode 100644 ui/components/message/index.page.js diff --git a/TODO b/TODO index 1e45ee9bd..a46d2ed72 100644 --- a/TODO +++ b/TODO @@ -1,4 +1,5 @@ @requireCsrfToken 消息提醒 bug修复 -使用BigInt管理权限 \ No newline at end of file +使用BigInt管理权限 +修复discussion前端权限节点显示异常 diff --git a/hydro/handler/home.ts b/hydro/handler/home.ts index 160eba057..7787875a0 100644 --- a/hydro/handler/home.ts +++ b/hydro/handler/home.ts @@ -31,7 +31,7 @@ import { const { geoip, useragent } = global.Hydro.lib; class HomeHandler extends Handler { - async homework(domainId) { + async homework(domainId: string) { if (this.user.hasPerm(PERM.PERM_VIEW_HOMEWORK)) { const tdocs = await contest.getMulti(domainId, {}, document.TYPE_HOMEWORK) .sort('beginAt', -1) @@ -46,11 +46,11 @@ class HomeHandler extends Handler { return [[], {}]; } - async contest(domainId) { + async contest(domainId: string) { if (this.user.hasPerm(PERM.PERM_VIEW_CONTEST)) { const tdocs = await contest.getMulti(domainId) .sort('beginAt', -1) - .limit(await system.get('CONTESTS_ON_MAIN')) + .limit(await system.get('CONTEST_ON_MAIN')) .toArray(); const tsdict = await contest.getListStatus( domainId, this.user._id, tdocs.map((tdoc) => tdoc.docId), @@ -60,11 +60,11 @@ class HomeHandler extends Handler { return [[], {}]; } - async training(domainId) { + async training(domainId: string) { if (this.user.hasPerm(PERM.PERM_VIEW_TRAINING)) { const tdocs = await training.getMulti(domainId) .sort('_id', 1) - .limit(await system.get('TRAININGS_ON_MAIN')) + .limit(await system.get('TRAINING_ON_MAIN')) .toArray(); const tsdict = await training.getListStatus( domainId, this.user._id, tdocs.map((tdoc) => tdoc.docId), @@ -74,10 +74,10 @@ class HomeHandler extends Handler { return [[], {}]; } - async discussion(domainId): Promise<[any[], any]> { + async discussion(domainId: string): Promise<[any[], any]> { if (this.user.hasPerm(PERM.PERM_VIEW_DISCUSSION)) { const ddocs = await discussion.getMulti(domainId) - .limit(await system.get('DISCUSSIONS_ON_MAIN')) + .limit(await system.get('DISCUSSION_ON_MAIN')) .toArray(); const vndict = await discussion.getListVnodes(domainId, ddocs, this); return [ddocs, vndict]; diff --git a/hydro/handler/import.ts b/hydro/handler/import.ts index 573eed448..0d73a45b5 100644 --- a/hydro/handler/import.ts +++ b/hydro/handler/import.ts @@ -34,9 +34,11 @@ class ProblemImportHandler extends Handler { throw new ValidationError('remoteType'); } const [pdoc, testdata] = await global.Hydro.lib[`import.${remoteType}`](url, this); - if (pid) pdoc.pid = pid; if (hidden) pdoc.hidden = true; - const docId = await problem.add(domainId, pdoc.title, pdoc.content, this.user._id, pdoc); + const docId = await problem.add( + domainId, pid || pdoc.pid, pdoc.title, pdoc.content, this.user._id, + pdoc.tag || [], pdoc.category || [], null, pdoc.hidden, + ); if (testdata) { const file = path.resolve(os.tmpdir(), 'hydro', `import_${domainId}_${pid || pdoc.pid || docId}.zip`); const w = fs.createWriteStream(file); diff --git a/hydro/handler/problem.ts b/hydro/handler/problem.ts index cc3104ecd..b964c4f31 100644 --- a/hydro/handler/problem.ts +++ b/hydro/handler/problem.ts @@ -534,13 +534,11 @@ class ProblemCreateHandler extends Handler { } @param('title', Types.String, isTitle) - @param('pid', Types.String, isPid) + @param('pid', Types.String, isPid, true) @param('content', Types.String, isContent) @param('hidden', Types.Boolean) async post(domainId: string, title: string, pid: string, content: string, hidden = false) { - pid = await problem.add(domainId, title, content, this.user._id, { - pid, hidden, - }); + pid = await problem.add(domainId, pid, title, content, this.user._id, [], [], null, hidden); this.response.body = { pid }; this.response.redirect = this.url('problem_settings', { pid }); } diff --git a/hydro/model/problem.ts b/hydro/model/problem.ts index cfe1295ca..d7c3c2206 100644 --- a/hydro/model/problem.ts +++ b/hydro/model/problem.ts @@ -24,13 +24,10 @@ export const pdocHidden: Pdoc = { config: '', }; -export async function add(domainId: string, title: string, content: string, owner: number, { - pid = null, - data = null, - category = [], - tag = [], - hidden = false, -}) { +export async function add( + domainId: string, pid: string = null, title: string, content: string, owner: number, + tag: string[] = [], category: string[] = [], data: ObjectID = null, hidden = false, +) { const d = await domain.inc(domainId, 'pidCounter', 1); if (!pid) pid = d.pidCounter.toString(); return await document.add( diff --git a/hydro/model/setting.ts b/hydro/model/setting.ts index 1658bea2a..9278dab98 100644 --- a/hydro/model/setting.ts +++ b/hydro/model/setting.ts @@ -133,7 +133,7 @@ SystemSetting( Setting('setting_ui', 'ui.header_logo', null, '/components/header/header-logo.png', 'text', 'Header Logo'), Setting('setting_ui', 'ui.header_logo_2x', null, '/components/header/header-logo@2x.png', 'text', 'Header Logo@2x'), Setting('setting_ui', 'ui.header_background', null, '/components/header/header-background.png', 'text', 'Header Background'), - Setting('setting_ui', 'ui.header_background_2x', null, '/components/header/header-background.png', 'text', 'Header Logo@2x'), + Setting('setting_ui', 'ui.header_background_2x', null, '/components/header/header-background@2x.png', 'text', 'Header Logo@2x'), Setting('setting_ui', 'ui.nav_logo_light', null, '/components/navigation/nav-logo-small_light.png', 'text', 'Nav Logo(Light)'), Setting('setting_ui', 'ui.nav_logo_dark', null, '/components/navigation/nav-logo-small_dark.png', 'text', 'Nav Logo(Dark)'), Setting('setting_ui', 'ui.nav_logo_light_2x', null, '/components/navigation/nav-logo-small@2x_light.png', 'text', 'Nav Logo@2x(Light)'), @@ -150,6 +150,10 @@ SystemSetting( Setting('setting_constant', 'SOLUTION_PER_PAGE', null, 20, 'number', 'Solutions per Page'), Setting('setting_constant', 'TRAINING_PER_PAGE', null, 10, 'number', 'Training per Page'), Setting('setting_constant', 'REPLY_PER_PAGE', null, 50, 'number', 'Reply per Page'), + Setting('setting_constant', 'HOMEWORK_ON_MAIN', null, 5, 'number', 'HOMEWORK_ON_MAIN'), + Setting('setting_constant', 'DISCUSSION_ON_MAIN', null, 20, 'number', 'DISCUSSION_ON_MAIN'), + Setting('setting_constant', 'CONTEST_ON_MAIN', null, 10, 'number', 'CONTEST_ON_MAIN'), + Setting('setting_constant', 'TRAINING_ON_MAIN', null, 10, 'number', 'TRAINING_ON_MAIN'), ); global.Hydro.postInit.push( diff --git a/hydro/model/user.ts b/hydro/model/user.ts index 64a2feaab..83978706a 100644 --- a/hydro/model/user.ts +++ b/hydro/model/user.ts @@ -188,7 +188,7 @@ export async function create({ mailLower: mail.trim().toLowerCase(), uname, unameLower: uname.trim().toLowerCase(), - password: pwhash(password, salt), + hash: pwhash(password, salt), salt, hashType: 'hydro', regat: new Date(), diff --git a/hydro/script/install.ts b/hydro/script/install.ts index 5b5efefe1..c4c3b43a9 100644 --- a/hydro/script/install.ts +++ b/hydro/script/install.ts @@ -6,7 +6,8 @@ import pwhash from '../lib/hash.hydro'; export const description = 'Install'; export async function run({ username = '', password = '' } = {}) { - await domain.add('system', 0, 'Hydro', 'Hydro System'); + const ddoc = await domain.get('system'); + if (!ddoc) await domain.add('system', 0, 'Hydro', 'Hydro System'); if (username && password) { const udoc = await user.getById('system', -1); if (!udoc) { diff --git a/locales/zh_CN.yaml b/locales/zh_CN.yaml index 6b4be313e..3b4b7bb71 100644 --- a/locales/zh_CN.yaml +++ b/locales/zh_CN.yaml @@ -208,6 +208,7 @@ Difficulty by algorithm: 算法计算的难度 Difficulty displayed: 显示的难度 Difficulty: 难度 Disabled: 关闭 +Disconnected from the server. Reconnecting...: 到服务器的连接断开。正在重新连接... Discussion {1} not found.: 讨论 {1} 不存在。 Discussion category {1} already exists.: 讨论分类 {1} 已经存在。 Discussion category {1} not found.: 讨论分类 {1} 不存在。 diff --git a/ui/components/header/header.page.styl b/ui/components/header/header.page.styl index 7188d212d..c28d262f6 100644 --- a/ui/components/header/header.page.styl +++ b/ui/components/header/header.page.styl @@ -8,16 +8,19 @@ $header-gap-mini-domain = ($header-bg-height-mini - $nav-item-height - $header-l &.warning background: #ffe1a6 +.header__options + background-image: url(./header-background.png) + background-image: url(./header-background@2x.png) + .header position: relative height: rem($header-bg-height) - background: #5eafca url(./header-background.png) no-repeat center top + background: #5eafca no-repeat center top background-size: cover transition: height .5s transition-timing-function: ease-out-cubic overflow: hidden +retina() - background-image: url(./header-background.png) background-size: rem($header-bg-width $header-bg-height) &.mini @@ -29,6 +32,12 @@ $header-gap-mini-domain = ($header-bg-height-mini - $nav-item-height - $header-l .header__content display: none +.header__logo__options + background-image: url(./header-logo.png) + background-image: url(./header-logo@2x.png) + background-image: url(./header-logo-summer.png) + background-image: url(./header-logo-summer@2x.png) + .header__logo display: block width: rem($header-logo-width-real) diff --git a/ui/components/message/index.page.js b/ui/components/message/index.page.js new file mode 100644 index 000000000..4f36727cc --- /dev/null +++ b/ui/components/message/index.page.js @@ -0,0 +1,29 @@ +import { AutoloadPage } from 'vj/misc/PageLoader'; +import Notification from 'vj/components/notification'; +import i18n from 'vj/utils/i18n'; + +const messagePage = new AutoloadPage('messagePage', () => { + async function init(isReconnect = false) { + const { default: SockJs } = await import('sockjs-client'); + + const sock = new SockJs(`/home/messages-conn?token=${UiContext.token}`); + sock.onopen = () => { + if (isReconnect) Notification.info('Reconnected'); + }; + sock.onmessage = (message) => { + const msg = JSON.parse(message.data); + console.log(msg); + Notification.show({ title: msg.udoc.uname, message: msg.mdoc.content, duration: 0 }); + }; + sock.onclose = () => { + Notification.warn(i18n('Disconnected from the server. Reconnecting...')); + setTimeout(() => { + init(true); + }, 3000); + }; + } + + if (UserContext._id !== 0) init(); +}); + +export default messagePage; diff --git a/ui/components/navigation/navigation.page.styl b/ui/components/navigation/navigation.page.styl index 77d44838b..c979b1dfe 100644 --- a/ui/components/navigation/navigation.page.styl +++ b/ui/components/navigation/navigation.page.styl @@ -107,6 +107,12 @@ .nav__list--secondary .nav__item--round margin-left: rem(5px) +.nav__logo__options + background-image: url(./nav-logo-small_light.png) + background-image: url(./nav-logo-small_dark.png) + background-image: url(./nav-logo-small@2x_light.png) + background-image: url(./nav-logo-small@2x_dark.png) + .nav__logo display: block width: 0 diff --git a/ui/components/notification/index.js b/ui/components/notification/index.js index c0789f89f..18b402d72 100644 --- a/ui/components/notification/index.js +++ b/ui/components/notification/index.js @@ -6,45 +6,64 @@ let autoHideTimer = null; export default class Notification { static success(message, duration) { - Notification.show({ type: 'success', message, duration }); + return Notification.show({ type: 'success', message, duration }); } static info(message, duration) { - Notification.show({ type: 'info', message, duration }); + return Notification.show({ type: 'info', message, duration }); } static warn(message, duration) { - Notification.show({ type: 'warn', message, duration }); + return Notification.show({ type: 'warn', message, duration }); } static error(message, duration) { - Notification.show({ type: 'error', message, duration }); + return Notification.show({ type: 'error', message, duration }); } - static show({ message, type = 'info', duration = 3000 }) { - if (lastNotification) { + static show({ + avatar, title, message, type = 'info', duration = 3000, + actionButtons = [], + }) { + if (duration && lastNotification) { Notification.hide(); } - const $n = $(tpl`
${message}
`) + const $dom = $(tpl`
`); + if (avatar) $(tpl``).appendTo($dom); + if (title) $(tpl`${title} `).appendTo($dom); + $(tpl`${message}`).appendTo($dom); + actionButtons.forEach((button) => { + $(tpl``).appendTo($dom); + }); + console.log($dom); + const $n = $dom .css('z-index', zIndexManager.getNext()) .appendTo('body'); $n.width(); // force reflow $n.removeClass('hide'); - lastNotification = $n; - autoHideTimer = setTimeout(Notification.hide, duration); + if (duration) { + lastNotification = $n; + autoHideTimer = setTimeout(Notification.hide, duration); + } + return $n; } - static hide() { - if (!lastNotification) { - return; + static hide($node) { + if (!$node) { + if (!lastNotification) { + return; + } + if (autoHideTimer) { + clearTimeout(autoHideTimer); + autoHideTimer = null; + } + const $n = lastNotification; + $n.addClass('hide'); + setTimeout(() => $n.remove(), 200); + lastNotification = null; + } else { + $node.addClass('hide'); + setTimeout(() => $node.remove(), 200); } - if (autoHideTimer) { - clearTimeout(autoHideTimer); - autoHideTimer = null; - } - const $n = lastNotification; - $n.addClass('hide'); - setTimeout(() => $n.remove(), 200); - lastNotification = null; } } diff --git a/ui/components/profile/profile.page.styl b/ui/components/profile/profile.page.styl index 435f010f8..fb3f85de2 100644 --- a/ui/components/profile/profile.page.styl +++ b/ui/components/profile/profile.page.styl @@ -10,6 +10,13 @@ &:hover text-decoration: none +for n in (1..21) + .user-profile-bg--{n} + background-image: url('backgrounds/' + n + '.jpg') + + .user-profile-bg--thumbnail-{n} + background-image: url('backgrounds/thumbnail/' + n + '.jpg') + for key, _ in $badge-bg-color .badge--{key} background-color: $badge-bg-color[key] !important