From c8a0d48a4139576d24c8e4e4d8dc41b503853171 Mon Sep 17 00:00:00 2001 From: undefined Date: Wed, 1 Sep 2021 20:27:30 +0800 Subject: [PATCH] =?UTF-8?q?ui:=20=E4=BC=98=E5=8C=96=E8=B5=84=E6=BA=90?= =?UTF-8?q?=E5=8A=A0=E8=BD=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/ui-default/entry.js | 28 ++- .../ui-default/{handler.js => handler.ts} | 184 ++++++++---------- packages/ui-default/hydro.js | 20 +- packages/ui-default/package.json | 2 +- .../ui-default/templates/components/user.html | 2 +- packages/ui-default/templates/extra.css | 13 -- .../ui-default/templates/layout/html5.html | 5 +- 7 files changed, 111 insertions(+), 143 deletions(-) rename packages/ui-default/{handler.js => handler.ts} (54%) delete mode 100644 packages/ui-default/templates/extra.css diff --git a/packages/ui-default/entry.js b/packages/ui-default/entry.js index 70cef2d7..59f9018f 100644 --- a/packages/ui-default/entry.js +++ b/packages/ui-default/entry.js @@ -1,5 +1,6 @@ window.Hydro = { extraPages: [], + preload: [], components: {}, utils: {}, node_modules: {}, @@ -19,9 +20,28 @@ console.log( `, ); -window.UiContext = JSON.parse(window.UiContext); +document.addEventListener('DOMContentLoaded', () => { + window.UiContext = JSON.parse(window.UiContext); -// eslint-disable-next-line -try { __webpack_public_path__ = UiContext.cdn_prefix } catch (e) { } + // eslint-disable-next-line + try { __webpack_public_path__ = UiContext.cdn_prefix } catch (e) { } -import('./hydro'); + // Locale & langs + const { version, payload } = JSON.parse(localStorage.getItem('hydro-constant') || '{}'); + if (version === UiContext.constantVersion) { + eval(payload[0]); // eslint-disable-line no-eval + payload.shift(); + window.Hydro.preload = payload; + import('./hydro'); + } else { + fetch(`/constant?version=${UiContext.constantVersion}`) + .then((res) => res.json()) + .then((data) => { + eval(data.payload[0]); // eslint-disable-line no-eval + localStorage.setItem('hydro-constant', JSON.stringify(data)); + data.payload.shift(); + window.Hydro.preload = data.payload; + import('./hydro'); + }); + } +}, false); diff --git a/packages/ui-default/handler.js b/packages/ui-default/handler.ts similarity index 54% rename from packages/ui-default/handler.js rename to packages/ui-default/handler.ts index 0fef05ed..9f5c93f7 100644 --- a/packages/ui-default/handler.js +++ b/packages/ui-default/handler.ts @@ -1,24 +1,82 @@ /* eslint-disable no-return-await */ /* eslint-disable camelcase */ -const { readdirSync, readFileSync } = require('fs'); -const { join } = require('path'); -const crypto = require('crypto'); -const { tmpdir } = require('os'); -const { ObjectID } = require('mongodb'); -const bus = require('hydrooj/src/service/bus'); -const { PERM } = require('hydrooj/src/model/builtin'); -const markdown = require('./backendlib/markdown'); +import crypto from 'crypto'; +import { readdir, readFile } from 'fs-extra'; +import { join } from 'path'; +import { tmpdir } from 'os'; +import { ObjectID } from 'mongodb'; +import * as bus from 'hydrooj/src/service/bus'; +import { Route, Handler } from 'hydrooj/src/service/server'; +import { PERM } from 'hydrooj/src/model/builtin'; +import markdown from './backendlib/markdown'; const { system, domain, user, setting, problem, contest, } = global.Hydro.model; -const { Route, Handler, UiContextBase } = global.Hydro.service.server; -class WikiHelpHandler extends Handler { - constructor(args) { - super(args); - this.noCheckPermView = true; +interface ConstantArgs { + lang: string; + domainId: string; +} + +const cache = {}; + +const basedir = join(tmpdir(), 'hydro', 'public'); +async function constant(args: ConstantArgs) { + // CompileLangs + const payload = [`window.LANGS=${JSON.stringify(setting.langs)};`]; + + // Locale + let { lang } = args; + if (!global.Hydro.locales[lang]) lang = system.get('server.language'); + payload[0] += `window.LOCALES=${JSON.stringify(global.Hydro.locales[lang])};`; + + // Extra style + let [nav_logo_dark, nav_logo_dark_2x] = system.getMany([ + 'ui-default.nav_logo_dark', 'ui-default.nav_logo_dark_2x', + ]); + const ddoc = await domain.get(args.domainId); + nav_logo_dark = ddoc.ui?.nav_logo_dark || nav_logo_dark; + nav_logo_dark_2x = ddoc.ui?.nav_logo_dark_2x || nav_logo_dark_2x; + payload[0] += `\ + const e = document.createElement('style'); + e.innerHTML = \`\ + ${nav_logo_dark ? `.nav__logo { background-image: url(${nav_logo_dark}) !important }` : ''} + ${nav_logo_dark_2x ? `\ + @media + only screen and (-webkit-min-device-pixel-ratio: 1.5), + only screen and (min-resolution: 1.5dppx), + only screen and (min-resolution: 144dpi) { + .nav__logo, .header--mobile__domain { + background-image: url(${nav_logo_dark_2x}) !important + } + }` : ''}\`; + document.body.appendChild(e);`; + + const files = await readdir(basedir); + const pages = files + .filter((file) => file.endsWith('.page.js')) + .map((i) => readFile(join(basedir, i), 'utf-8')); + payload.push(...await Promise.all(pages)); + + const c = crypto.createHash('sha1'); + c.update(JSON.stringify(payload)); + const version = c.digest('hex'); + cache[version] = { version, payload }; + return version; +} + +bus.on('handler/after', async (that) => { + if (that.response.template) { + that.UiContext.constantVersion = await constant({ + domainId: that.domainId, + lang: that.session.viewLang || that.user.viewLang, + }); } +}); + +class WikiHelpHandler extends Handler { + noCheckPermView = true; async get({ domainId }) { const LANGS = setting.langs; @@ -34,72 +92,15 @@ class WikiHelpHandler extends Handler { } class WikiAboutHandler extends Handler { - constructor(args) { - super(args); - this.noCheckPermView = true; - } + noCheckPermView = true; async get() { this.response.template = 'about.html'; } } -class UiConstantsHandler extends Handler { - constructor(args) { - super(args); - this.noCheckPermView = true; - } - - async get() { - this.response.body = `window.LANGS=${JSON.stringify(setting.langs)}`; - this.response.type = 'text/javascript'; - this.ctx.set('nolog', '1'); - } -} - -class UiSettingsHandler extends Handler { - constructor(args) { - super(args); - this.noCheckPermView = true; - } - - async get({ domainId }) { - const [nav_logo_dark, nav_logo_dark_2x] = system.getMany([ - 'ui-default.nav_logo_dark', 'ui-default.nav_logo_dark_2x', - ]); - const ddoc = await domain.get(domainId); - this.response.body = await this.renderHTML('extra.css', { - nav_logo_dark, - nav_logo_dark_2x, - ...(ddoc.ui || {}), - }); - this.response.type = 'text/css'; - this.ctx.set('nolog', '1'); - } -} - -class LocaleHandler extends Handler { - constructor(args) { - super(args); - this.noCheckPermView = true; - } - - async get({ id }) { - // eslint-disable-next-line prefer-destructuring - id = id.split('.')[0]; - // TODO use language_default setting - if (!global.Hydro.locales[id]) id = system.get('server.language'); - this.response.body = `window.LOCALES=${JSON.stringify(global.Hydro.locales[id])}`; - this.response.type = 'text/javascript'; - this.ctx.set('nolog', '1'); - } -} - class SetThemeHandler extends Handler { - constructor(args) { - super(args); - this.noCheckPermView = true; - } + noCheckPermView = true async get({ theme }) { await user.setById(this.user._id, { theme }); @@ -108,10 +109,7 @@ class SetThemeHandler extends Handler { } class MarkdownHandler extends Handler { - constructor(args) { - super(args); - this.noCheckPermView = true; - } + noCheckPermView = true; async post({ text, html = false, inline = false }) { this.response.body = inline @@ -122,6 +120,14 @@ class MarkdownHandler extends Handler { } } +class UiConstantsHandler extends Handler { + noCheckPermView = true; + + async get({ version }) { + this.response.body = cache[version]; + } +} + class RichMediaHandler extends Handler { async renderUser(domainId, payload) { let d = payload.domainId || domainId; @@ -162,35 +168,11 @@ class RichMediaHandler extends Handler { } } -const getHash = (i) => { - const shasum = crypto.createHash('sha1'); - const file = readFileSync(join(tmpdir(), 'hydro', 'public', i)); - shasum.update(file); - return shasum.digest('hex').substr(0, 10); -}; - -const getUrl = (files) => files.map((i) => `/${i}?${getHash(i)}`); - -bus.on('app/started', () => { - setTimeout(() => { - const files = readdirSync(join(tmpdir(), 'hydro', 'public')); - const pages = files.filter((file) => file.endsWith('.page.js')); - const themes = files.filter((file) => file.endsWith('.theme.js')); - UiContextBase.extraPages = getUrl(pages); - UiContextBase.themes = {}; - for (const theme of themes) { - UiContextBase.themes[theme] = `/${theme}?${getHash(theme)}`; - } - }, 1000); -}); - global.Hydro.handler.ui = async () => { Route('wiki_help', '/wiki/help', WikiHelpHandler); Route('wiki_about', '/wiki/about', WikiAboutHandler); - Route('ui_constants', '/ui-constants.js', UiConstantsHandler); - Route('locale', '/locale/:id', LocaleHandler); Route('set_theme', '/set_theme/:id', SetThemeHandler); - Route('ui_extracss', '/extra.css', UiSettingsHandler); + Route('constant', '/constant', UiConstantsHandler); Route('markdown', '/markdown', MarkdownHandler); Route('media', '/media', RichMediaHandler); }; diff --git a/packages/ui-default/hydro.js b/packages/ui-default/hydro.js index e786308c..1fda6969 100644 --- a/packages/ui-default/hydro.js +++ b/packages/ui-default/hydro.js @@ -24,25 +24,7 @@ function buildSequence(pages, type) { } async function load() { - if (UiContext.extraPages) { - const tasks = []; - const ts = new Date().getTime(); - for (const page of UiContext.extraPages) { - const head = document.getElementsByTagName('head')[0]; - const script = document.createElement('script'); - if (page.includes('.module.')) script.type = 'module'; - script.src = page; - head.appendChild(script); - tasks.push(new Promise((resolve) => { - script.onload = resolve; - })); - } - await Promise.all(tasks); - const time = new Date().getTime() - ts; - if ((process.env.NODE_ENV !== 'production' && time > 16) || time > 256) { - console.warn(`Extra pages loading took ${time}ms`); - } - } + for (const page of window.Hydro.preload) await eval(page); // eslint-disable-line no-eval const pageLoader = new PageLoader(); diff --git a/packages/ui-default/package.json b/packages/ui-default/package.json index 8dd60083..d2bea541 100644 --- a/packages/ui-default/package.json +++ b/packages/ui-default/package.json @@ -1,6 +1,6 @@ { "name": "@hydrooj/ui-default", - "version": "4.18.15", + "version": "4.19.0", "author": "undefined ", "license": "AGPL-3.0", "main": "hydro.js", diff --git a/packages/ui-default/templates/components/user.html b/packages/ui-default/templates/components/user.html index 1efc9266..6c65a693 100644 --- a/packages/ui-default/templates/components/user.html +++ b/packages/ui-default/templates/components/user.html @@ -2,7 +2,7 @@ {% if udoc %}