From 96fa6cc7b9d25409173316a51248c14d3e84e21c Mon Sep 17 00:00:00 2001 From: undefined Date: Mon, 12 Dec 2022 00:53:44 +0800 Subject: [PATCH] ui: frontend api (#470) [skip-cache] --- .eslintignore | 2 +- .eslintrc.yaml | 24 ++- build/prepare.js | 7 + package.json | 4 +- packages/onlyoffice/public/office.page.js | 153 +++++++++--------- packages/ui-default/api.ts | 62 +++++++ .../build/utils/{root.js => root.ts} | 1 - packages/ui-default/bus.ts | 3 +- .../components/DOMAttachedObject.ts | 4 +- .../components/DomainSelectAutoComplete.tsx | 2 +- .../components/ProblemSelectAutoComplete.tsx | 3 +- .../components/UserSelectAutoComplete.tsx | 2 +- .../components/browser-update.page.ts | 3 +- .../ui-default/components/calendar/index.js | 2 +- .../ui-default/components/clipboard.page.ts | 4 +- .../components/contest/contest.page.ts | 4 +- .../ui-default/components/dialog/DomDialog.ts | 2 +- .../ui-default/components/dialog/index.ts | 3 +- .../components/discussion/CommentBox.js | 2 +- .../components/discussion/comments.page.tsx | 8 +- .../components/discussion/history.page.tsx | 4 +- .../components/discussion/reaction.page.tsx | 2 +- .../components/dropdown/Dropdown.js | 5 +- .../components/editor/cmeditor.page.ts | 2 +- .../ui-default/components/editor/index.ts | 3 +- .../ui-default/components/form/form.page.ts | 2 +- .../highlighter/highlighter.page.ts | 3 +- .../components/highlighter/prismjs.js | 2 +- packages/ui-default/components/hint.ts | 3 +- .../components/hitokoto/index.page.js | 4 +- .../ui-default/components/languageselect.tsx | 2 +- .../ui-default/components/marker/Marker.js | 3 +- .../components/marker/MarkerReactive.js | 2 +- .../ui-default/components/media/media.page.js | 2 +- .../components/menu/menu-heading.page.js | 2 +- .../ui-default/components/menu/menu.page.js | 3 +- .../components/message/index.page.ts | 3 +- .../MessagePadDialogueContentContainer.jsx | 9 +- .../MessagePadDialogueListContainer.jsx | 2 +- .../messagepad/MessagePadInputContainer.jsx | 2 +- .../components/messagepad/index.jsx | 3 +- .../ui-default/components/monaco/index.ts | 3 +- .../components/monaco/languages/markdown.ts | 2 +- .../ui-default/components/monaco/loader.ts | 2 +- .../components/navigation/navigation.page.js | 4 +- .../components/notification/index.js | 3 +- .../notification/notification.page.js | 2 +- .../components/preview/preview.page.ts | 7 +- .../components/problem/create.page.js | 3 +- .../components/problemconfig/BasicForm.tsx | 2 +- .../problemconfig/ProblemConfigForm.tsx | 2 +- .../components/problemconfig/ProblemType.tsx | 2 +- .../components/problemconfig/SubtaskTable.tsx | 2 +- .../problemconfig/TestCasesTable.tsx | 2 +- .../ui-default/components/rotator/index.js | 2 +- .../scratchpad/ScratchpadPretestContainer.jsx | 2 +- .../scratchpad/ScratchpadRecordsContainer.jsx | 2 +- .../ScratchpadRecordsRowContainer.jsx | 9 +- .../scratchpad/ScratchpadToolbarContainer.jsx | 4 +- .../components/scratchpad/reducers/ui.ts | 2 +- packages/ui-default/components/selectUser.ts | 3 +- .../components/signin/signInDialog.page.js | 8 +- .../ui-default/components/star/star.page.js | 2 +- .../ui-default/components/time/time.page.js | 2 +- packages/ui-default/components/upload.ts | 7 +- .../ui-default/components/vote/vote.page.js | 2 +- .../zipDownloader/{index.js => index.ts} | 12 +- packages/ui-default/entry.js | 7 +- packages/ui-default/hydro.ts | 5 +- packages/ui-default/index.ts | 17 +- packages/ui-default/modules.js | 37 ----- packages/ui-default/package.json | 2 + packages/ui-default/pages/api.page.tsx | 2 +- packages/ui-default/pages/contest.page.ts | 2 +- .../ui-default/pages/contest_edit.page.ts | 4 +- .../ui-default/pages/domain_group.page.ts | 7 +- packages/ui-default/pages/domain_role.page.js | 7 +- packages/ui-default/pages/domain_user.page.js | 7 +- packages/ui-default/pages/files.page.js | 7 +- .../ui-default/pages/home_messages.page.jsx | 3 +- .../ui-default/pages/home_preference.page.jsx | 7 +- .../ui-default/pages/home_security.page.ts | 7 +- .../ui-default/pages/homework_main.page.js | 5 +- .../ui-default/pages/manage_script.page.js | 2 +- .../pages/manage_user_import.page.js | 4 +- .../ui-default/pages/manage_user_priv.page.js | 4 +- .../ui-default/pages/problem_config.page.tsx | 13 +- .../ui-default/pages/problem_detail.page.jsx | 9 +- .../ui-default/pages/problem_edit.page.js | 7 +- .../ui-default/pages/problem_files.page.js | 7 +- .../ui-default/pages/problem_main.page.js | 8 +- .../ui-default/pages/problem_sidebar.page.ts | 4 +- .../ui-default/pages/problem_submit.page.tsx | 2 +- packages/ui-default/pages/record_main.page.ts | 3 +- packages/ui-default/pages/setting.page.tsx | 2 +- .../ui-default/pages/training_edit.page.ts | 4 +- packages/ui-default/pages/user_login.page.ts | 2 +- packages/ui-default/types.ts | 11 -- packages/ui-default/utils/api.ts | 26 --- packages/ui-default/utils/availableLangs.ts | 12 -- packages/ui-default/utils/base.ts | 153 ++++++++++++++++++ packages/ui-default/utils/delay.js | 5 - .../ui-default/utils/emulateAnchorClick.js | 20 --- packages/ui-default/utils/i18n.js | 8 - packages/ui-default/utils/index.ts | 118 ++++++++++++++ packages/ui-default/utils/loadModule.ts | 14 -- packages/ui-default/utils/loadReactRedux.ts | 2 +- packages/ui-default/utils/mongoId.js | 16 -- packages/ui-default/utils/pipeStream.js | 21 --- packages/ui-default/utils/pjax.js | 2 +- packages/ui-default/utils/request.js | 106 ------------ .../ui-default/utils/{slide.js => slide.ts} | 0 packages/ui-default/utils/substitute.js | 13 -- packages/ui-default/utils/tpl.ts | 31 ---- packages/ui-default/utils/zIndexManager.js | 13 -- packages/ui-default/utils/zip.js | 9 -- 116 files changed, 599 insertions(+), 629 deletions(-) create mode 100644 packages/ui-default/api.ts rename packages/ui-default/build/utils/{root.js => root.ts} (84%) rename packages/ui-default/components/zipDownloader/{index.js => index.ts} (93%) delete mode 100644 packages/ui-default/modules.js delete mode 100644 packages/ui-default/types.ts delete mode 100644 packages/ui-default/utils/api.ts delete mode 100644 packages/ui-default/utils/availableLangs.ts create mode 100644 packages/ui-default/utils/base.ts delete mode 100644 packages/ui-default/utils/delay.js delete mode 100644 packages/ui-default/utils/emulateAnchorClick.js delete mode 100644 packages/ui-default/utils/i18n.js create mode 100644 packages/ui-default/utils/index.ts delete mode 100644 packages/ui-default/utils/loadModule.ts delete mode 100644 packages/ui-default/utils/mongoId.js delete mode 100644 packages/ui-default/utils/pipeStream.js delete mode 100644 packages/ui-default/utils/request.js rename packages/ui-default/utils/{slide.js => slide.ts} (100%) delete mode 100644 packages/ui-default/utils/substitute.js delete mode 100644 packages/ui-default/utils/tpl.ts delete mode 100644 packages/ui-default/utils/zIndexManager.js delete mode 100644 packages/ui-default/utils/zip.js diff --git a/.eslintignore b/.eslintignore index 8b90310b..401c38a5 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,4 +1,4 @@ dist *.d.ts node_modules -public +public/**/*.js \ No newline at end of file diff --git a/.eslintrc.yaml b/.eslintrc.yaml index e3b8a2db..d2d19209 100644 --- a/.eslintrc.yaml +++ b/.eslintrc.yaml @@ -3,7 +3,7 @@ extends: - airbnb-base - airbnb-typescript/base env: - jquery: true + es6: true globals: Atomics: readonly SharedArrayBuffer: readonly @@ -14,8 +14,26 @@ plugins: - simple-import-sort - eslint-plugin-import ignorePatterns: - - public/ - - '*.spec.ts' + - public/**/*.js + - packages/ui-default +overrides: + - files: + - '**/public/**/*.ts' + - '**/public/**/*.page.js' + rules: + '@typescript-eslint/indent': + - warn + - 2 + env: + browser: true + es6: true + jquery: true + globals: + UiContext: true + UserContext: true + externalModules: true + LOCALES: true + LANGS: true rules: '@typescript-eslint/no-shadow': 1 diff --git a/build/prepare.js b/build/prepare.js index 8029fe80..1c0de4d6 100644 --- a/build/prepare.js +++ b/build/prepare.js @@ -1,5 +1,10 @@ const fs = require('fs'); const path = require('path'); +const child = require('child_process'); + +if (fs.existsSync('plugins/patch-package/package.json') && fs.existsSync('node_modules/patch-package/package.json')) { + child.execSync('npx patch-package --patch-dir=plugins/patch-package/patches', { stdio: 'inherit' }); +} const dir = path.dirname(path.dirname(require.resolve('@types/node/package.json'))); const types = fs.readdirSync(dir).filter((i) => !['sharedworker', 'serviceworker'].includes(i)); @@ -51,6 +56,8 @@ const configFlat = (name) => ({ if (!fs.existsSync(path.resolve(process.cwd(), 'plugins'))) { fs.mkdirSync(path.resolve(process.cwd(), 'plugins')); + // Write an empty file to make eslint happy + fs.writeFileSync(path.resolve(process.cwd(), 'plugins/eslint.ts'), ''); } const modules = [ diff --git a/package.json b/package.json index aff4dc8e..29352ffe 100644 --- a/package.json +++ b/package.json @@ -17,8 +17,8 @@ "build:ui:production:webpack": "cross-env NODE_OPTIONS=--max_old_space_size=8192 node packages/ui-default/build --production", "test": "mocha", "benchmark": "cross-env BENCHMARK=true mocha", - "lint": "eslint packages --ext ts --fix", - "lint:ci": "eslint packages --ext ts", + "lint": "eslint packages plugins --ext ts --fix", + "lint:ci": "eslint packages plugins --ext ts", "lint:ui": "yarn workspace @hydrooj/ui-default lint --ext .js,.ts,.jsx,.tsx . --fix", "lint:ui:ci": "yarn workspace @hydrooj/ui-default lint --ext .js,.ts,.jsx,.tsx .", "debug": "node --trace-warnings --async-stack-traces --trace-deprecation node_modules/hydrooj/bin/hydrooj --debug --template", diff --git a/packages/onlyoffice/public/office.page.js b/packages/onlyoffice/public/office.page.js index 203f9af5..50a0bda2 100644 --- a/packages/onlyoffice/public/office.page.js +++ b/packages/onlyoffice/public/office.page.js @@ -1,82 +1,81 @@ -(() => { - const { AutoloadPage } = window.Hydro; - const { $ } = window.node_modules; +import { $ } from '@hydrooj/ui-default'; - let loaded = false; - async function load() { - if (loaded) return Promise.resolve(); - return new Promise((resolve, reject) => { - const scriptElement = document.createElement('script'); - scriptElement.src = UiContext.onlyofficeApi; - scriptElement.async = true; - document.head.appendChild(scriptElement); - scriptElement.onload = resolve; - scriptElement.onerror = reject; - loaded = true; - }); - } +const { AutoloadPage } = window.Hydro; - const loader = mode => async (element) => { - const id = `${mode}-${Math.random().toString()}`; - $(element).attr('id', id); - const url = $(element).text(); - const t = new URL(url, window.location.href).pathname.split('.'); - const n = new URL(url, window.location.href).pathname.split('/'); - const lang = UserContext.viewLang.includes('_') ? UserContext.viewLang.split('_')[0] : UserContext.viewLang; - // eslint-disable-next-line no-undef - window.editor = new DocsAPI.DocEditor(id, { - document: { - fileType: t[t.length - 1], - key: Math.random().toString(16), - title: decodeURIComponent(n[n.length - 1]), - url: new URL(url, window.location.href), - permissions: { - comment: false, - copy: true, - download: true, - edit: false, - fillForms: false, - modifyContentControl: false, - modifyFilter: false, - print: true, - protect: false, - review: false, - }, - }, - editorConfig: { - lang, - mode: 'view', - user: { - group: "Hydro", - id: UserContext._id.toString(), - name: UserContext.uname, - }, - customization: { - chat: false, - comments: false, - help: false, - hideRulers: true, - plugins: false, - } - }, - documentType: mode, - height: mode === 'slide' ? '560px' : '900px', - }); - } +let loaded = false; +async function load() { + if (loaded) return Promise.resolve(); + return new Promise((resolve, reject) => { + const scriptElement = document.createElement('script'); + scriptElement.src = UiContext.onlyofficeApi; + scriptElement.async = true; + document.head.appendChild(scriptElement); + scriptElement.onload = resolve; + scriptElement.onerror = reject; + loaded = true; + }); +} - const getEles = types => { - const eles = []; - for (const type of types) eles.push(...$('div[data-' + type + ']').get()); - return eles; - } +const loader = (mode) => async (element) => { + const id = `${mode}-${Math.random().toString()}`; + $(element).attr('id', id); + const url = $(element).text(); + const t = new URL(url, window.location.href).pathname.split('.'); + const n = new URL(url, window.location.href).pathname.split('/'); + const lang = UserContext.viewLang.includes('_') ? UserContext.viewLang.split('_')[0] : UserContext.viewLang; + // eslint-disable-next-line no-undef + window.editor = new DocsAPI.DocEditor(id, { + document: { + fileType: t[t.length - 1], + key: Math.random().toString(16), + title: decodeURIComponent(n[n.length - 1]), + url: new URL(url, window.location.href), + permissions: { + comment: false, + copy: true, + download: true, + edit: false, + fillForms: false, + modifyContentControl: false, + modifyFilter: false, + print: true, + protect: false, + review: false, + }, + }, + editorConfig: { + lang, + mode: 'view', + user: { + group: 'Hydro', + id: UserContext._id.toString(), + name: UserContext.uname, + }, + customization: { + chat: false, + comments: false, + help: false, + hideRulers: true, + plugins: false, + }, + }, + documentType: mode, + height: mode === 'slide' ? '560px' : '900px', + }); +}; - const page = new AutoloadPage('onlyoffice', async () => { - let all = getEles(['doc', 'docx', 'cell', 'xls', 'xlsx', 'slide', 'ppt', 'pptx']); - if (all.length) await load(); - getEles(['doc', 'docx']).forEach(loader('word')); - getEles(['cell', 'xls', 'xlsx']).forEach(loader('cell')) - getEles(['slide', 'ppt', 'pptx']).forEach(loader('slide')); - }); +const getEles = (types) => { + const eles = []; + for (const type of types) eles.push(...$(`div[data-${type}]`).get()); + return eles; +}; - window.Hydro.extraPages.push(page); -})(); +const page = new AutoloadPage('onlyoffice', async () => { + const all = getEles(['doc', 'docx', 'cell', 'xls', 'xlsx', 'slide', 'ppt', 'pptx']); + if (all.length) await load(); + getEles(['doc', 'docx']).forEach(loader('word')); + getEles(['cell', 'xls', 'xlsx']).forEach(loader('cell')); + getEles(['slide', 'ppt', 'pptx']).forEach(loader('slide')); +}); + +window.Hydro.extraPages.push(page); diff --git a/packages/ui-default/api.ts b/packages/ui-default/api.ts new file mode 100644 index 00000000..6411f9e0 --- /dev/null +++ b/packages/ui-default/api.ts @@ -0,0 +1,62 @@ +export * from './utils'; +export { default as Notification } from './components/notification'; +export * from './components/dialog'; +export * as bus from './bus'; +export { default as loadMonaco } from './components/monaco/loader'; +export * as redux from 'react-redux'; +export * from './components/zipDownloader'; +export { default as $ } from 'jquery'; +export { default as _ } from 'lodash'; +export { default as React } from 'react'; +export { default as ReactDOM } from 'react-dom/client'; +export * from './misc/Page'; + +export default async function load(name: string) { + if (window.node_modules[name]) return window.node_modules[name]; + if (name === 'echarts') return import('echarts'); + if (name === 'moment') return import('moment'); + throw new Error(`Module ${name} not found`); +} + +import AutoComplete from './components/autocomplete'; +import CustomSelectAutoComplete from './components/autocomplete/CustomSelectAutoComplete'; +import DomainSelectAutoComplete from './components/autocomplete/DomainSelectAutoComplete'; +import ProblemSelectAutoComplete from './components/autocomplete/ProblemSelectAutoComplete'; +import UserSelectAutoComplete from './components/autocomplete/UserSelectAutoComplete'; + +export { + load, AutoComplete, UserSelectAutoComplete, ProblemSelectAutoComplete, DomainSelectAutoComplete, CustomSelectAutoComplete, +}; +export const { UserContext, UiContext } = window; +export function addPage(page: import('./misc/Page').Page | (() => Promise | void)) { + window.Hydro.extraPages.push(page); +} + +declare global { + interface Window { + LANGS: Record; + } + + let UserContext: Record; + let UiContext: Record; +} + +// Below are old version api compat +/* eslint-disable import/order */ +import $ from 'jquery'; +import _ from 'lodash'; +import React from 'react'; +import ReactDOM from 'react-dom/client'; +import * as redux from 'react-redux'; + +const modules = { + _, $, React, redux, ReactDOM, load, +}; + +declare global { + interface Window { + node_modules: typeof modules; + } +} + +Object.assign(window, { node_modules: modules, $, jQuery: $ }); diff --git a/packages/ui-default/build/utils/root.js b/packages/ui-default/build/utils/root.ts similarity index 84% rename from packages/ui-default/build/utils/root.js rename to packages/ui-default/build/utils/root.ts index b402e614..0468e917 100644 --- a/packages/ui-default/build/utils/root.js +++ b/packages/ui-default/build/utils/root.ts @@ -1,4 +1,3 @@ -/* eslint-disable */ import path from 'path'; export default function root(fn = '.') { diff --git a/packages/ui-default/bus.ts b/packages/ui-default/bus.ts index 1a7348fe..cd2b73e9 100644 --- a/packages/ui-default/bus.ts +++ b/packages/ui-default/bus.ts @@ -1,4 +1,5 @@ /* eslint-disable no-await-in-loop */ +import type * as monaco from 'monaco-editor'; // eslint-disable-next-line @typescript-eslint/naming-convention const _hooks: Record any>> = {}; @@ -11,7 +12,7 @@ export type Disposable = () => void; export type VoidReturn = Promise | any; export interface EventMap extends Record { - 'scratchpadEditorCreate': (editor, monaco) => any; + 'scratchpadEditorCreate': (editor: monaco.editor.IStandaloneCodeEditor, monaco: typeof import('monaco-editor')) => any; } function getHooks(name: K) { diff --git a/packages/ui-default/components/DOMAttachedObject.ts b/packages/ui-default/components/DOMAttachedObject.ts index 05707afa..5c7a9d9c 100644 --- a/packages/ui-default/components/DOMAttachedObject.ts +++ b/packages/ui-default/components/DOMAttachedObject.ts @@ -23,7 +23,7 @@ function monitorResource(resource) { } } -export default class DOMAttachedObject { +class DOMAttachedObject { static uniqueIdCounter = 0; static DOMAttachKey: string; static DOMAttachSelector: string; @@ -127,3 +127,5 @@ export default class DOMAttachedObject { if (monitorDetach) monitorResource(this); } } + +export default DOMAttachedObject as typeof DOMAttachedObject & DOMAttachedObject; diff --git a/packages/ui-default/components/autocomplete/components/DomainSelectAutoComplete.tsx b/packages/ui-default/components/autocomplete/components/DomainSelectAutoComplete.tsx index acd65bf7..bcae9330 100644 --- a/packages/ui-default/components/autocomplete/components/DomainSelectAutoComplete.tsx +++ b/packages/ui-default/components/autocomplete/components/DomainSelectAutoComplete.tsx @@ -1,7 +1,7 @@ import type { DomainDoc } from 'hydrooj/src/interface'; import PropTypes from 'prop-types'; import React, { forwardRef } from 'react'; -import request from 'vj/utils/request'; +import { request } from 'vj/utils'; import AutoComplete, { AutoCompleteHandle, AutoCompleteProps } from './AutoComplete'; const DomainSelectAutoComplete = forwardRef, AutoCompleteProps>((props, ref) => ( diff --git a/packages/ui-default/components/autocomplete/components/ProblemSelectAutoComplete.tsx b/packages/ui-default/components/autocomplete/components/ProblemSelectAutoComplete.tsx index b4aa4bde..e0c1b26d 100644 --- a/packages/ui-default/components/autocomplete/components/ProblemSelectAutoComplete.tsx +++ b/packages/ui-default/components/autocomplete/components/ProblemSelectAutoComplete.tsx @@ -1,8 +1,7 @@ import type { ProblemDoc } from 'hydrooj/src/interface'; import PropTypes from 'prop-types'; import React, { forwardRef } from 'react'; -import api, { gql } from 'vj/utils/api'; -import request from 'vj/utils/request'; +import { api, gql, request } from 'vj/utils'; import AutoComplete, { AutoCompleteHandle, AutoCompleteProps } from './AutoComplete'; const ProblemSelectAutoComplete = forwardRef, AutoCompleteProps>((props, ref) => ( diff --git a/packages/ui-default/components/autocomplete/components/UserSelectAutoComplete.tsx b/packages/ui-default/components/autocomplete/components/UserSelectAutoComplete.tsx index b887cfca..ba03aa46 100644 --- a/packages/ui-default/components/autocomplete/components/UserSelectAutoComplete.tsx +++ b/packages/ui-default/components/autocomplete/components/UserSelectAutoComplete.tsx @@ -1,7 +1,7 @@ import type { Udoc } from 'hydrooj/src/interface'; import PropTypes from 'prop-types'; import React, { forwardRef } from 'react'; -import api, { gql } from 'vj/utils/api'; +import { api, gql } from 'vj/utils'; import AutoComplete, { AutoCompleteHandle, AutoCompleteProps } from './AutoComplete'; // eslint-disable-next-line prefer-arrow-callback diff --git a/packages/ui-default/components/browser-update.page.ts b/packages/ui-default/components/browser-update.page.ts index 09924576..6994ad83 100644 --- a/packages/ui-default/components/browser-update.page.ts +++ b/packages/ui-default/components/browser-update.page.ts @@ -1,6 +1,5 @@ import { AutoloadPage } from 'vj/misc/Page'; -import i18n from 'vj/utils/i18n'; -import tpl from 'vj/utils/tpl'; +import { i18n, tpl } from 'vj/utils'; import { InfoDialog } from './dialog'; function isSupported() { diff --git a/packages/ui-default/components/calendar/index.js b/packages/ui-default/components/calendar/index.js index ad39257b..180b5202 100644 --- a/packages/ui-default/components/calendar/index.js +++ b/packages/ui-default/components/calendar/index.js @@ -1,7 +1,7 @@ import $ from 'jquery'; import _ from 'lodash'; import moment from 'moment'; -import tpl from 'vj/utils/tpl'; +import { tpl } from 'vj/utils'; export default class Calendar { constructor(events) { diff --git a/packages/ui-default/components/clipboard.page.ts b/packages/ui-default/components/clipboard.page.ts index 9d19d5c7..18241c63 100644 --- a/packages/ui-default/components/clipboard.page.ts +++ b/packages/ui-default/components/clipboard.page.ts @@ -1,9 +1,7 @@ import Clipboard from 'clipboard'; import Notification from 'vj/components/notification'; import { AutoloadPage } from 'vj/misc/Page'; -import base64 from 'vj/utils/base64'; -import i18n from 'vj/utils/i18n'; -import substitute from 'vj/utils/substitute'; +import { base64, i18n, substitute } from 'vj/utils'; export default new AutoloadPage('clipboard', () => { $('[data-copy]').get().forEach((el) => { diff --git a/packages/ui-default/components/contest/contest.page.ts b/packages/ui-default/components/contest/contest.page.ts index 2b6ac5e7..fe98096a 100644 --- a/packages/ui-default/components/contest/contest.page.ts +++ b/packages/ui-default/components/contest/contest.page.ts @@ -1,9 +1,7 @@ import $ from 'jquery'; import Notification from 'vj/components/notification'; import { AutoloadPage } from 'vj/misc/Page'; -import delay from 'vj/utils/delay'; -import i18n from 'vj/utils/i18n'; -import request from 'vj/utils/request'; +import { delay, i18n, request } from 'vj/utils'; const contestPage = new AutoloadPage('contestPage', () => { $('[data-contest-code]').on('click', (ev) => { diff --git a/packages/ui-default/components/dialog/DomDialog.ts b/packages/ui-default/components/dialog/DomDialog.ts index 2ffaa213..f03ef52b 100644 --- a/packages/ui-default/components/dialog/DomDialog.ts +++ b/packages/ui-default/components/dialog/DomDialog.ts @@ -1,7 +1,7 @@ import $ from 'jquery'; import _ from 'lodash'; import DOMAttachedObject from 'vj/components/DOMAttachedObject'; -import zIndexManager from 'vj/utils/zIndexManager'; +import { zIndexManager } from 'vj/utils'; export interface DialogOptions { classes: string; diff --git a/packages/ui-default/components/dialog/index.ts b/packages/ui-default/components/dialog/index.ts index 5c061d2c..b16f31d6 100644 --- a/packages/ui-default/components/dialog/index.ts +++ b/packages/ui-default/components/dialog/index.ts @@ -1,6 +1,5 @@ import $ from 'jquery'; -import i18n from 'vj/utils/i18n'; -import tpl from 'vj/utils/tpl'; +import { i18n, tpl } from 'vj/utils'; import DomDialog, { DialogOptions } from './DomDialog'; export class Dialog { diff --git a/packages/ui-default/components/discussion/CommentBox.js b/packages/ui-default/components/discussion/CommentBox.js index a1e2f165..3f4f27b2 100644 --- a/packages/ui-default/components/discussion/CommentBox.js +++ b/packages/ui-default/components/discussion/CommentBox.js @@ -3,7 +3,7 @@ import _ from 'lodash'; import DOMAttachedObject from 'vj/components/DOMAttachedObject'; import TextareaHandler from 'vj/components/editor/textareaHandler'; import Notification from 'vj/components/notification'; -import request from 'vj/utils/request'; +import { request } from 'vj/utils'; let initialized = false; const $template = $('.dczcomments__box').eq(0).clone(); diff --git a/packages/ui-default/components/discussion/comments.page.tsx b/packages/ui-default/components/discussion/comments.page.tsx index 41a72373..35d7e85d 100644 --- a/packages/ui-default/components/discussion/comments.page.tsx +++ b/packages/ui-default/components/discussion/comments.page.tsx @@ -4,11 +4,9 @@ import $ from 'jquery'; import { ConfirmDialog } from 'vj/components/dialog'; import CommentBox from 'vj/components/discussion/CommentBox'; import { AutoloadPage } from 'vj/misc/Page'; -import delay from 'vj/utils/delay'; -import i18n from 'vj/utils/i18n'; -import request from 'vj/utils/request'; -import { slideDown, slideUp } from 'vj/utils/slide'; -import tpl from 'vj/utils/tpl'; +import { + delay, i18n, request, slideDown, slideUp, tpl, +} from 'vj/utils'; const $replyTemplate = $('.commentbox-container').eq(0).clone(); diff --git a/packages/ui-default/components/discussion/history.page.tsx b/packages/ui-default/components/discussion/history.page.tsx index 8cb56e55..97dd1250 100644 --- a/packages/ui-default/components/discussion/history.page.tsx +++ b/packages/ui-default/components/discussion/history.page.tsx @@ -5,9 +5,7 @@ import { QueryClient, QueryClientProvider, useQuery } from 'react-query'; import TimeAgo from 'timeago-react'; import { InfoDialog } from 'vj/components/dialog'; import { AutoloadPage } from 'vj/misc/Page'; -import i18n from 'vj/utils/i18n'; -import request from 'vj/utils/request'; -import tpl from 'vj/utils/tpl'; +import { i18n, request, tpl } from 'vj/utils'; async function historyDialog(payload, time, uid) { const ts = new Date(time).getTime(); diff --git a/packages/ui-default/components/discussion/reaction.page.tsx b/packages/ui-default/components/discussion/reaction.page.tsx index 9f072070..98bb1827 100644 --- a/packages/ui-default/components/discussion/reaction.page.tsx +++ b/packages/ui-default/components/discussion/reaction.page.tsx @@ -6,7 +6,7 @@ import { chunk } from 'lodash'; import * as React from 'react'; import ReactDOM from 'react-dom/client'; import { AutoloadPage } from 'vj/misc/Page'; -import request from 'vj/utils/request'; +import { request } from 'vj/utils'; function renderReactions(reactions, self, rootEle) { let html = ''; diff --git a/packages/ui-default/components/dropdown/Dropdown.js b/packages/ui-default/components/dropdown/Dropdown.js index 75809207..a75c1911 100644 --- a/packages/ui-default/components/dropdown/Dropdown.js +++ b/packages/ui-default/components/dropdown/Dropdown.js @@ -2,8 +2,7 @@ import _ from 'lodash'; import Drop from 'tether-drop'; import responsiveCutoff from 'vj/breakpoints.json'; import DOMAttachedObject from 'vj/components/DOMAttachedObject'; -import { isBelow } from 'vj/utils/mediaQuery'; -import zIndexManager from 'vj/utils/zIndexManager'; +import { mediaQuery, zIndexManager } from 'vj/utils'; export default class Dropdown extends DOMAttachedObject { static DOMAttachKey = 'vjDropdownInstance'; @@ -12,7 +11,7 @@ export default class Dropdown extends DOMAttachedObject { constructor($dom, options = {}) { if ($dom.attr('data-dropdown-trigger-desktop-only') !== undefined) { - if (isBelow(responsiveCutoff.mobile)) { + if (mediaQuery.isBelow(responsiveCutoff.mobile)) { super(null); return; } diff --git a/packages/ui-default/components/editor/cmeditor.page.ts b/packages/ui-default/components/editor/cmeditor.page.ts index 37114ffc..6300caaa 100644 --- a/packages/ui-default/components/editor/cmeditor.page.ts +++ b/packages/ui-default/components/editor/cmeditor.page.ts @@ -1,5 +1,5 @@ import { AutoloadPage } from 'vj/misc/Page'; -import delay from 'vj/utils/delay'; +import { delay } from 'vj/utils'; import CmEditor from '.'; function runSubstitute($container: JQuery) { diff --git a/packages/ui-default/components/editor/index.ts b/packages/ui-default/components/editor/index.ts index a8c6f9ae..408f6d93 100644 --- a/packages/ui-default/components/editor/index.ts +++ b/packages/ui-default/components/editor/index.ts @@ -3,8 +3,7 @@ import _ from 'lodash'; import { nanoid } from 'nanoid'; import DOMAttachedObject from 'vj/components/DOMAttachedObject'; import Notification from 'vj/components/notification'; -import i18n from 'vj/utils/i18n'; -import request from 'vj/utils/request'; +import { i18n, request } from 'vj/utils'; export const config = { toolbar: [ diff --git a/packages/ui-default/components/form/form.page.ts b/packages/ui-default/components/form/form.page.ts index aa9f8eac..4859fd1b 100644 --- a/packages/ui-default/components/form/form.page.ts +++ b/packages/ui-default/components/form/form.page.ts @@ -1,6 +1,6 @@ import $ from 'jquery'; import { AutoloadPage } from 'vj/misc/Page'; -import delay from 'vj/utils/delay'; +import { delay } from 'vj/utils'; const formPage = new AutoloadPage('formPage', () => { $(document).on('vjFormDisableUpdate', 'input, select, textarea', (ev) => { diff --git a/packages/ui-default/components/highlighter/highlighter.page.ts b/packages/ui-default/components/highlighter/highlighter.page.ts index 5bde403a..b6eee7d6 100644 --- a/packages/ui-default/components/highlighter/highlighter.page.ts +++ b/packages/ui-default/components/highlighter/highlighter.page.ts @@ -1,7 +1,6 @@ import $ from 'jquery'; import { AutoloadPage } from 'vj/misc/Page'; -import i18n from 'vj/utils/i18n'; -import tpl from 'vj/utils/tpl'; +import { i18n, tpl } from 'vj/utils'; const highlighterPage = new AutoloadPage('highlighterPage', () => { import('./prismjs').then(({ default: prismjs }) => { diff --git a/packages/ui-default/components/highlighter/prismjs.js b/packages/ui-default/components/highlighter/prismjs.js index d73f8893..13c1a3d7 100644 --- a/packages/ui-default/components/highlighter/prismjs.js +++ b/packages/ui-default/components/highlighter/prismjs.js @@ -11,7 +11,7 @@ import $ from 'jquery'; import components from 'prismjs/components'; import getLoader from 'prismjs/dependencies'; import Notification from 'vj/components/notification/index'; -import i18n from 'vj/utils/i18n'; +import { i18n } from 'vj/utils'; import languageMeta from './meta'; const files = require.context('prismjs/components/', true, /prism-[a-z0-9-]+\.js/); diff --git a/packages/ui-default/components/hint.ts b/packages/ui-default/components/hint.ts index e8e7159c..eb71b2c8 100644 --- a/packages/ui-default/components/hint.ts +++ b/packages/ui-default/components/hint.ts @@ -1,7 +1,6 @@ import $ from 'jquery'; import { InfoDialog } from 'vj/components/dialog'; -import i18n from 'vj/utils/i18n'; -import tpl from 'vj/utils/tpl'; +import { i18n, tpl } from 'vj/utils'; export default function createHint(message: string, element?: any) { if (i18n(message) === message || !element) return; diff --git a/packages/ui-default/components/hitokoto/index.page.js b/packages/ui-default/components/hitokoto/index.page.js index 4ab68d78..8db34005 100644 --- a/packages/ui-default/components/hitokoto/index.page.js +++ b/packages/ui-default/components/hitokoto/index.page.js @@ -1,8 +1,6 @@ import $ from 'jquery'; import { NamedPage } from 'vj/misc/Page'; -import i18n from 'vj/utils/i18n'; -import request from 'vj/utils/request'; -import tpl from 'vj/utils/tpl'; +import { i18n, request, tpl } from 'vj/utils'; export default new NamedPage('homepage', () => { function getHitokoto($containers) { diff --git a/packages/ui-default/components/languageselect.tsx b/packages/ui-default/components/languageselect.tsx index 8282881b..4e7d2864 100644 --- a/packages/ui-default/components/languageselect.tsx +++ b/packages/ui-default/components/languageselect.tsx @@ -1,7 +1,7 @@ import $ from 'jquery'; import React from 'react'; import ReactDOM from 'react-dom/client'; -import i18n from 'vj/utils/i18n'; +import { i18n } from 'vj/utils'; export default function LanguageSelect({ fieldSelector, firstLoadMain, firstLoadSub, availableLangs, mainLangs, diff --git a/packages/ui-default/components/marker/Marker.js b/packages/ui-default/components/marker/Marker.js index 4a4ce323..00e93c80 100644 --- a/packages/ui-default/components/marker/Marker.js +++ b/packages/ui-default/components/marker/Marker.js @@ -1,8 +1,7 @@ import $ from 'jquery'; import _ from 'lodash'; import Tooltip from 'vj/components/tooltip/Tooltip'; -import delay from 'vj/utils/delay'; -import i18n from 'vj/utils/i18n'; +import { delay, i18n } from 'vj/utils'; const MARKER_ID = `marker_${Math.floor(Math.random() * 0xFFFFFF).toString(16)}`; const MARKER_OFFSET = 20; diff --git a/packages/ui-default/components/marker/MarkerReactive.js b/packages/ui-default/components/marker/MarkerReactive.js index 38022e5c..92a2272d 100644 --- a/packages/ui-default/components/marker/MarkerReactive.js +++ b/packages/ui-default/components/marker/MarkerReactive.js @@ -1,7 +1,7 @@ import $ from 'jquery'; import _ from 'lodash'; import DOMAttachedObject from 'vj/components/DOMAttachedObject'; -import delay from 'vj/utils/delay'; +import { delay } from 'vj/utils'; import Marker from './Marker'; export default class MarkerReactive extends DOMAttachedObject { diff --git a/packages/ui-default/components/media/media.page.js b/packages/ui-default/components/media/media.page.js index abb1cc2d..3892db57 100644 --- a/packages/ui-default/components/media/media.page.js +++ b/packages/ui-default/components/media/media.page.js @@ -1,6 +1,6 @@ import $ from 'jquery'; import { AutoloadPage } from 'vj/misc/Page'; -import request from 'vj/utils/request'; +import { request } from 'vj/utils'; export default new AutoloadPage('media', async () => { async function parseMedia($dom = $(document.body)) { diff --git a/packages/ui-default/components/menu/menu-heading.page.js b/packages/ui-default/components/menu/menu-heading.page.js index 7ac908bf..bacd1d23 100644 --- a/packages/ui-default/components/menu/menu-heading.page.js +++ b/packages/ui-default/components/menu/menu-heading.page.js @@ -1,6 +1,6 @@ import $ from 'jquery'; import { AutoloadPage } from 'vj/misc/Page'; -import tpl from 'vj/utils/tpl'; +import { tpl } from 'vj/utils'; const menuHeadingPage = new AutoloadPage('menuHeadingPage', null, () => { $('[data-heading-extract-to]').get().forEach((container) => { diff --git a/packages/ui-default/components/menu/menu.page.js b/packages/ui-default/components/menu/menu.page.js index 74c74699..23aef115 100644 --- a/packages/ui-default/components/menu/menu.page.js +++ b/packages/ui-default/components/menu/menu.page.js @@ -1,7 +1,6 @@ import $ from 'jquery'; import { AutoloadPage } from 'vj/misc/Page'; -import delay from 'vj/utils/delay'; -import { slideDown } from 'vj/utils/slide'; +import { delay, slideDown } from 'vj/utils'; function expandMenu($menu) { slideDown($menu, 500, { opacity: 0 }, { opacity: 1 }); diff --git a/packages/ui-default/components/message/index.page.ts b/packages/ui-default/components/message/index.page.ts index d23d0e10..6914a140 100644 --- a/packages/ui-default/components/message/index.page.ts +++ b/packages/ui-default/components/message/index.page.ts @@ -4,8 +4,7 @@ import { InfoDialog } from 'vj/components/dialog'; import VjNotification from 'vj/components/notification/index'; import { FLAG_ALERT } from 'vj/constant/message'; import { AutoloadPage } from 'vj/misc/Page'; -import i18n from 'vj/utils/i18n'; -import tpl from 'vj/utils/tpl'; +import { i18n, tpl } from 'vj/utils'; const onmessage = (msg) => { console.log('Received message', msg); diff --git a/packages/ui-default/components/messagepad/MessagePadDialogueContentContainer.jsx b/packages/ui-default/components/messagepad/MessagePadDialogueContentContainer.jsx index 3f34d5ff..4a6f4335 100644 --- a/packages/ui-default/components/messagepad/MessagePadDialogueContentContainer.jsx +++ b/packages/ui-default/components/messagepad/MessagePadDialogueContentContainer.jsx @@ -6,8 +6,7 @@ import moment from 'moment'; import React from 'react'; import { connect } from 'react-redux'; import TimeAgo from 'timeago-react'; -import i18n from 'vj/utils/i18n'; -import { parse as parseMongoId } from 'vj/utils/mongoId'; +import { i18n, mongoId } from 'vj/utils'; import Message from './MessageComponent'; const mapStateToProps = (state) => ({ @@ -70,7 +69,7 @@ export default connect(mapStateToProps)(class MessagePadDialogueContentContainer renderInner() { if (this.props.activeId === null) return []; const sorted = this.props.item.messages - .sort((msg1, msg2) => parseMongoId(msg1._id).timestamp - parseMongoId(msg2._id).timestamp); + .sort((msg1, msg2) => mongoId(msg1._id).timestamp - mongoId(msg2._id).timestamp); return sorted.map((msg) => (
{this.renderContent(msg)}
-
)); diff --git a/packages/ui-default/components/messagepad/MessagePadDialogueListContainer.jsx b/packages/ui-default/components/messagepad/MessagePadDialogueListContainer.jsx index cdf9e9f6..e85732e4 100644 --- a/packages/ui-default/components/messagepad/MessagePadDialogueListContainer.jsx +++ b/packages/ui-default/components/messagepad/MessagePadDialogueListContainer.jsx @@ -4,7 +4,7 @@ import $ from 'jquery'; import _ from 'lodash'; import React from 'react'; import { connect } from 'react-redux'; -import i18n from 'vj/utils/i18n'; +import { i18n } from 'vj/utils'; import ListItem from './DialogueListItemComponent'; const mapStateToProps = (state) => ({ diff --git a/packages/ui-default/components/messagepad/MessagePadInputContainer.jsx b/packages/ui-default/components/messagepad/MessagePadInputContainer.jsx index ff7b6d3f..eda74702 100644 --- a/packages/ui-default/components/messagepad/MessagePadInputContainer.jsx +++ b/packages/ui-default/components/messagepad/MessagePadInputContainer.jsx @@ -3,7 +3,7 @@ import PropTypes from 'prop-types'; import React from 'react'; import { connect } from 'react-redux'; import Icon from 'vj/components/react/IconComponent'; -import request from 'vj/utils/request'; +import { request } from 'vj/utils'; const mapStateToProps = (state) => ({ activeId: state.activeId, diff --git a/packages/ui-default/components/messagepad/index.jsx b/packages/ui-default/components/messagepad/index.jsx index d62f8975..73b298b1 100644 --- a/packages/ui-default/components/messagepad/index.jsx +++ b/packages/ui-default/components/messagepad/index.jsx @@ -2,8 +2,7 @@ import PropTypes from 'prop-types'; import React from 'react'; import { connect } from 'react-redux'; import Icon from 'vj/components/react/IconComponent'; -import i18n from 'vj/utils/i18n'; -import request from 'vj/utils/request'; +import { i18n, request } from 'vj/utils'; import MessagePadDialogueContent from './MessagePadDialogueContentContainer'; import MessagePadDialogueList from './MessagePadDialogueListContainer'; import MessagePadInput from './MessagePadInputContainer'; diff --git a/packages/ui-default/components/monaco/index.ts b/packages/ui-default/components/monaco/index.ts index d6c24378..fb19fd9a 100644 --- a/packages/ui-default/components/monaco/index.ts +++ b/packages/ui-default/components/monaco/index.ts @@ -6,8 +6,7 @@ import * as monaco from 'monaco-editor/esm/vs/editor/editor.api'; import { IQuickInputService } from 'monaco-editor/esm/vs/platform/quickinput/common/quickInput'; import list from 'monaco-themes/themes/themelist.json'; import { nanoid } from 'nanoid'; -import i18n from 'vj/utils/i18n'; -import request from 'vj/utils/request'; +import { i18n, request } from 'vj/utils'; export default monaco; export const customOptions: monaco.editor.IStandaloneDiffEditorConstructionOptions = JSON.parse(localStorage.getItem('editor.config') || '{}'); diff --git a/packages/ui-default/components/monaco/languages/markdown.ts b/packages/ui-default/components/monaco/languages/markdown.ts index 3676257a..f6f6901f 100644 --- a/packages/ui-default/components/monaco/languages/markdown.ts +++ b/packages/ui-default/components/monaco/languages/markdown.ts @@ -1,7 +1,7 @@ import keyword from 'emojis-keywords'; import list from 'emojis-list'; import * as monaco from 'monaco-editor/esm/vs/editor/editor.api'; -import api, { gql } from 'vj/utils/api'; +import { api, gql } from 'vj/utils'; const qqEmojies = [ 'weixiao', diff --git a/packages/ui-default/components/monaco/loader.ts b/packages/ui-default/components/monaco/loader.ts index 8e84e35e..b2ca5662 100644 --- a/packages/ui-default/components/monaco/loader.ts +++ b/packages/ui-default/components/monaco/loader.ts @@ -1,4 +1,4 @@ -import loadExternalModule from 'vj/utils/loadModule'; +import { loadExternalModule } from 'vj/utils'; let loaded; const loaders = { diff --git a/packages/ui-default/components/navigation/navigation.page.js b/packages/ui-default/components/navigation/navigation.page.js index 567efb3c..77c0c0df 100644 --- a/packages/ui-default/components/navigation/navigation.page.js +++ b/packages/ui-default/components/navigation/navigation.page.js @@ -3,9 +3,7 @@ import Slideout from 'slideout'; import Notification from 'vj/components/notification'; import selectUser from 'vj/components/selectUser'; import { AutoloadPage } from 'vj/misc/Page'; -import i18n from 'vj/utils/i18n'; -import request from 'vj/utils/request'; -import tpl from 'vj/utils/tpl'; +import { i18n, request, tpl } from 'vj/utils'; function handleNavLogoutClick(ev) { const $logoutLink = $(ev.currentTarget); diff --git a/packages/ui-default/components/notification/index.js b/packages/ui-default/components/notification/index.js index ed231571..3709dbba 100644 --- a/packages/ui-default/components/notification/index.js +++ b/packages/ui-default/components/notification/index.js @@ -1,7 +1,6 @@ import { Intent, Position, Toaster } from '@blueprintjs/core'; import $ from 'jquery'; -import tpl from 'vj/utils/tpl'; -import zIndexManager from 'vj/utils/zIndexManager'; +import { tpl, zIndexManager } from 'vj/utils/base'; const ToasterContainer = document.createElement('div'); ToasterContainer.style.position = 'fixed'; diff --git a/packages/ui-default/components/notification/notification.page.js b/packages/ui-default/components/notification/notification.page.js index 0060a503..eb750d4a 100644 --- a/packages/ui-default/components/notification/notification.page.js +++ b/packages/ui-default/components/notification/notification.page.js @@ -1,6 +1,6 @@ import Notification from 'vj/components/notification/index'; import { AutoloadPage } from 'vj/misc/Page'; -import i18n from 'vj/utils/i18n'; +import { i18n } from 'vj/utils'; export default new AutoloadPage('notificationPage', (pagename) => { const message = i18n(`Hint::Page::${pagename}`); diff --git a/packages/ui-default/components/preview/preview.page.ts b/packages/ui-default/components/preview/preview.page.ts index 37c72835..6857a503 100644 --- a/packages/ui-default/components/preview/preview.page.ts +++ b/packages/ui-default/components/preview/preview.page.ts @@ -4,10 +4,9 @@ import { nanoid } from 'nanoid'; import { ActionDialog, InfoDialog } from 'vj/components/dialog/index'; import Notification from 'vj/components/notification'; import { AutoloadPage } from 'vj/misc/Page'; -import i18n from 'vj/utils/i18n'; -import pjax from 'vj/utils/pjax'; -import request from 'vj/utils/request'; -import tpl from 'vj/utils/tpl'; +import { + i18n, pjax, request, tpl, +} from 'vj/utils'; async function startEdit(filename, value, fileCategory = 'file') { const { default: Editor } = await import('vj/components/editor/index'); diff --git a/packages/ui-default/components/problem/create.page.js b/packages/ui-default/components/problem/create.page.js index a95415e9..92e7aaa0 100644 --- a/packages/ui-default/components/problem/create.page.js +++ b/packages/ui-default/components/problem/create.page.js @@ -1,8 +1,7 @@ import $ from 'jquery'; import { InfoDialog } from 'vj/components/dialog'; import { NamedPage } from 'vj/misc/Page'; -import i18n from 'vj/utils/i18n'; -import tpl from 'vj/utils/tpl'; +import { i18n, tpl } from 'vj/utils'; export default new NamedPage(['problem_create', 'problem_edit'], () => { $('input[name="pid"]').on('blur', () => { diff --git a/packages/ui-default/components/problemconfig/BasicForm.tsx b/packages/ui-default/components/problemconfig/BasicForm.tsx index c5dbd993..53d2d4ed 100644 --- a/packages/ui-default/components/problemconfig/BasicForm.tsx +++ b/packages/ui-default/components/problemconfig/BasicForm.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { useDispatch, useSelector } from 'react-redux'; -import i18n from 'vj/utils/i18n'; +import { i18n } from 'vj/utils'; import FileSelectAutoComplete from '../autocomplete/components/FileSelectAutoComplete'; import type { RootState } from './reducer/index'; diff --git a/packages/ui-default/components/problemconfig/ProblemConfigForm.tsx b/packages/ui-default/components/problemconfig/ProblemConfigForm.tsx index 33280e56..316bd699 100644 --- a/packages/ui-default/components/problemconfig/ProblemConfigForm.tsx +++ b/packages/ui-default/components/problemconfig/ProblemConfigForm.tsx @@ -3,7 +3,7 @@ import { } from '@blueprintjs/core'; import React from 'react'; import { useDispatch, useSelector } from 'react-redux'; -import i18n from 'vj/utils/i18n'; +import { i18n } from 'vj/utils'; import CustomSelectAutoComplete from '../autocomplete/components/CustomSelectAutoComplete'; import FileSelectAutoComplete from '../autocomplete/components/FileSelectAutoComplete'; import { FormItem } from './BasicForm'; diff --git a/packages/ui-default/components/problemconfig/ProblemType.tsx b/packages/ui-default/components/problemconfig/ProblemType.tsx index 8d2c653b..a89ccbf9 100644 --- a/packages/ui-default/components/problemconfig/ProblemType.tsx +++ b/packages/ui-default/components/problemconfig/ProblemType.tsx @@ -3,7 +3,7 @@ import { } from '@blueprintjs/core'; import React from 'react'; import { useDispatch, useSelector } from 'react-redux'; -import i18n from 'vj/utils/i18n'; +import { i18n } from 'vj/utils'; import { FormItem, ManagedSelect, SingleFileSelect } from './BasicForm'; import type { RootState } from './reducer/index'; diff --git a/packages/ui-default/components/problemconfig/SubtaskTable.tsx b/packages/ui-default/components/problemconfig/SubtaskTable.tsx index 7ee7b635..1a7d49d3 100644 --- a/packages/ui-default/components/problemconfig/SubtaskTable.tsx +++ b/packages/ui-default/components/problemconfig/SubtaskTable.tsx @@ -4,7 +4,7 @@ import type { SubtaskConfig } from 'hydrooj/src/interface'; import { isEqual, pick } from 'lodash'; import React from 'react'; import { useDispatch, useSelector } from 'react-redux'; -import i18n from 'vj/utils/i18n'; +import { i18n } from 'vj/utils'; import CustomSelectAutoComplete from '../autocomplete/components/CustomSelectAutoComplete'; import { FormItem } from './BasicForm'; import { RootState } from './reducer'; diff --git a/packages/ui-default/components/problemconfig/TestCasesTable.tsx b/packages/ui-default/components/problemconfig/TestCasesTable.tsx index ddd40797..253ad6a7 100644 --- a/packages/ui-default/components/problemconfig/TestCasesTable.tsx +++ b/packages/ui-default/components/problemconfig/TestCasesTable.tsx @@ -4,7 +4,7 @@ import type { SubtaskConfig } from 'hydrooj/src/interface'; import { isEqual } from 'lodash'; import React, { useEffect, useRef } from 'react'; import { useDispatch, useSelector } from 'react-redux'; -import i18n from 'vj/utils/i18n'; +import { i18n } from 'vj/utils'; import FileSelectAutoComplete from '../autocomplete/components/FileSelectAutoComplete'; import type { RootState } from './reducer/index'; diff --git a/packages/ui-default/components/rotator/index.js b/packages/ui-default/components/rotator/index.js index 9fa1a7e5..912e610c 100644 --- a/packages/ui-default/components/rotator/index.js +++ b/packages/ui-default/components/rotator/index.js @@ -1,7 +1,7 @@ import $ from 'jquery'; import _ from 'lodash'; import DOMAttachedObject from 'vj/components/DOMAttachedObject'; -import delay from 'vj/utils/delay'; +import { delay } from 'vj/utils'; const ANIMATION_DURATION = 4000; diff --git a/packages/ui-default/components/scratchpad/ScratchpadPretestContainer.jsx b/packages/ui-default/components/scratchpad/ScratchpadPretestContainer.jsx index b2ef89cf..dc36b685 100644 --- a/packages/ui-default/components/scratchpad/ScratchpadPretestContainer.jsx +++ b/packages/ui-default/components/scratchpad/ScratchpadPretestContainer.jsx @@ -2,7 +2,7 @@ import AnsiUp from 'ansi_up'; import React from 'react'; import { connect } from 'react-redux'; import Icon from 'vj/components/react/IconComponent'; -import i18n from 'vj/utils/i18n'; +import { i18n } from 'vj/utils'; import DataInput from './DataInputComponent'; import Panel from './PanelComponent'; diff --git a/packages/ui-default/components/scratchpad/ScratchpadRecordsContainer.jsx b/packages/ui-default/components/scratchpad/ScratchpadRecordsContainer.jsx index 886c6441..1f21cbfb 100644 --- a/packages/ui-default/components/scratchpad/ScratchpadRecordsContainer.jsx +++ b/packages/ui-default/components/scratchpad/ScratchpadRecordsContainer.jsx @@ -2,7 +2,7 @@ import classNames from 'classnames'; import React from 'react'; import { connect } from 'react-redux'; import Icon from 'vj/components/react/IconComponent'; -import i18n from 'vj/utils/i18n'; +import { i18n } from 'vj/utils'; import Panel from './PanelComponent'; import ScratchpadRecordsRow from './ScratchpadRecordsRowContainer'; diff --git a/packages/ui-default/components/scratchpad/ScratchpadRecordsRowContainer.jsx b/packages/ui-default/components/scratchpad/ScratchpadRecordsRowContainer.jsx index af5525dc..425f969d 100644 --- a/packages/ui-default/components/scratchpad/ScratchpadRecordsRowContainer.jsx +++ b/packages/ui-default/components/scratchpad/ScratchpadRecordsRowContainer.jsx @@ -5,10 +5,9 @@ import React from 'react'; import { connect } from 'react-redux'; import TimeAgo from 'timeago-react'; import * as recordEnum from 'vj/constant/record'; -import emulateAnchorClick from 'vj/utils/emulateAnchorClick'; -import i18n from 'vj/utils/i18n'; -import { parse as parseMongoId } from 'vj/utils/mongoId'; -import substitute from 'vj/utils/substitute'; +import { + emulateAnchorClick, i18n, mongoId, substitute, +} from 'vj/utils'; const shouldShowDetail = (data) => recordEnum.STATUS_SCRATCHPAD_SHOW_DETAIL_FLAGS[data.status]; @@ -58,7 +57,7 @@ export default connect(mapStateToProps, null, mergeProps)(class ScratchpadRecord render() { const { data } = this.props; - const submitAt = parseMongoId(data._id).timestamp * 1000; + const submitAt = mongoId(data._id).timestamp * 1000; // Is pretest return data.contest?.toString() === '000000000000000000000000' ? null : ( this.handleRowClick(ev, data._id)}> diff --git a/packages/ui-default/components/scratchpad/ScratchpadToolbarContainer.jsx b/packages/ui-default/components/scratchpad/ScratchpadToolbarContainer.jsx index e774f115..98085c15 100644 --- a/packages/ui-default/components/scratchpad/ScratchpadToolbarContainer.jsx +++ b/packages/ui-default/components/scratchpad/ScratchpadToolbarContainer.jsx @@ -4,9 +4,7 @@ import PropTypes from 'prop-types'; import React from 'react'; import { connect } from 'react-redux'; import Icon from 'vj/components/react/IconComponent'; -import getAvailableLangs from 'vj/utils/availableLangs'; -import i18n from 'vj/utils/i18n'; -import request from 'vj/utils/request'; +import { getAvailableLangs, i18n, request } from 'vj/utils'; import Toolbar, { ToolbarButtonComponent as ToolbarButton, ToolbarItemComponent as ToolbarItem, diff --git a/packages/ui-default/components/scratchpad/reducers/ui.ts b/packages/ui-default/components/scratchpad/reducers/ui.ts index b4943a07..ecd2c8bd 100644 --- a/packages/ui-default/components/scratchpad/reducers/ui.ts +++ b/packages/ui-default/components/scratchpad/reducers/ui.ts @@ -1,5 +1,5 @@ import Notification from 'vj/components/notification'; -import i18n from 'vj/utils/i18n'; +import { i18n } from 'vj/utils'; export default function reducer(state = { main: { diff --git a/packages/ui-default/components/selectUser.ts b/packages/ui-default/components/selectUser.ts index d6e29a16..3aa05a3b 100644 --- a/packages/ui-default/components/selectUser.ts +++ b/packages/ui-default/components/selectUser.ts @@ -1,7 +1,6 @@ import UserSelectAutoComplete from 'vj/components/autocomplete/UserSelectAutoComplete'; import { ActionDialog } from 'vj/components/dialog'; -import i18n from 'vj/utils/i18n'; -import tpl from 'vj/utils/tpl'; +import { i18n, tpl } from 'vj/utils'; import createHint from './hint'; let hintInserted = false; diff --git a/packages/ui-default/components/signin/signInDialog.page.js b/packages/ui-default/components/signin/signInDialog.page.js index 36ddbdc5..911591fc 100644 --- a/packages/ui-default/components/signin/signInDialog.page.js +++ b/packages/ui-default/components/signin/signInDialog.page.js @@ -3,9 +3,7 @@ import responsiveCutoff from 'vj/breakpoints.json'; import DomDialog from 'vj/components/dialog/DomDialog'; import { InfoDialog } from 'vj/components/dialog/index'; import { AutoloadPage } from 'vj/misc/Page'; -import i18n from 'vj/utils/i18n'; -import { isBelow } from 'vj/utils/mediaQuery'; -import tpl from 'vj/utils/tpl'; +import { i18n, mediaQuery, tpl } from 'vj/utils'; const signinDialogPage = new AutoloadPage('signinDialogPage', null, () => { const signInDialog = DomDialog.getOrConstruct($('.dialog--signin'), { @@ -17,7 +15,7 @@ const signinDialogPage = new AutoloadPage('signinDialogPage', null, () => { if ($('[name="nav_login"]').length > 0) { // nav $('[name="nav_login"]').on('click', (ev) => { - if (isBelow(responsiveCutoff.mobile)) return; + if (mediaQuery.isBelow(responsiveCutoff.mobile)) return; if (ev.shiftKey || ev.metaKey || ev.ctrlKey) return; signInDialog.show(); ev.preventDefault(); @@ -32,7 +30,7 @@ const signinDialogPage = new AutoloadPage('signinDialogPage', null, () => { } window.showSignInDialog = () => { - if (isBelow(responsiveCutoff.mobile)) { + if (mediaQuery.isBelow(responsiveCutoff.mobile)) { if ($('[name="nav_login"]').length > 0) { window.location.href = $('[name="nav_login"]').attr('href'); return; diff --git a/packages/ui-default/components/star/star.page.js b/packages/ui-default/components/star/star.page.js index fbb426cd..5fbd6440 100644 --- a/packages/ui-default/components/star/star.page.js +++ b/packages/ui-default/components/star/star.page.js @@ -1,6 +1,6 @@ import $ from 'jquery'; import { AutoloadPage } from 'vj/misc/Page'; -import request from 'vj/utils/request'; +import { request } from 'vj/utils'; function setStarButtonState($starButton, star) { if (star) { diff --git a/packages/ui-default/components/time/time.page.js b/packages/ui-default/components/time/time.page.js index 47b44e92..dcac7329 100644 --- a/packages/ui-default/components/time/time.page.js +++ b/packages/ui-default/components/time/time.page.js @@ -1,7 +1,7 @@ import $ from 'jquery'; import * as timeago from 'timeago.js'; import { AutoloadPage } from 'vj/misc/Page'; -import i18n from 'vj/utils/i18n'; +import { i18n } from 'vj/utils'; try { const locales = require.context('timeago.js/lib/lang', false, /\.js$/); diff --git a/packages/ui-default/components/upload.ts b/packages/ui-default/components/upload.ts index cbc4fc1a..83131f0e 100644 --- a/packages/ui-default/components/upload.ts +++ b/packages/ui-default/components/upload.ts @@ -1,9 +1,8 @@ import { Dialog } from 'vj/components/dialog/index'; import Notification from 'vj/components/notification'; -import delay from 'vj/utils/delay'; -import i18n from 'vj/utils/i18n'; -import pjax from 'vj/utils/pjax'; -import request from 'vj/utils/request'; +import { + delay, i18n, pjax, request, +} from 'vj/utils'; function onBeforeUnload(e) { e.returnValue = ''; diff --git a/packages/ui-default/components/vote/vote.page.js b/packages/ui-default/components/vote/vote.page.js index ddda0ca7..012eeaa9 100644 --- a/packages/ui-default/components/vote/vote.page.js +++ b/packages/ui-default/components/vote/vote.page.js @@ -1,7 +1,7 @@ import $ from 'jquery'; import Rotator from 'vj/components/rotator'; import { AutoloadPage } from 'vj/misc/Page'; -import request from 'vj/utils/request'; +import { request } from 'vj/utils'; function setVoteState($container, value, status) { const $num = $container.find('.vote-number'); diff --git a/packages/ui-default/components/zipDownloader/index.js b/packages/ui-default/components/zipDownloader/index.ts similarity index 93% rename from packages/ui-default/components/zipDownloader/index.js rename to packages/ui-default/components/zipDownloader/index.ts index e75ab9d3..0b7a0a19 100644 --- a/packages/ui-default/components/zipDownloader/index.js +++ b/packages/ui-default/components/zipDownloader/index.ts @@ -2,11 +2,9 @@ import { dump } from 'js-yaml'; import PQueue from 'p-queue'; import streamsaver from 'streamsaver'; import Notification from 'vj/components/notification'; -import api, { gql } from 'vj/utils/api'; -import i18n from 'vj/utils/i18n'; -import pipeStream from 'vj/utils/pipeStream'; -import request from 'vj/utils/request'; -import { createZipStream } from 'vj/utils/zip'; +import { + api, createZipStream, gql, i18n, pipeStream, request, +} from 'vj/utils'; let isBeforeUnloadTriggeredByLibrary = !window.isSecureContext; function onBeforeUnload(e) { @@ -29,8 +27,8 @@ export default async function download(filename, targets) { await waitForWritableStream; const fileStream = streamsaver.createWriteStream(filename); const queue = new PQueue({ concurrency: 5 }); - const abortCallbackReceiver = {}; - function stopDownload() { abortCallbackReceiver.abort(); } + const abortCallbackReceiver: any = {}; + function stopDownload() { abortCallbackReceiver.abort?.(); } let i = 0; async function downloadFile(target) { try { diff --git a/packages/ui-default/entry.js b/packages/ui-default/entry.js index 60ec942b..6c05947f 100644 --- a/packages/ui-default/entry.js +++ b/packages/ui-default/entry.js @@ -46,10 +46,11 @@ document.addEventListener('DOMContentLoaded', async () => { // eslint-disable-next-line camelcase try { __webpack_public_path__ = UiContext.cdn_prefix; } catch (e) { } - const [data] = await Promise.all([ - (await fetch(`/constant/${UiContext.constantVersion}`, { cache: 'force-cache' })).json(), - await import('./modules'), + const [data, HydroExports] = await Promise.all([ + fetch(`/constant/${UiContext.constantVersion}`, { cache: 'force-cache' }).then((r) => r.json()), + import('./api'), ]); + Object.assign(window, { HydroExports }); eval(data[0]); // eslint-disable-line no-eval data.shift(); window.Hydro.preload = data; diff --git a/packages/ui-default/hydro.ts b/packages/ui-default/hydro.ts index 2dceb8c2..ca0eddd8 100644 --- a/packages/ui-default/hydro.ts +++ b/packages/ui-default/hydro.ts @@ -4,16 +4,15 @@ import $ from 'jquery'; import _ from 'lodash'; import Notification from 'vj/components/notification'; import PageLoader from 'vj/misc/PageLoader'; -import delay from 'vj/utils/delay'; +import { delay } from 'vj/utils'; declare global { interface Window { UserContext: any; UiContext: any; Hydro: any; - // eslint-disable-next-line camelcase - node_modules: any; externalModules: Record; + captureException?: (e: Error) => void; } } diff --git a/packages/ui-default/index.ts b/packages/ui-default/index.ts index b14d4d9f..30ba83b4 100644 --- a/packages/ui-default/index.ts +++ b/packages/ui-default/index.ts @@ -51,7 +51,22 @@ export async function buildUI() { target: [ 'chrome60', ], - plugins: global.Hydro.ui.esbuildPlugins || [], + plugins: [ + ...(global.Hydro.ui.esbuildPlugins || []), + { + name: 'federation', + setup(b) { + b.onResolve({ filter: /^@hydrooj\/ui-default/ }, () => ({ + path: 'api', + namespace: 'ui-default', + })); + b.onLoad({ filter: /.*/, namespace: 'ui-default' }, () => ({ + contents: 'module.exports = window.HydroExports;', + loader: 'tsx', + })); + }, + }, + ], minify: !process.env.DEV, }); if (build.errors.length) console.error(build.errors); diff --git a/packages/ui-default/modules.js b/packages/ui-default/modules.js deleted file mode 100644 index cd77ea05..00000000 --- a/packages/ui-default/modules.js +++ /dev/null @@ -1,37 +0,0 @@ -import './utils/delay'; -import './utils/emulateAnchorClick'; -import './utils/i18n'; -import './utils/loadReactRedux'; -import './utils/mediaQuery'; -import './utils/mongoId'; -import './utils/pipeStream'; -import './utils/pjax'; -import './utils/request'; -import './utils/slide'; -import './utils/substitute'; -import './utils/tpl'; -import './utils/zIndexManager'; -import './utils/zip'; -import './components/autocomplete'; -import './components/dialog'; -import './components/notification'; -import './components/monaco/loader'; - -import $ from 'jquery'; -import _ from 'lodash'; -import React from 'react'; -import ReactDOM from 'react-dom/client'; -import * as redux from 'react-redux'; - -const modules = { - _, $, React, redux, ReactDOM, -}; -export default async function load(name) { - if (modules[name]) return modules[name]; - if (name === 'echarts') return import('echarts'); - if (name === 'moment') return import('moment'); - throw new Error(`Module ${name} not found`); -} -window.node_modules = { ...modules, load }; -window.$ = $; -window.jQuery = $; diff --git a/packages/ui-default/package.json b/packages/ui-default/package.json index 25b2c210..495eb219 100644 --- a/packages/ui-default/package.json +++ b/packages/ui-default/package.json @@ -4,6 +4,7 @@ "author": "undefined ", "license": "AGPL-3.0", "main": "index.ts", + "types": "api.ts", "repository": "https://github.com/hydro-dev/Hydro.git", "preferUnplugged": true, "scripts": { @@ -29,6 +30,7 @@ "@types/markdown-it": "^12.2.3", "@types/pickadate": "^3.5.32", "@types/qrcode": "^1.5.0", + "@types/react": "^18.0.26", "@types/react-dom": "^18.0.9", "@types/redux-logger": "^3.0.9", "@types/serviceworker": "^0.0.57", diff --git a/packages/ui-default/pages/api.page.tsx b/packages/ui-default/pages/api.page.tsx index 9b7355a5..e1144dc9 100644 --- a/packages/ui-default/pages/api.page.tsx +++ b/packages/ui-default/pages/api.page.tsx @@ -2,7 +2,7 @@ import { pick } from 'lodash'; import React from 'react'; import { createRoot } from 'react-dom/client'; import { NamedPage } from 'vj/misc/Page'; -import request from 'vj/utils/request'; +import { request } from 'vj/utils'; const defaultQuery = `\ query Example( diff --git a/packages/ui-default/pages/contest.page.ts b/packages/ui-default/pages/contest.page.ts index 08910339..5a3cbaf9 100644 --- a/packages/ui-default/pages/contest.page.ts +++ b/packages/ui-default/pages/contest.page.ts @@ -1,7 +1,7 @@ import { formatSeconds } from '@hydrooj/utils/lib/common'; import NProgress from 'nprogress'; import { NamedPage } from 'vj/misc/Page'; -import tpl from 'vj/utils/tpl'; +import { tpl } from 'vj/utils'; const contestTimer = $(tpl``); contestTimer.appendTo(document.body); diff --git a/packages/ui-default/pages/contest_edit.page.ts b/packages/ui-default/pages/contest_edit.page.ts index 0efd92fa..89b37de4 100644 --- a/packages/ui-default/pages/contest_edit.page.ts +++ b/packages/ui-default/pages/contest_edit.page.ts @@ -2,9 +2,7 @@ import $ from 'jquery'; import ProblemSelectAutoComplete from 'vj/components/autocomplete/ProblemSelectAutoComplete'; import { ConfirmDialog } from 'vj/components/dialog'; import { NamedPage } from 'vj/misc/Page'; -import i18n from 'vj/utils/i18n'; -import request from 'vj/utils/request'; -import tpl from 'vj/utils/tpl'; +import { i18n, request, tpl } from 'vj/utils'; const page = new NamedPage(['contest_edit', 'contest_create', 'homework_create', 'homework_edit'], (pagename) => { ProblemSelectAutoComplete.getOrConstruct($('[name="pids"]'), { multi: true, clearDefaultValue: false }); diff --git a/packages/ui-default/pages/domain_group.page.ts b/packages/ui-default/pages/domain_group.page.ts index 9f58cc27..0269d00a 100644 --- a/packages/ui-default/pages/domain_group.page.ts +++ b/packages/ui-default/pages/domain_group.page.ts @@ -4,10 +4,9 @@ import UserSelectAutoComplete from 'vj/components/autocomplete/UserSelectAutoCom import { ActionDialog, ConfirmDialog } from 'vj/components/dialog'; import Notification from 'vj/components/notification'; import { NamedPage } from 'vj/misc/Page'; -import api, { gql } from 'vj/utils/api'; -import delay from 'vj/utils/delay'; -import i18n from 'vj/utils/i18n'; -import tpl from 'vj/utils/tpl'; +import { + api, delay, gql, i18n, tpl, +} from 'vj/utils'; function update(name: string, uids: number[]) { return api(gql` diff --git a/packages/ui-default/pages/domain_role.page.js b/packages/ui-default/pages/domain_role.page.js index 2f2a7d6d..27798bae 100644 --- a/packages/ui-default/pages/domain_role.page.js +++ b/packages/ui-default/pages/domain_role.page.js @@ -3,10 +3,9 @@ import _ from 'lodash'; import { ActionDialog, ConfirmDialog } from 'vj/components/dialog'; import Notification from 'vj/components/notification'; import { NamedPage } from 'vj/misc/Page'; -import delay from 'vj/utils/delay'; -import i18n from 'vj/utils/i18n'; -import request from 'vj/utils/request'; -import tpl from 'vj/utils/tpl'; +import { + delay, i18n, request, tpl, +} from 'vj/utils'; const page = new NamedPage('domain_role', () => { const createRoleDialog = new ActionDialog({ diff --git a/packages/ui-default/pages/domain_user.page.js b/packages/ui-default/pages/domain_user.page.js index d4dad3d8..e19aa129 100644 --- a/packages/ui-default/pages/domain_user.page.js +++ b/packages/ui-default/pages/domain_user.page.js @@ -4,10 +4,9 @@ import UserSelectAutoComplete from 'vj/components/autocomplete/UserSelectAutoCom import { ActionDialog, ConfirmDialog } from 'vj/components/dialog'; import Notification from 'vj/components/notification'; import { NamedPage } from 'vj/misc/Page'; -import delay from 'vj/utils/delay'; -import i18n from 'vj/utils/i18n'; -import request from 'vj/utils/request'; -import tpl from 'vj/utils/tpl'; +import { + delay, i18n, request, tpl, +} from 'vj/utils'; const page = new NamedPage('domain_user', () => { const addUserSelector = UserSelectAutoComplete.getOrConstruct($('.dialog__body--add-user [name="user"]')); diff --git a/packages/ui-default/pages/files.page.js b/packages/ui-default/pages/files.page.js index dc466b96..dd16ab05 100644 --- a/packages/ui-default/pages/files.page.js +++ b/packages/ui-default/pages/files.page.js @@ -4,10 +4,9 @@ import { ConfirmDialog } from 'vj/components/dialog/index'; import Notification from 'vj/components/notification'; import uploadFiles from 'vj/components/upload'; import { NamedPage } from 'vj/misc/Page'; -import i18n from 'vj/utils/i18n'; -import pjax from 'vj/utils/pjax'; -import request from 'vj/utils/request'; -import tpl from 'vj/utils/tpl'; +import { + i18n, pjax, request, tpl, +} from 'vj/utils'; function ensureAndGetSelectedFiles() { const files = _.map( diff --git a/packages/ui-default/pages/home_messages.page.jsx b/packages/ui-default/pages/home_messages.page.jsx index b14edd17..04cd6e3d 100644 --- a/packages/ui-default/pages/home_messages.page.jsx +++ b/packages/ui-default/pages/home_messages.page.jsx @@ -4,8 +4,7 @@ import { createRoot } from 'react-dom/client'; import VjNotification from 'vj/components/notification'; import selectUser from 'vj/components/selectUser'; import { NamedPage } from 'vj/misc/Page'; -import api, { gql } from 'vj/utils/api'; -import loadReactRedux from 'vj/utils/loadReactRedux'; +import { api, gql, loadReactRedux } from 'vj/utils'; const page = new NamedPage('home_messages', () => { let reduxStore; diff --git a/packages/ui-default/pages/home_preference.page.jsx b/packages/ui-default/pages/home_preference.page.jsx index 0fb57776..d9a6f410 100644 --- a/packages/ui-default/pages/home_preference.page.jsx +++ b/packages/ui-default/pages/home_preference.page.jsx @@ -1,10 +1,9 @@ import $ from 'jquery'; import { renderLanguageSelect } from 'vj/components/languageselect'; import { NamedPage } from 'vj/misc/Page'; -import getAvailableLangs from 'vj/utils/availableLangs'; -import delay from 'vj/utils/delay'; -import i18n from 'vj/utils/i18n'; -import tpl from 'vj/utils/tpl'; +import { + delay, getAvailableLangs, i18n, tpl, +} from 'vj/utils'; async function initCodeLangHelper() { const $el = $(tpl`
`); diff --git a/packages/ui-default/pages/home_security.page.ts b/packages/ui-default/pages/home_security.page.ts index 8d649f1d..8b83b61d 100644 --- a/packages/ui-default/pages/home_security.page.ts +++ b/packages/ui-default/pages/home_security.page.ts @@ -3,10 +3,9 @@ import QRCode from 'qrcode'; import { ActionDialog } from 'vj/components/dialog'; import Notification from 'vj/components/notification'; import { NamedPage } from 'vj/misc/Page'; -import api, { gql } from 'vj/utils/api'; -import delay from 'vj/utils/delay'; -import i18n from 'vj/utils/i18n'; -import tpl from 'vj/utils/tpl'; +import { + api, delay, gql, i18n, tpl, +} from 'vj/utils'; export default new NamedPage('home_security', () => { $(document).on('click', '[name="tfa_enable"]', async () => { diff --git a/packages/ui-default/pages/homework_main.page.js b/packages/ui-default/pages/homework_main.page.js index 1ee7d763..6399a280 100644 --- a/packages/ui-default/pages/homework_main.page.js +++ b/packages/ui-default/pages/homework_main.page.js @@ -1,7 +1,6 @@ import $ from 'jquery'; import { NamedPage } from 'vj/misc/Page'; -import i18n from 'vj/utils/i18n'; -import { parse as parseMongoId } from 'vj/utils/mongoId'; +import { i18n, mongoId } from 'vj/utils'; const page = new NamedPage('homework_main', async () => { // Homework Calendar @@ -13,7 +12,7 @@ const page = new NamedPage('homework_main', async () => { title: doc.title, maskFrom: doc.penaltySince ? doc.penaltySince : null, maskTitle: i18n('Time Extension'), - colorIndex: parseMongoId(doc._id).timestamp % 12, + colorIndex: mongoId(doc._id).timestamp % 12, link: doc.url, })); const calendar = new Calendar(events); diff --git a/packages/ui-default/pages/manage_script.page.js b/packages/ui-default/pages/manage_script.page.js index a36ef089..935229ab 100644 --- a/packages/ui-default/pages/manage_script.page.js +++ b/packages/ui-default/pages/manage_script.page.js @@ -2,7 +2,7 @@ import $ from 'jquery'; import { ActionDialog } from 'vj/components/dialog'; import Notification from 'vj/components/notification'; import { NamedPage } from 'vj/misc/Page'; -import request from 'vj/utils/request'; +import { request } from 'vj/utils'; const page = new NamedPage('manage_script', () => { const runScriptDialog = new ActionDialog({ diff --git a/packages/ui-default/pages/manage_user_import.page.js b/packages/ui-default/pages/manage_user_import.page.js index 38148f52..70f3ff8b 100644 --- a/packages/ui-default/pages/manage_user_import.page.js +++ b/packages/ui-default/pages/manage_user_import.page.js @@ -1,9 +1,7 @@ import $ from 'jquery'; import Notification from 'vj/components/notification'; import { NamedPage } from 'vj/misc/Page'; -import delay from 'vj/utils/delay'; -import i18n from 'vj/utils/i18n'; -import request from 'vj/utils/request'; +import { delay, i18n, request } from 'vj/utils'; const page = new NamedPage('manage_user_import', () => { async function post(draft) { diff --git a/packages/ui-default/pages/manage_user_priv.page.js b/packages/ui-default/pages/manage_user_priv.page.js index b64a6550..864c097c 100644 --- a/packages/ui-default/pages/manage_user_priv.page.js +++ b/packages/ui-default/pages/manage_user_priv.page.js @@ -3,9 +3,7 @@ import { ActionDialog } from 'vj/components/dialog'; import Notification from 'vj/components/notification'; import selectUser from 'vj/components/selectUser'; import { NamedPage } from 'vj/misc/Page'; -import i18n from 'vj/utils/i18n'; -import pjax from 'vj/utils/pjax'; -import request from 'vj/utils/request'; +import { i18n, pjax, request } from 'vj/utils'; const page = new NamedPage('manage_user_priv', () => { const setPrivDialog = new ActionDialog({ diff --git a/packages/ui-default/pages/problem_config.page.tsx b/packages/ui-default/pages/problem_config.page.tsx index 5abea345..525adc5c 100644 --- a/packages/ui-default/pages/problem_config.page.tsx +++ b/packages/ui-default/pages/problem_config.page.tsx @@ -10,10 +10,9 @@ import { configYamlFormat } from 'vj/components/problemconfig/ProblemConfigEdito import uploadFiles from 'vj/components/upload'; import download from 'vj/components/zipDownloader'; import { NamedPage } from 'vj/misc/Page'; -import i18n from 'vj/utils/i18n'; -import loadReactRedux from 'vj/utils/loadReactRedux'; -import request from 'vj/utils/request'; -import tpl from 'vj/utils/tpl'; +import { + i18n, loadReactRedux, request, tpl, +} from 'vj/utils'; const page = new NamedPage('problem_config', () => { let reduxStore; @@ -72,7 +71,7 @@ const page = new NamedPage('problem_config', () => { async function handleClickDownloadAll() { const files = reduxStore.getState().testdata.map((i) => i.name); const { links, pdoc } = await request.post('./files', { operation: 'get_links', files, type: 'testdata' }); - const targets = []; + const targets: { filename: string, url: string }[] = []; for (const filename of Object.keys(links)) targets.push({ filename, url: links[filename] }); await download(`${pdoc.docId} ${pdoc.title}.zip`, targets); } @@ -103,7 +102,7 @@ const page = new NamedPage('problem_config', () => { store.dispatch({ type: 'CONFIG_LOAD', - payload: request.get(), + payload: request.get(''), }); const unsubscribe = store.subscribe(() => { // TODO set yaml schema @@ -124,7 +123,7 @@ const page = new NamedPage('problem_config', () => { subtasks: normalizeSubtasks(subtasks, (i) => i, state.config.time, state.config.memory, true), }); }); - createRoot($('#ProblemConfig').get(0)).render( + createRoot(document.getElementById('ProblemConfig')!).render(
diff --git a/packages/ui-default/pages/problem_detail.page.jsx b/packages/ui-default/pages/problem_detail.page.jsx index 5c02838d..1ad2cb1e 100644 --- a/packages/ui-default/pages/problem_detail.page.jsx +++ b/packages/ui-default/pages/problem_detail.page.jsx @@ -6,12 +6,9 @@ import { createRoot } from 'react-dom/client'; import Notification from 'vj/components/notification'; import { downloadProblemSet } from 'vj/components/zipDownloader'; import { NamedPage } from 'vj/misc/Page'; -import delay from 'vj/utils/delay'; -import i18n from 'vj/utils/i18n'; -import loadReactRedux from 'vj/utils/loadReactRedux'; -import pjax from 'vj/utils/pjax'; -import request from 'vj/utils/request'; -import tpl from 'vj/utils/tpl'; +import { + delay, i18n, loadReactRedux, pjax, request, tpl, +} from 'vj/utils'; class ProblemPageExtender { constructor() { diff --git a/packages/ui-default/pages/problem_edit.page.js b/packages/ui-default/pages/problem_edit.page.js index 3200f262..7ba89677 100644 --- a/packages/ui-default/pages/problem_edit.page.js +++ b/packages/ui-default/pages/problem_edit.page.js @@ -8,10 +8,9 @@ import Notification from 'vj/components/notification'; import uploadFiles from 'vj/components/upload'; import download from 'vj/components/zipDownloader'; import { NamedPage } from 'vj/misc/Page'; -import i18n from 'vj/utils/i18n'; -import request from 'vj/utils/request'; -import { slideDown, slideUp } from 'vj/utils/slide'; -import tpl from 'vj/utils/tpl'; +import { + i18n, request, slideDown, slideUp, tpl, +} from 'vj/utils'; const categories = {}; const dirtyCategories = []; diff --git a/packages/ui-default/pages/problem_files.page.js b/packages/ui-default/pages/problem_files.page.js index 89130fd2..d8613dad 100644 --- a/packages/ui-default/pages/problem_files.page.js +++ b/packages/ui-default/pages/problem_files.page.js @@ -7,10 +7,9 @@ import { previewFile } from 'vj/components/preview/preview.page'; import uploadFiles from 'vj/components/upload'; import download from 'vj/components/zipDownloader'; import { NamedPage } from 'vj/misc/Page'; -import i18n from 'vj/utils/i18n'; -import pjax from 'vj/utils/pjax'; -import request from 'vj/utils/request'; -import tpl from 'vj/utils/tpl'; +import { + i18n, pjax, request, tpl, +} from 'vj/utils'; async function downloadProblemFilesAsArchive(type, files) { const { links, pdoc } = await request.post('', { operation: 'get_links', files, type }); diff --git a/packages/ui-default/pages/problem_main.page.js b/packages/ui-default/pages/problem_main.page.js index 27bf0bbb..4e75046a 100644 --- a/packages/ui-default/pages/problem_main.page.js +++ b/packages/ui-default/pages/problem_main.page.js @@ -6,11 +6,9 @@ import createHint from 'vj/components/hint'; import Notification from 'vj/components/notification'; import { downloadProblemSet } from 'vj/components/zipDownloader'; import { NamedPage } from 'vj/misc/Page'; -import delay from 'vj/utils/delay'; -import i18n from 'vj/utils/i18n'; -import pjax from 'vj/utils/pjax'; -import request from 'vj/utils/request'; -import tpl from 'vj/utils/tpl'; +import { + delay, i18n, pjax, request, tpl, +} from 'vj/utils'; const categories = {}; const dirtyCategories = []; diff --git a/packages/ui-default/pages/problem_sidebar.page.ts b/packages/ui-default/pages/problem_sidebar.page.ts index c5f2e2a3..35021af3 100644 --- a/packages/ui-default/pages/problem_sidebar.page.ts +++ b/packages/ui-default/pages/problem_sidebar.page.ts @@ -2,9 +2,7 @@ import $ from 'jquery'; import { ActionDialog, ConfirmDialog } from 'vj/components/dialog'; import Notification from 'vj/components/notification'; import { NamedPage } from 'vj/misc/Page'; -import i18n from 'vj/utils/i18n'; -import request from 'vj/utils/request'; -import tpl from 'vj/utils/tpl'; +import { i18n, request, tpl } from 'vj/utils'; const page = new NamedPage([ 'problem_create', 'problem_edit', 'problem_solution', 'problem_submit', diff --git a/packages/ui-default/pages/problem_submit.page.tsx b/packages/ui-default/pages/problem_submit.page.tsx index 8e9ce821..a0068fb4 100644 --- a/packages/ui-default/pages/problem_submit.page.tsx +++ b/packages/ui-default/pages/problem_submit.page.tsx @@ -1,7 +1,7 @@ import $ from 'jquery'; import { renderLanguageSelect } from 'vj/components/languageselect'; import { NamedPage } from 'vj/misc/Page'; -import getAvailableLangs from 'vj/utils/availableLangs'; +import { getAvailableLangs } from 'vj/utils'; const page = new NamedPage(['problem_submit', 'contest_detail_problem_submit', 'homework_detail_problem_submit'], async () => { const { config } = UiContext.pdoc; diff --git a/packages/ui-default/pages/record_main.page.ts b/packages/ui-default/pages/record_main.page.ts index 635d8f2d..ea7fc0d5 100644 --- a/packages/ui-default/pages/record_main.page.ts +++ b/packages/ui-default/pages/record_main.page.ts @@ -2,8 +2,7 @@ import $ from 'jquery'; import ProblemSelectAutoComplete from 'vj/components/autocomplete/ProblemSelectAutoComplete'; import UserSelectAutoComplete from 'vj/components/autocomplete/UserSelectAutoComplete'; import { NamedPage } from 'vj/misc/Page'; -import getAvailableLangs from 'vj/utils/availableLangs'; -import tpl from 'vj/utils/tpl'; +import { getAvailableLangs, tpl } from 'vj/utils'; const page = new NamedPage('record_main', async () => { const [{ default: WebSocket }, { DiffDOM }] = await Promise.all([ diff --git a/packages/ui-default/pages/setting.page.tsx b/packages/ui-default/pages/setting.page.tsx index b53d1ac6..73a9c285 100644 --- a/packages/ui-default/pages/setting.page.tsx +++ b/packages/ui-default/pages/setting.page.tsx @@ -2,7 +2,7 @@ import yaml from 'js-yaml'; import Schema from 'schemastery'; import Notification from 'vj/components/notification'; import { NamedPage } from 'vj/misc/Page'; -import request from 'vj/utils/request'; +import { request } from 'vj/utils'; const page = new NamedPage('manage_config', async () => { const schema = new Schema(UiContext.schema); diff --git a/packages/ui-default/pages/training_edit.page.ts b/packages/ui-default/pages/training_edit.page.ts index 777d5bf1..8610a753 100644 --- a/packages/ui-default/pages/training_edit.page.ts +++ b/packages/ui-default/pages/training_edit.page.ts @@ -1,9 +1,7 @@ import $ from 'jquery'; import { ConfirmDialog } from 'vj/components/dialog'; import { NamedPage } from 'vj/misc/Page'; -import i18n from 'vj/utils/i18n'; -import request from 'vj/utils/request'; -import tpl from 'vj/utils/tpl'; +import { i18n, request, tpl } from 'vj/utils'; export default new NamedPage('training_edit', () => { let confirmed = false; diff --git a/packages/ui-default/pages/user_login.page.ts b/packages/ui-default/pages/user_login.page.ts index 20e1b064..f16ef441 100644 --- a/packages/ui-default/pages/user_login.page.ts +++ b/packages/ui-default/pages/user_login.page.ts @@ -1,6 +1,6 @@ import $ from 'jquery'; import { AutoloadPage } from 'vj/misc/Page'; -import api, { gql } from 'vj/utils/api'; +import { api, gql } from 'vj/utils'; export default new AutoloadPage('user_login', (pagename) => { (pagename === 'user_login' ? $(document) : $('.dialog--signin__main')).on('blur', '[name="uname"]', async () => { diff --git a/packages/ui-default/types.ts b/packages/ui-default/types.ts deleted file mode 100644 index c6859648..00000000 --- a/packages/ui-default/types.ts +++ /dev/null @@ -1,11 +0,0 @@ -declare global { - interface Window { - node_modules: any; - LANGS: Record; - } - - let UserContext: Record; - let UiContext: Record; -} - -export { }; diff --git a/packages/ui-default/utils/api.ts b/packages/ui-default/utils/api.ts deleted file mode 100644 index cebf72b0..00000000 --- a/packages/ui-default/utils/api.ts +++ /dev/null @@ -1,26 +0,0 @@ -import request from './request'; - -export default async (q: string, path: string[] = []) => { - let query = q.trim(); - if (!query.startsWith('query')) query = `query{${query}}`; - const res = await request.post(`/d/${UiContext.domainId}/api`, { query }); - if (res.errors) throw new Error(res.errors[0].message); - let cursor = res; - for (const p of path) { - cursor = cursor[p]; - if (!cursor) return undefined; - } - return cursor; -}; - -export const gql = ( - pieces: TemplateStringsArray, - ...templates: (string | number | string[] | number[])[] -) => { - let res = ''; - for (let i = 0; i < pieces.length; i++) { - res += pieces[i]; - if (templates[i]) res += JSON.stringify(templates[i]); - } - return res; -}; diff --git a/packages/ui-default/utils/availableLangs.ts b/packages/ui-default/utils/availableLangs.ts deleted file mode 100644 index bff34101..00000000 --- a/packages/ui-default/utils/availableLangs.ts +++ /dev/null @@ -1,12 +0,0 @@ -const prefixes = new Set(Object.keys(window.LANGS).filter((i) => i.includes('.')).map((i) => i.split('.')[0])); - -export default function getAvailableLangs(langsList?: string[]) { - const Langs = {}; - for (const key in window.LANGS) { - if (prefixes.has(key)) continue; - if (langsList && langsList.length && langsList.join('') && !langsList.includes(key)) continue; - else if (window.LANGS[key].hidden && !langsList?.includes(key)) continue; - Langs[key] = window.LANGS[key]; - } - return Langs; -} diff --git a/packages/ui-default/utils/base.ts b/packages/ui-default/utils/base.ts new file mode 100644 index 00000000..f0545b74 --- /dev/null +++ b/packages/ui-default/utils/base.ts @@ -0,0 +1,153 @@ +import $ from 'jquery'; +import _ from 'lodash'; + +export function substitute(str: string, obj: any) { + return str.replace(/\{([^{}]+)\}/g, (match, key) => { + if (obj[key] !== undefined) return obj[key].toString(); + return `{${key}}`; + }); +} + +export function i18n(str: string, ...params: any[]) { + if (!str) return ''; + return substitute((window as any).LOCALES?.[str] || str, params); +} + +export function delay(ms) { + return new Promise((resolve) => { setTimeout(resolve, ms); }); +} + +type Substitution = string | number | { templateRaw: true, html: string }; + +export function tpl(pieces: TemplateStringsArray, ...substitutions: Substitution[]) { + let result = pieces[0]; + for (let i = 0; i < substitutions.length; ++i) { + const subst = substitutions[i]; + let substHtml: string; + if (typeof subst === 'object' && subst.templateRaw) { + substHtml = subst.html; + } else substHtml = _.escape(String(subst)); + result += substHtml + pieces[i + 1]; + } + return result; +} + +tpl.typoMsg = function (msg: string, raw = false) { + if (raw) return `

${msg}

`; + const lines = msg.trim().split('\n'); + return `
${lines.map((i) => `

${_.escape(i)}

`).join('\n')}
`; +}; + +export function rawHtml(html: string) { + return { + templateRaw: true, + html, + }; +} + +let zIndexCurrent = 1000; + +export const zIndexManager = { + getCurrent() { + return zIndexCurrent; + }, + getNext() { + return ++zIndexCurrent; + }, +}; + +export const request = { + async ajax(options: Record) { + return new Promise((resolve, reject) => { + $ + .ajax({ + dataType: 'json', + headers: { + Accept: 'application/json', + }, + ...options, + }) + .fail((jqXHR, textStatus, errorThrown: any) => { + if (textStatus === 'abort') { + const err = new Error(i18n('Aborted')) as any; + err.aborted = true; + reject(err); + } else if (jqXHR.readyState === 0) { + reject(new Error(i18n('Network error'))); + } else if (typeof jqXHR.responseJSON === 'object' && jqXHR.responseJSON.error) { + const { error } = jqXHR.responseJSON; + if (error.params) { + const message = i18n(error.message, ...error.params); + const err = new Error(message === error.message && error.params.length + ? `${error.message}: ${error.params.join(' ')}` + : message) as any; + err.rawMessage = error.message; + err.params = error.params; + reject(err); + } else reject(new Error(jqXHR.responseJSON.error.message)); + } else if (errorThrown instanceof Error) { + reject(errorThrown); + } else { + reject(new Error(textStatus)); + } + }) + .done(resolve); + }); + }, + + postFile(url: string, form: FormData, options: any = {}) { + return this.ajax({ + url, + data: form, + processData: false, + contentType: false, + type: 'POST', + dataType: undefined, + ...options, + }); + }, + + post(url: string, dataOrForm: JQueryStatic | Node | string | Record = {}, options: any = {}) { + let postData; + // @ts-ignore + if (dataOrForm instanceof $ && dataOrForm.is('form')) { + // $form + postData = (dataOrForm as any).serialize(); + } else if (dataOrForm instanceof Node && $(dataOrForm).is('form')) { + // form + postData = $(dataOrForm).serialize(); + } else if (typeof dataOrForm === 'string') { + // foo=bar&box=boz + postData = dataOrForm; + } else { + // {foo: 'bar'} + postData = JSON.stringify(dataOrForm); + options.contentType = 'application/json'; + } + return request.ajax({ + url, + method: 'post', + data: postData, + ...options, + }); + }, + + get(url: string, qs: Record = {}, options: Record = {}) { + return request.ajax({ + url, + data: qs, + method: 'get', + ...options, + }); + }, +}; + +Object.assign(window.Hydro.utils, { + i18n, + rawHtml, + substitute, + request, + tpl, + delay, + zIndexManager, +}); diff --git a/packages/ui-default/utils/delay.js b/packages/ui-default/utils/delay.js deleted file mode 100644 index 5d4474a3..00000000 --- a/packages/ui-default/utils/delay.js +++ /dev/null @@ -1,5 +0,0 @@ -export default function delay(ms) { - return new Promise((resolve) => { setTimeout(resolve, ms); }); -} - -window.Hydro.utils.delay = delay; diff --git a/packages/ui-default/utils/emulateAnchorClick.js b/packages/ui-default/utils/emulateAnchorClick.js deleted file mode 100644 index d40d94ca..00000000 --- a/packages/ui-default/utils/emulateAnchorClick.js +++ /dev/null @@ -1,20 +0,0 @@ -/** - * @param {Event} ev - * @param {String} targetUrl - * @param {Boolean} alwaysOpenInNewWindow - */ -export default function emulateAnchorClick(ev, targetUrl, alwaysOpenInNewWindow = false) { - let openInNewWindow; - if (alwaysOpenInNewWindow) { - openInNewWindow = true; - } else { - openInNewWindow = (ev.ctrlKey || ev.shiftKey || ev.metaKey); - } - if (openInNewWindow) { - window.open(targetUrl); - } else { - window.location.href = targetUrl; - } -} - -window.Hydro.utils.emulateAnchorClick = emulateAnchorClick; diff --git a/packages/ui-default/utils/i18n.js b/packages/ui-default/utils/i18n.js deleted file mode 100644 index 7cb5aab1..00000000 --- a/packages/ui-default/utils/i18n.js +++ /dev/null @@ -1,8 +0,0 @@ -import substitute from './substitute'; - -export default function i18n(str, ...params) { - if (!str) return ''; - return substitute(window.LOCALES?.[str] || str, params); -} - -window.Hydro.utils.i18n = i18n; diff --git a/packages/ui-default/utils/index.ts b/packages/ui-default/utils/index.ts new file mode 100644 index 00000000..4a9b997f --- /dev/null +++ b/packages/ui-default/utils/index.ts @@ -0,0 +1,118 @@ +import 'streamsaver/examples/zip-stream'; + +import { request } from './base'; + +export async function api(q: string, path: string[] = []) { + let query = q.trim(); + if (!query.startsWith('query')) query = `query{${query}}`; + const res = await request.post(`/d/${UiContext.domainId}/api`, { query }); + if (res.errors) throw new Error(res.errors[0].message); + let cursor = res; + for (const p of path) { + cursor = cursor[p]; + if (!cursor) return undefined; + } + return cursor; +} + +export const gql = ( + pieces: TemplateStringsArray, + ...templates: (string | number | string[] | number[])[] +) => { + let res = ''; + for (let i = 0; i < pieces.length; i++) { + res += pieces[i]; + if (templates[i]) res += JSON.stringify(templates[i]); + } + return res; +}; + +export function getAvailableLangs(langsList?: string[]) { + const prefixes = new Set(Object.keys(window.LANGS).filter((i) => i.includes('.')).map((i) => i.split('.')[0])); + const Langs = {}; + for (const key in window.LANGS) { + if (prefixes.has(key)) continue; + if (langsList && langsList.length && langsList.join('') && !langsList.includes(key)) continue; + else if (window.LANGS[key].hidden && !langsList?.includes(key)) continue; + Langs[key] = window.LANGS[key]; + } + return Langs; +} + +export const createZipStream = (window as any).ZIP; + +export function createZipBlob(underlyingSource) { + return new Response(createZipStream(underlyingSource)).blob(); +} + +export async function pipeStream(read, write, abort) { + if (window.WritableStream && read.pipeTo) { + const abortController = new AbortController(); + if (abort) abort.abort = abortController.abort.bind(abortController); + await read.pipeTo(write, abortController); + } else { + const writer = write.getWriter(); + if (abort) abort.abort = writer.abort.bind(writer); + const reader = read.getReader(); + // eslint-disable-next-line no-constant-condition + while (1) { + const readResult = await reader.read(); + if (readResult.done) { + writer.close(); + break; + } else writer.write(readResult.value); + } + } +} + +// https://github.com/andrasq/node-mongoid-js/blob/master/mongoid.js +/* eslint-disable */ +export function mongoId(idstring: string) { + if (typeof idstring !== 'string') idstring = String(idstring); + return { + timestamp: parseInt(idstring.slice(0, 0 + 8), 16), + machineid: parseInt(idstring.slice(8, 8 + 6), 16), + pid: parseInt(idstring.slice(14, 14 + 4), 16), + sequence: parseInt(idstring.slice(18, 18 + 6), 16), + }; +} +/* eslint-enable */ + +const loaded = {}; + +export async function loadExternalModule(target: string) { + if (loaded[target]) return loaded[target]; + const ele = document.createElement('script'); + ele.src = target; + await new Promise((resolve, reject) => { + ele.onload = resolve; + ele.onerror = reject; + document.head.appendChild(ele); + }); + loaded[target] = window.exports; + return loaded[target]; +} + +export function emulateAnchorClick(ev: KeyboardEvent, targetUrl: string, alwaysOpenInNewWindow = false) { + let openInNewWindow; + if (alwaysOpenInNewWindow) openInNewWindow = true; + else openInNewWindow = (ev.ctrlKey || ev.shiftKey || ev.metaKey); + if (openInNewWindow) window.open(targetUrl); + else window.location.href = targetUrl; +} + +export { default as pjax } from './pjax'; +export { default as base64 } from './base64'; +export { default as loadReactRedux } from './loadReactRedux'; +export * as mediaQuery from './mediaQuery'; +export * from './slide'; +export * from './base'; + +const zip = { createZipStream, createZipBlob }; +Object.assign(window.Hydro.utils, { + zip, + pipeStream, + mongoId, + loadExternalModule, + emulateAnchorClick, +}); diff --git a/packages/ui-default/utils/loadModule.ts b/packages/ui-default/utils/loadModule.ts deleted file mode 100644 index 39707137..00000000 --- a/packages/ui-default/utils/loadModule.ts +++ /dev/null @@ -1,14 +0,0 @@ -const loaded = {}; - -export default async function loadExternalModule(target: string) { - if (loaded[target]) return loaded[target]; - const ele = document.createElement('script'); - ele.src = target; - await new Promise((resolve, reject) => { - ele.onload = resolve; - ele.onerror = reject; - document.head.appendChild(ele); - }); - loaded[target] = window.exports; - return loaded[target]; -} diff --git a/packages/ui-default/utils/loadReactRedux.ts b/packages/ui-default/utils/loadReactRedux.ts index 46723457..a0233158 100644 --- a/packages/ui-default/utils/loadReactRedux.ts +++ b/packages/ui-default/utils/loadReactRedux.ts @@ -1,4 +1,4 @@ -import { Action, AnyAction, Reducer } from 'redux'; +import type { Action, AnyAction, Reducer } from 'redux'; export default async function loadReactRedux(storeReducer: Reducer) { const React = await import('react'); diff --git a/packages/ui-default/utils/mongoId.js b/packages/ui-default/utils/mongoId.js deleted file mode 100644 index 508cfd5e..00000000 --- a/packages/ui-default/utils/mongoId.js +++ /dev/null @@ -1,16 +0,0 @@ -/* eslint-disable */ -// https://github.com/andrasq/node-mongoid-js/blob/master/mongoid.js - -export function parse(idstring) { - if (typeof idstring !== 'string') { - idstring = String(idstring); - } - return { - timestamp: parseInt(idstring.slice( 0, 0+8), 16), - machineid: parseInt(idstring.slice( 8, 8+6), 16), - pid: parseInt(idstring.slice(14, 14+4), 16), - sequence: parseInt(idstring.slice(18, 18+6), 16), - }; -} - -window.Hydro.utils.mongoId = parse; diff --git a/packages/ui-default/utils/pipeStream.js b/packages/ui-default/utils/pipeStream.js deleted file mode 100644 index 8c75698b..00000000 --- a/packages/ui-default/utils/pipeStream.js +++ /dev/null @@ -1,21 +0,0 @@ -export default async function pipeStream(read, write, abort) { - if (window.WritableStream && read.pipeTo) { - const abortController = new AbortController(); - if (abort) abort.abort = abortController.abort.bind(abortController); - await read.pipeTo(write, abortController); - } else { - const writer = write.getWriter(); - if (abort) abort.abort = writer.abort.bind(writer); - const reader = read.getReader(); - // eslint-disable-next-line no-constant-condition - while (1) { - const readResult = await reader.read(); - if (readResult.done) { - writer.close(); - break; - } else writer.write(readResult.value); - } - } -} - -window.Hydro.utils.pipeStream = pipeStream; diff --git a/packages/ui-default/utils/pjax.js b/packages/ui-default/utils/pjax.js index 01a09444..7a0f8f13 100644 --- a/packages/ui-default/utils/pjax.js +++ b/packages/ui-default/utils/pjax.js @@ -5,7 +5,7 @@ import $ from 'jquery'; import { nanoid } from 'nanoid'; import NProgress from 'nprogress'; import Notification from 'vj/components/notification'; -import request from 'vj/utils/request'; +import { request } from './base'; const pjax = {}; diff --git a/packages/ui-default/utils/request.js b/packages/ui-default/utils/request.js deleted file mode 100644 index 4335928f..00000000 --- a/packages/ui-default/utils/request.js +++ /dev/null @@ -1,106 +0,0 @@ -import $ from 'jquery'; -import i18n from './i18n'; - -const request = { - /** - * @param {object} options - */ - async ajax(options) { - return new Promise((resolve, reject) => { - $ - .ajax({ - dataType: 'json', - headers: { - Accept: 'application/json', - }, - ...options, - }) - .fail((jqXHR, textStatus, errorThrown) => { - if (textStatus === 'abort') { - const err = new Error(i18n('Aborted')); - err.aborted = true; - reject(err); - } else if (jqXHR.readyState === 0) { - reject(new Error(i18n('Network error'))); - } else if (typeof jqXHR.responseJSON === 'object' && jqXHR.responseJSON.error) { - const { error } = jqXHR.responseJSON; - if (error.params) { - const message = i18n(error.message, ...error.params); - const err = new Error(message === error.message && error.params.length ? `${error.message}: ${error.params.join(' ')}` : message); - err.rawMessage = error.message; - err.params = error.params; - reject(err); - } else reject(new Error(jqXHR.responseJSON.error.message)); - } else if (errorThrown instanceof Error) { - reject(errorThrown); - } else { - reject(new Error(textStatus)); - } - }) - .done(resolve); - }); - }, - - /** - * @param {string} url - * @param {FormData} FormData - * @param {object} options - */ - postFile(url, form, options = {}) { - return this.ajax({ - url, - data: form, - processData: false, - contentType: false, - type: 'POST', - dataType: undefined, - ...options, - }); - }, - - /** - * @param {string} url - * @param {JQueryStatic | Node | string | object} dataOrForm - * @param {object} options - */ - post(url, dataOrForm = {}, options = {}) { - let postData; - if (dataOrForm instanceof jQuery && dataOrForm.is('form')) { - // $form - postData = dataOrForm.serialize(); - } else if (dataOrForm instanceof Node && $(dataOrForm).is('form')) { - // form - postData = $(dataOrForm).serialize(); - } else if (typeof dataOrForm === 'string') { - // foo=bar&box=boz - postData = dataOrForm; - } else { - // {foo: 'bar'} - postData = JSON.stringify(dataOrForm); - options.contentType = 'application/json'; - } - return request.ajax({ - url, - method: 'post', - data: postData, - ...options, - }); - }, - - /** - * @param {string} url - * @param {object} qs - * @param {object} options - */ - get(url, qs = {}, options = {}) { - return request.ajax({ - url, - data: qs, - method: 'get', - ...options, - }); - }, -}; - -export default request; -window.Hydro.utils.request = request; diff --git a/packages/ui-default/utils/slide.js b/packages/ui-default/utils/slide.ts similarity index 100% rename from packages/ui-default/utils/slide.js rename to packages/ui-default/utils/slide.ts diff --git a/packages/ui-default/utils/substitute.js b/packages/ui-default/utils/substitute.js deleted file mode 100644 index 753c228f..00000000 --- a/packages/ui-default/utils/substitute.js +++ /dev/null @@ -1,13 +0,0 @@ -/** - * @param {string} str - * @param {any} obj - * @returns {string} - */ -export default function substitute(str, obj) { - return str.replace(/\{([^{}]+)\}/g, (match, key) => { - if (obj[key] !== undefined) return obj[key].toString(); - return `{${key}}`; - }); -} - -window.Hydro.utils.substitute = substitute; diff --git a/packages/ui-default/utils/tpl.ts b/packages/ui-default/utils/tpl.ts deleted file mode 100644 index 60703210..00000000 --- a/packages/ui-default/utils/tpl.ts +++ /dev/null @@ -1,31 +0,0 @@ -import _ from 'lodash'; - -type Substitution = string | number | { templateRaw: true, html: string }; - -export default function tpl(pieces: TemplateStringsArray, ...substitutions: Substitution[]) { - let result = pieces[0]; - for (let i = 0; i < substitutions.length; ++i) { - const subst = substitutions[i]; - let substHtml: string; - if (typeof subst === 'object' && subst.templateRaw) { - substHtml = subst.html; - } else substHtml = _.escape(String(subst)); - result += substHtml + pieces[i + 1]; - } - return result; -} - -tpl.typoMsg = function (msg: string, raw = false) { - if (raw) return `

${msg}

`; - const lines = msg.trim().split('\n'); - return `
${lines.map((i) => `

${_.escape(i)}

`).join('\n')}
`; -}; - -export function rawHtml(html: string) { - return { - templateRaw: true, - html, - }; -} - -window.Hydro.utils.tpl = tpl; diff --git a/packages/ui-default/utils/zIndexManager.js b/packages/ui-default/utils/zIndexManager.js deleted file mode 100644 index 34427138..00000000 --- a/packages/ui-default/utils/zIndexManager.js +++ /dev/null @@ -1,13 +0,0 @@ -let zIndexCurrent = 1000; - -const manager = { - getCurrent() { - return zIndexCurrent; - }, - getNext() { - return ++zIndexCurrent; - }, -}; - -export default manager; -window.Hydro.utils.zIndexManager = manager; diff --git a/packages/ui-default/utils/zip.js b/packages/ui-default/utils/zip.js deleted file mode 100644 index 26600611..00000000 --- a/packages/ui-default/utils/zip.js +++ /dev/null @@ -1,9 +0,0 @@ -import 'streamsaver/examples/zip-stream'; - -export const createZipStream = window.ZIP; - -export async function createZipBlob(underlyingSource) { - return new Response(createZipStream(underlyingSource)).blob(); -} - -window.Hydro.utils.zip = { createZipStream, createZipBlob };