From 02a9bf9ecfe1659b3481a5386e7a06ee2f0e5fc5 Mon Sep 17 00:00:00 2001 From: crupest Date: Tue, 1 Sep 2020 01:34:41 +0800 Subject: Upgrade packages. --- Timeline/ClientApp/.pnp.js | 529 ++++++++++++++++++++++------------------ Timeline/ClientApp/package.json | 30 +-- Timeline/ClientApp/yarn.lock | 488 +++++++++++++++++++----------------- 3 files changed, 572 insertions(+), 475 deletions(-) diff --git a/Timeline/ClientApp/.pnp.js b/Timeline/ClientApp/.pnp.js index ef53bd2c..394de43b 100644 --- a/Timeline/ClientApp/.pnp.js +++ b/Timeline/ClientApp/.pnp.js @@ -79,15 +79,15 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ], [ "@types/lodash", - "npm:4.14.159::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Flodash%2Fdownload%2F%40types%2Flodash-4.14.159.tgz" + "npm:4.14.161::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Flodash%2Fdownload%2F%40types%2Flodash-4.14.161.tgz" ], [ "@types/node", - "npm:14.6.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Fnode%2Fdownload%2F%40types%2Fnode-14.6.0.tgz" + "npm:14.6.2::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Fnode%2Fdownload%2F%40types%2Fnode-14.6.2.tgz" ], [ "@types/react", - "npm:16.9.46::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Freact%2Fdownload%2F%40types%2Freact-16.9.46.tgz" + "npm:16.9.49::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Freact%2Fdownload%2F%40types%2Freact-16.9.49.tgz" ], [ "@types/react-dom", @@ -119,15 +119,15 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ], [ "@typescript-eslint/eslint-plugin", - "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:3.9.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40typescript-eslint%2Feslint-plugin%2Fdownload%2F%40typescript-eslint%2Feslint-plugin-3.9.1.tgz" + "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:4.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40typescript-eslint%2Feslint-plugin%2Fdownload%2F%40typescript-eslint%2Feslint-plugin-4.0.0.tgz" ], [ "@typescript-eslint/parser", - "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:3.9.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40typescript-eslint%2Fparser%2Fdownload%2F%40typescript-eslint%2Fparser-3.9.1.tgz" + "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:4.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40typescript-eslint%2Fparser%2Fdownload%2F%40typescript-eslint%2Fparser-4.0.0.tgz" ], [ "@yarnpkg/pnpify", - "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:2.1.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40yarnpkg%2Fpnpify%2Fdownload%2F%40yarnpkg%2Fpnpify-2.1.0.tgz" + "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:2.2.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40yarnpkg%2Fpnpify%2Fdownload%2F%40yarnpkg%2Fpnpify-2.2.1.tgz" ], [ "axios", @@ -147,7 +147,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ], [ "bootstrap-icons", - "npm:1.0.0-alpha5::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fbootstrap-icons%2Fdownload%2Fbootstrap-icons-1.0.0-alpha5.tgz" + "npm:1.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fbootstrap-icons%2Fdownload%2Fbootstrap-icons-1.0.0.tgz" ], [ "classnames", @@ -163,7 +163,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ], [ "copy-webpack-plugin", - "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:6.0.3::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fcopy-webpack-plugin%2Fdownload%2Fcopy-webpack-plugin-6.0.3.tgz" + "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:6.1.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fcopy-webpack-plugin%2Fdownload%2Fcopy-webpack-plugin-6.1.0.tgz" ], [ "core-js", @@ -175,7 +175,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ], [ "css-loader", - "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:4.2.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fcss-loader%2Fdownload%2Fcss-loader-4.2.1.tgz" + "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:4.2.2::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fcss-loader%2Fdownload%2Fcss-loader-4.2.2.tgz" ], [ "eslint", @@ -207,11 +207,11 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ], [ "file-loader", - "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:6.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Ffile-loader%2Fdownload%2Ffile-loader-6.0.0.tgz" + "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:6.1.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Ffile-loader%2Fdownload%2Ffile-loader-6.1.0.tgz" ], [ "html-webpack-plugin", - "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:4.3.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fhtml-webpack-plugin%2Fdownload%2Fhtml-webpack-plugin-4.3.0.tgz" + "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:4.4.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fhtml-webpack-plugin%2Fdownload%2Fhtml-webpack-plugin-4.4.1.tgz" ], [ "http-server", @@ -235,7 +235,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ], [ "mini-css-extract-plugin", - "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:0.10.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fmini-css-extract-plugin%2Fdownload%2Fmini-css-extract-plugin-0.10.0.tgz" + "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:0.11.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fmini-css-extract-plugin%2Fdownload%2Fmini-css-extract-plugin-0.11.0.tgz" ], [ "pepjs", @@ -255,7 +255,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ], [ "prettier", - "npm:2.1.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fprettier%2Fdownload%2Fprettier-2.1.0.tgz" + "npm:2.1.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fprettier%2Fdownload%2Fprettier-2.1.1.tgz" ], [ "react", @@ -271,7 +271,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ], [ "react-i18next", - "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:11.7.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Freact-i18next%2Fdownload%2Freact-i18next-11.7.1.tgz" + "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:11.7.2::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Freact-i18next%2Fdownload%2Freact-i18next-11.7.2.tgz" ], [ "react-inlinesvg", @@ -307,7 +307,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ], [ "sass-loader", - "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:9.0.3::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fsass-loader%2Fdownload%2Fsass-loader-9.0.3.tgz" + "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:10.0.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fsass-loader%2Fdownload%2Fsass-loader-10.0.1.tgz" ], [ "style-loader", @@ -385,9 +385,9 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["@hot-loader/react-dom", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:16.13.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40hot-loader%2Freact-dom%2Fdownload%2F%40hot-loader%2Freact-dom-16.13.0.tgz"], ["@types/classnames", "npm:2.2.10::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Fclassnames%2Fdownload%2F%40types%2Fclassnames-2.2.10.tgz"], ["@types/crypto-js", "npm:3.1.47::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Fcrypto-js%2Fdownload%2F%40types%2Fcrypto-js-3.1.47.tgz"], - ["@types/lodash", "npm:4.14.159::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Flodash%2Fdownload%2F%40types%2Flodash-4.14.159.tgz"], - ["@types/node", "npm:14.6.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Fnode%2Fdownload%2F%40types%2Fnode-14.6.0.tgz"], - ["@types/react", "npm:16.9.46::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Freact%2Fdownload%2F%40types%2Freact-16.9.46.tgz"], + ["@types/lodash", "npm:4.14.161::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Flodash%2Fdownload%2F%40types%2Flodash-4.14.161.tgz"], + ["@types/node", "npm:14.6.2::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Fnode%2Fdownload%2F%40types%2Fnode-14.6.2.tgz"], + ["@types/react", "npm:16.9.49::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Freact%2Fdownload%2F%40types%2Freact-16.9.49.tgz"], ["@types/react-dom", "npm:16.9.8::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Freact-dom%2Fdownload%2F%40types%2Freact-dom-16.9.8.tgz"], ["@types/react-responsive", "npm:8.0.2::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Freact-responsive%2Fdownload%2F%40types%2Freact-responsive-8.0.2.tgz"], ["@types/react-router", "npm:5.1.8::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Freact-router%2Fdownload%2F%40types%2Freact-router-5.1.8.tgz"], @@ -395,21 +395,21 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["@types/reactstrap", "npm:8.5.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Freactstrap%2Fdownload%2F%40types%2Freactstrap-8.5.1.tgz"], ["@types/webpack-env", "npm:1.15.2::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Fwebpack-env%2Fdownload%2F%40types%2Fwebpack-env-1.15.2.tgz"], ["@types/xregexp", "npm:4.3.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Fxregexp%2Fdownload%2F%40types%2Fxregexp-4.3.0.tgz"], - ["@typescript-eslint/eslint-plugin", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:3.9.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40typescript-eslint%2Feslint-plugin%2Fdownload%2F%40typescript-eslint%2Feslint-plugin-3.9.1.tgz"], - ["@typescript-eslint/parser", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:3.9.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40typescript-eslint%2Fparser%2Fdownload%2F%40typescript-eslint%2Fparser-3.9.1.tgz"], - ["@yarnpkg/pnpify", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:2.1.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40yarnpkg%2Fpnpify%2Fdownload%2F%40yarnpkg%2Fpnpify-2.1.0.tgz"], + ["@typescript-eslint/eslint-plugin", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:4.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40typescript-eslint%2Feslint-plugin%2Fdownload%2F%40typescript-eslint%2Feslint-plugin-4.0.0.tgz"], + ["@typescript-eslint/parser", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:4.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40typescript-eslint%2Fparser%2Fdownload%2F%40typescript-eslint%2Fparser-4.0.0.tgz"], + ["@yarnpkg/pnpify", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:2.2.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40yarnpkg%2Fpnpify%2Fdownload%2F%40yarnpkg%2Fpnpify-2.2.1.tgz"], ["axios", "npm:0.20.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Faxios%2Fdownload%2Faxios-0.20.0.tgz"], ["babel-loader", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:8.1.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fbabel-loader%2Fdownload%2Fbabel-loader-8.1.0.tgz"], ["babel-plugin-transform-builtin-extend", "npm:1.1.2::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fbabel-plugin-transform-builtin-extend%2Fdownload%2Fbabel-plugin-transform-builtin-extend-1.1.2.tgz"], ["bootstrap", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:4.5.2::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fbootstrap%2Fdownload%2Fbootstrap-4.5.2.tgz"], - ["bootstrap-icons", "npm:1.0.0-alpha5::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fbootstrap-icons%2Fdownload%2Fbootstrap-icons-1.0.0-alpha5.tgz"], + ["bootstrap-icons", "npm:1.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fbootstrap-icons%2Fdownload%2Fbootstrap-icons-1.0.0.tgz"], ["classnames", "npm:2.2.6::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fclassnames%2Fdownload%2Fclassnames-2.2.6.tgz"], ["clean-webpack-plugin", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:3.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fclean-webpack-plugin%2Fdownload%2Fclean-webpack-plugin-3.0.0.tgz"], ["clsx", "npm:1.1.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fclsx%2Fdownload%2Fclsx-1.1.1.tgz"], - ["copy-webpack-plugin", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:6.0.3::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fcopy-webpack-plugin%2Fdownload%2Fcopy-webpack-plugin-6.0.3.tgz"], + ["copy-webpack-plugin", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:6.1.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fcopy-webpack-plugin%2Fdownload%2Fcopy-webpack-plugin-6.1.0.tgz"], ["core-js", "npm:3.6.5::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fcore-js%2Fdownload%2Fcore-js-3.6.5.tgz"], ["crypto-js", "npm:4.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fcrypto-js%2Fdownload%2Fcrypto-js-4.0.0.tgz"], - ["css-loader", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:4.2.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fcss-loader%2Fdownload%2Fcss-loader-4.2.1.tgz"], + ["css-loader", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:4.2.2::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fcss-loader%2Fdownload%2Fcss-loader-4.2.2.tgz"], ["eslint", "npm:7.7.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Feslint%2Fdownload%2Feslint-7.7.0.tgz"], ["eslint-config-prettier", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:6.11.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Feslint-config-prettier%2Fdownload%2Feslint-config-prettier-6.11.0.tgz"], ["eslint-import-resolver-webpack", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:0.12.2::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Feslint-import-resolver-webpack%2Fdownload%2Feslint-import-resolver-webpack-0.12.2.tgz"], @@ -417,23 +417,23 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["eslint-plugin-prettier", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:3.1.4::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Feslint-plugin-prettier%2Fdownload%2Feslint-plugin-prettier-3.1.4.tgz"], ["eslint-plugin-react", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:7.20.6::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Feslint-plugin-react%2Fdownload%2Feslint-plugin-react-7.20.6.tgz"], ["eslint-plugin-react-hooks", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:4.1.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Feslint-plugin-react-hooks%2Fdownload%2Feslint-plugin-react-hooks-4.1.0.tgz"], - ["file-loader", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:6.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Ffile-loader%2Fdownload%2Ffile-loader-6.0.0.tgz"], - ["html-webpack-plugin", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:4.3.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fhtml-webpack-plugin%2Fdownload%2Fhtml-webpack-plugin-4.3.0.tgz"], + ["file-loader", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:6.1.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Ffile-loader%2Fdownload%2Ffile-loader-6.1.0.tgz"], + ["html-webpack-plugin", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:4.4.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fhtml-webpack-plugin%2Fdownload%2Fhtml-webpack-plugin-4.4.1.tgz"], ["http-server", "npm:0.12.3::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fhttp-server%2Fdownload%2Fhttp-server-0.12.3.tgz"], ["i18next", "npm:19.7.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fi18next%2Fdownload%2Fi18next-19.7.0.tgz"], ["i18next-browser-languagedetector", "npm:6.0.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fi18next-browser-languagedetector%2Fdownload%2Fi18next-browser-languagedetector-6.0.1.tgz"], ["localforage", "npm:1.9.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Flocalforage%2Fdownload%2Flocalforage-1.9.0.tgz"], ["lodash", "npm:4.17.20::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Flodash%2Fdownload%2Flodash-4.17.20.tgz"], - ["mini-css-extract-plugin", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:0.10.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fmini-css-extract-plugin%2Fdownload%2Fmini-css-extract-plugin-0.10.0.tgz"], + ["mini-css-extract-plugin", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:0.11.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fmini-css-extract-plugin%2Fdownload%2Fmini-css-extract-plugin-0.11.0.tgz"], ["pepjs", "npm:0.5.2::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fpepjs%2Fdownload%2Fpepjs-0.5.2.tgz"], ["pnp-webpack-plugin", "npm:1.6.4::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fpnp-webpack-plugin%2Fdownload%2Fpnp-webpack-plugin-1.6.4.tgz"], ["postcss-loader", "npm:3.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fpostcss-loader%2Fdownload%2Fpostcss-loader-3.0.0.tgz"], ["postcss-preset-env", "npm:6.7.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fpostcss-preset-env%2Fdownload%2Fpostcss-preset-env-6.7.0.tgz"], - ["prettier", "npm:2.1.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fprettier%2Fdownload%2Fprettier-2.1.0.tgz"], + ["prettier", "npm:2.1.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fprettier%2Fdownload%2Fprettier-2.1.1.tgz"], ["react", "npm:16.13.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Freact%2Fdownload%2Freact-16.13.1.tgz"], ["react-dom", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:16.13.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Freact-dom%2Fdownload%2Freact-dom-16.13.1.tgz"], ["react-hot-loader", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:4.12.21::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Freact-hot-loader%2Fdownload%2Freact-hot-loader-4.12.21.tgz"], - ["react-i18next", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:11.7.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Freact-i18next%2Fdownload%2Freact-i18next-11.7.1.tgz"], + ["react-i18next", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:11.7.2::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Freact-i18next%2Fdownload%2Freact-i18next-11.7.2.tgz"], ["react-inlinesvg", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:2.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Freact-inlinesvg%2Fdownload%2Freact-inlinesvg-2.0.0.tgz"], ["react-responsive", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:8.1.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Freact-responsive%2Fdownload%2Freact-responsive-8.1.0.tgz"], ["react-router", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:5.2.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Freact-router%2Fdownload%2Freact-router-5.2.0.tgz"], @@ -442,7 +442,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["regenerator-runtime", "npm:0.13.7::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fregenerator-runtime%2Fdownload%2Fregenerator-runtime-0.13.7.tgz"], ["rxjs", "npm:6.6.2::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Frxjs%2Fdownload%2Frxjs-6.6.2.tgz"], ["sass", "npm:1.26.10::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fsass%2Fdownload%2Fsass-1.26.10.tgz"], - ["sass-loader", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:9.0.3::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fsass-loader%2Fdownload%2Fsass-loader-9.0.3.tgz"], + ["sass-loader", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:10.0.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fsass-loader%2Fdownload%2Fsass-loader-10.0.1.tgz"], ["style-loader", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:1.2.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fstyle-loader%2Fdownload%2Fstyle-loader-1.2.1.tgz"], ["ts-loader", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:8.0.3::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fts-loader%2Fdownload%2Fts-loader-8.0.3.tgz"], ["typescript", "patch:typescript@npm%3A4.0.2%3A%3A__archiveUrl=https%253A%252F%252Fregistry.npm.taobao.org%252Ftypescript%252Fdownload%252Ftypescript-4.0.2.tgz#builtin::version=4.0.2&hash=5b02a2"], @@ -3229,15 +3229,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], - ["@types/eslint-visitor-keys", [ - ["npm:1.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Feslint-visitor-keys%2Fdownload%2F%40types%2Feslint-visitor-keys-1.0.0.tgz", { - "packageLocation": "./.yarn/cache/@types-eslint-visitor-keys-npm-1.0.0-9c16fc1263-48d1f32631.zip/node_modules/@types/eslint-visitor-keys/", - "packageDependencies": [ - ["@types/eslint-visitor-keys", "npm:1.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Feslint-visitor-keys%2Fdownload%2F%40types%2Feslint-visitor-keys-1.0.0.tgz"] - ], - "linkType": "HARD", - }] - ]], ["@types/estree", [ ["npm:0.0.39::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Festree%2Fdownload%2F%40types%2Festree-0.0.39.tgz", { "packageLocation": "./.yarn/cache/@types-estree-npm-0.0.39-db58f9afdf-43e5361de3.zip/node_modules/@types/estree/", @@ -3321,10 +3312,10 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { }] ]], ["@types/lodash", [ - ["npm:4.14.159::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Flodash%2Fdownload%2F%40types%2Flodash-4.14.159.tgz", { - "packageLocation": "./.yarn/cache/@types-lodash-npm-4.14.159-de2da0836d-15fc1b6909.zip/node_modules/@types/lodash/", + ["npm:4.14.161::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Flodash%2Fdownload%2F%40types%2Flodash-4.14.161.tgz", { + "packageLocation": "./.yarn/cache/@types-lodash-npm-4.14.161-fc1b1238ad-c3657517da.zip/node_modules/@types/lodash/", "packageDependencies": [ - ["@types/lodash", "npm:4.14.159::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Flodash%2Fdownload%2F%40types%2Flodash-4.14.159.tgz"] + ["@types/lodash", "npm:4.14.161::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Flodash%2Fdownload%2F%40types%2Flodash-4.14.161.tgz"] ], "linkType": "HARD", }] @@ -3353,10 +3344,10 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ], "linkType": "HARD", }], - ["npm:14.6.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Fnode%2Fdownload%2F%40types%2Fnode-14.6.0.tgz", { - "packageLocation": "./.yarn/cache/@types-node-npm-14.6.0-8de88b91df-ff23553ab7.zip/node_modules/@types/node/", + ["npm:14.6.2::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Fnode%2Fdownload%2F%40types%2Fnode-14.6.2.tgz", { + "packageLocation": "./.yarn/cache/@types-node-npm-14.6.2-46f5ae4338-e88f4749fd.zip/node_modules/@types/node/", "packageDependencies": [ - ["@types/node", "npm:14.6.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Fnode%2Fdownload%2F%40types%2Fnode-14.6.0.tgz"] + ["@types/node", "npm:14.6.2::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Fnode%2Fdownload%2F%40types%2Fnode-14.6.2.tgz"] ], "linkType": "HARD", }] @@ -3380,10 +3371,10 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ], "linkType": "HARD", }], - ["npm:16.9.46::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Freact%2Fdownload%2F%40types%2Freact-16.9.46.tgz", { - "packageLocation": "./.yarn/cache/@types-react-npm-16.9.46-49309c9681-ad8d07195d.zip/node_modules/@types/react/", + ["npm:16.9.49::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Freact%2Fdownload%2F%40types%2Freact-16.9.49.tgz", { + "packageLocation": "./.yarn/cache/@types-react-npm-16.9.49-eebeaec305-cf5dabe1e9.zip/node_modules/@types/react/", "packageDependencies": [ - ["@types/react", "npm:16.9.46::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Freact%2Fdownload%2F%40types%2Freact-16.9.46.tgz"], + ["@types/react", "npm:16.9.49::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Freact%2Fdownload%2F%40types%2Freact-16.9.49.tgz"], ["@types/prop-types", "npm:15.7.3::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Fprop-types%2Fdownload%2F%40types%2Fprop-types-15.7.3.tgz"], ["csstype", "npm:3.0.2::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fcsstype%2Fdownload%2Fcsstype-3.0.2.tgz"] ], @@ -3538,18 +3529,19 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { }] ]], ["@typescript-eslint/eslint-plugin", [ - ["virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:3.9.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40typescript-eslint%2Feslint-plugin%2Fdownload%2F%40typescript-eslint%2Feslint-plugin-3.9.1.tgz", { - "packageLocation": "./.yarn/$$virtual/@typescript-eslint-eslint-plugin-virtual-6b090f53c6/0/cache/@typescript-eslint-eslint-plugin-npm-3.9.1-2cda56b7cb-252bab346c.zip/node_modules/@typescript-eslint/eslint-plugin/", + ["virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:4.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40typescript-eslint%2Feslint-plugin%2Fdownload%2F%40typescript-eslint%2Feslint-plugin-4.0.0.tgz", { + "packageLocation": "./.yarn/$$virtual/@typescript-eslint-eslint-plugin-virtual-59fa95d1e1/0/cache/@typescript-eslint-eslint-plugin-npm-4.0.0-d45a5fb39f-4a6d9fa69a.zip/node_modules/@typescript-eslint/eslint-plugin/", "packageDependencies": [ - ["@typescript-eslint/eslint-plugin", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:3.9.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40typescript-eslint%2Feslint-plugin%2Fdownload%2F%40typescript-eslint%2Feslint-plugin-3.9.1.tgz"], - ["@typescript-eslint/experimental-utils", "virtual:6b090f53c60ffa0b7aaef0ced1caa498287b02ac23431999e7b6354a77c6335756f1b98cede1091b7477651df82c3e43da90a593747dbb5c19e306c383157b84#npm:3.9.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40typescript-eslint%2Fexperimental-utils%2Fdownload%2F%40typescript-eslint%2Fexperimental-utils-3.9.1.tgz"], - ["@typescript-eslint/parser", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:3.9.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40typescript-eslint%2Fparser%2Fdownload%2F%40typescript-eslint%2Fparser-3.9.1.tgz"], + ["@typescript-eslint/eslint-plugin", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:4.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40typescript-eslint%2Feslint-plugin%2Fdownload%2F%40typescript-eslint%2Feslint-plugin-4.0.0.tgz"], + ["@typescript-eslint/experimental-utils", "virtual:59fa95d1e1b45f175b2b0fe13055bb329362fd9201cf365c000bd2cdfb659cd47e66040b8775a7bbfcc4ef91002fac4d001fb7c414f5d53084426fa9cb85ceba#npm:4.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40typescript-eslint%2Fexperimental-utils%2Fdownload%2F%40typescript-eslint%2Fexperimental-utils-4.0.0.tgz"], + ["@typescript-eslint/parser", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:4.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40typescript-eslint%2Fparser%2Fdownload%2F%40typescript-eslint%2Fparser-4.0.0.tgz"], + ["@typescript-eslint/scope-manager", "npm:4.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40typescript-eslint%2Fscope-manager%2Fdownload%2F%40typescript-eslint%2Fscope-manager-4.0.0.tgz"], ["debug", "virtual:1e7fd7cbf468e9bdfe63ec86a342d405dc91ac84c8bb9894805983f8c23226bd5a88ff6423eed3317b91c46b7e9258b9c2030fa7275a62d19703cdbb018c2efc#npm:4.2.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fdebug%2Fdownload%2Fdebug-4.2.0.tgz"], ["eslint", "npm:7.7.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Feslint%2Fdownload%2Feslint-7.7.0.tgz"], ["functional-red-black-tree", "npm:1.0.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Ffunctional-red-black-tree%2Fdownload%2Ffunctional-red-black-tree-1.0.1.tgz"], ["regexpp", "npm:3.1.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fregexpp%2Fdownload%2Fregexpp-3.1.0.tgz"], ["semver", "npm:7.3.2::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fsemver%2Fdownload%2Fsemver-7.3.2.tgz"], - ["tsutils", "virtual:6b090f53c60ffa0b7aaef0ced1caa498287b02ac23431999e7b6354a77c6335756f1b98cede1091b7477651df82c3e43da90a593747dbb5c19e306c383157b84#npm:3.17.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Ftsutils%2Fdownload%2Ftsutils-3.17.1.tgz"], + ["tsutils", "virtual:59fa95d1e1b45f175b2b0fe13055bb329362fd9201cf365c000bd2cdfb659cd47e66040b8775a7bbfcc4ef91002fac4d001fb7c414f5d53084426fa9cb85ceba#npm:3.17.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Ftsutils%2Fdownload%2Ftsutils-3.17.1.tgz"], ["typescript", "patch:typescript@npm%3A4.0.2%3A%3A__archiveUrl=https%253A%252F%252Fregistry.npm.taobao.org%252Ftypescript%252Fdownload%252Ftypescript-4.0.2.tgz#builtin::version=4.0.2&hash=5b02a2"] ], "packagePeers": [ @@ -3561,13 +3553,14 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { }] ]], ["@typescript-eslint/experimental-utils", [ - ["virtual:6b090f53c60ffa0b7aaef0ced1caa498287b02ac23431999e7b6354a77c6335756f1b98cede1091b7477651df82c3e43da90a593747dbb5c19e306c383157b84#npm:3.9.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40typescript-eslint%2Fexperimental-utils%2Fdownload%2F%40typescript-eslint%2Fexperimental-utils-3.9.1.tgz", { - "packageLocation": "./.yarn/$$virtual/@typescript-eslint-experimental-utils-virtual-df4286ad2d/0/cache/@typescript-eslint-experimental-utils-npm-3.9.1-40f2b41373-8092e411ad.zip/node_modules/@typescript-eslint/experimental-utils/", + ["virtual:59fa95d1e1b45f175b2b0fe13055bb329362fd9201cf365c000bd2cdfb659cd47e66040b8775a7bbfcc4ef91002fac4d001fb7c414f5d53084426fa9cb85ceba#npm:4.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40typescript-eslint%2Fexperimental-utils%2Fdownload%2F%40typescript-eslint%2Fexperimental-utils-4.0.0.tgz", { + "packageLocation": "./.yarn/$$virtual/@typescript-eslint-experimental-utils-virtual-4a8395b7de/0/cache/@typescript-eslint-experimental-utils-npm-4.0.0-b8c5c1ae8e-bce9fe3ac3.zip/node_modules/@typescript-eslint/experimental-utils/", "packageDependencies": [ - ["@typescript-eslint/experimental-utils", "virtual:6b090f53c60ffa0b7aaef0ced1caa498287b02ac23431999e7b6354a77c6335756f1b98cede1091b7477651df82c3e43da90a593747dbb5c19e306c383157b84#npm:3.9.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40typescript-eslint%2Fexperimental-utils%2Fdownload%2F%40typescript-eslint%2Fexperimental-utils-3.9.1.tgz"], + ["@typescript-eslint/experimental-utils", "virtual:59fa95d1e1b45f175b2b0fe13055bb329362fd9201cf365c000bd2cdfb659cd47e66040b8775a7bbfcc4ef91002fac4d001fb7c414f5d53084426fa9cb85ceba#npm:4.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40typescript-eslint%2Fexperimental-utils%2Fdownload%2F%40typescript-eslint%2Fexperimental-utils-4.0.0.tgz"], ["@types/json-schema", "npm:7.0.5::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Fjson-schema%2Fdownload%2F%40types%2Fjson-schema-7.0.5.tgz"], - ["@typescript-eslint/types", "npm:3.9.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40typescript-eslint%2Ftypes%2Fdownload%2F%40typescript-eslint%2Ftypes-3.9.1.tgz"], - ["@typescript-eslint/typescript-estree", "virtual:df4286ad2dbd8990171b878316c1fe9598308406e0387a68bb607fb89d218a32cd304b92b54ad46892acd7ac98d4f454c214ce71de95737e5e709e231b69559a#npm:3.9.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40typescript-eslint%2Ftypescript-estree%2Fdownload%2F%40typescript-eslint%2Ftypescript-estree-3.9.1.tgz"], + ["@typescript-eslint/scope-manager", "npm:4.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40typescript-eslint%2Fscope-manager%2Fdownload%2F%40typescript-eslint%2Fscope-manager-4.0.0.tgz"], + ["@typescript-eslint/types", "npm:4.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40typescript-eslint%2Ftypes%2Fdownload%2F%40typescript-eslint%2Ftypes-4.0.0.tgz"], + ["@typescript-eslint/typescript-estree", "virtual:4a8395b7deb0d3993286ac711e436689d2b59efe25d1542ce3d6598858347eec1ac2f3a32f5db446b8f814f22c45ec15e85f608c128181d0866e9bf9eb4382b5#npm:4.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40typescript-eslint%2Ftypescript-estree%2Fdownload%2F%40typescript-eslint%2Ftypescript-estree-4.0.0.tgz"], ["eslint", "npm:7.7.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Feslint%2Fdownload%2Feslint-7.7.0.tgz"], ["eslint-scope", "npm:5.1.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Feslint-scope%2Fdownload%2Feslint-scope-5.1.0.tgz"], ["eslint-utils", "npm:2.1.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Feslint-utils%2Fdownload%2Feslint-utils-2.1.0.tgz"] @@ -3579,16 +3572,15 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { }] ]], ["@typescript-eslint/parser", [ - ["virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:3.9.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40typescript-eslint%2Fparser%2Fdownload%2F%40typescript-eslint%2Fparser-3.9.1.tgz", { - "packageLocation": "./.yarn/$$virtual/@typescript-eslint-parser-virtual-da4b860a85/0/cache/@typescript-eslint-parser-npm-3.9.1-b73c5ce5bf-ed9a482969.zip/node_modules/@typescript-eslint/parser/", - "packageDependencies": [ - ["@typescript-eslint/parser", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:3.9.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40typescript-eslint%2Fparser%2Fdownload%2F%40typescript-eslint%2Fparser-3.9.1.tgz"], - ["@types/eslint-visitor-keys", "npm:1.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Feslint-visitor-keys%2Fdownload%2F%40types%2Feslint-visitor-keys-1.0.0.tgz"], - ["@typescript-eslint/experimental-utils", "virtual:6b090f53c60ffa0b7aaef0ced1caa498287b02ac23431999e7b6354a77c6335756f1b98cede1091b7477651df82c3e43da90a593747dbb5c19e306c383157b84#npm:3.9.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40typescript-eslint%2Fexperimental-utils%2Fdownload%2F%40typescript-eslint%2Fexperimental-utils-3.9.1.tgz"], - ["@typescript-eslint/types", "npm:3.9.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40typescript-eslint%2Ftypes%2Fdownload%2F%40typescript-eslint%2Ftypes-3.9.1.tgz"], - ["@typescript-eslint/typescript-estree", "virtual:da4b860a853ff200ac4ab1f139143c870c7a3642b2e9964b44a445f50c829ca897de6d1c95ed33aa2b04e0f49f3c030468ee540e04dd8c5a3cbd692a0a24d0d4#npm:3.9.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40typescript-eslint%2Ftypescript-estree%2Fdownload%2F%40typescript-eslint%2Ftypescript-estree-3.9.1.tgz"], + ["virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:4.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40typescript-eslint%2Fparser%2Fdownload%2F%40typescript-eslint%2Fparser-4.0.0.tgz", { + "packageLocation": "./.yarn/$$virtual/@typescript-eslint-parser-virtual-ee0c5b9816/0/cache/@typescript-eslint-parser-npm-4.0.0-70b0a03a5b-665ffa7698.zip/node_modules/@typescript-eslint/parser/", + "packageDependencies": [ + ["@typescript-eslint/parser", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:4.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40typescript-eslint%2Fparser%2Fdownload%2F%40typescript-eslint%2Fparser-4.0.0.tgz"], + ["@typescript-eslint/scope-manager", "npm:4.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40typescript-eslint%2Fscope-manager%2Fdownload%2F%40typescript-eslint%2Fscope-manager-4.0.0.tgz"], + ["@typescript-eslint/types", "npm:4.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40typescript-eslint%2Ftypes%2Fdownload%2F%40typescript-eslint%2Ftypes-4.0.0.tgz"], + ["@typescript-eslint/typescript-estree", "virtual:ee0c5b981620be6d6802688c21643864ec63033187eafe8707943e01009d1232386c22e6e883ac63d89d75a3be7604bc335360c0c8b90bd4a841142f596d3bf8#npm:4.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40typescript-eslint%2Ftypescript-estree%2Fdownload%2F%40typescript-eslint%2Ftypescript-estree-4.0.0.tgz"], + ["debug", "virtual:1e7fd7cbf468e9bdfe63ec86a342d405dc91ac84c8bb9894805983f8c23226bd5a88ff6423eed3317b91c46b7e9258b9c2030fa7275a62d19703cdbb018c2efc#npm:4.2.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fdebug%2Fdownload%2Fdebug-4.2.0.tgz"], ["eslint", "npm:7.7.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Feslint%2Fdownload%2Feslint-7.7.0.tgz"], - ["eslint-visitor-keys", "npm:1.3.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Feslint-visitor-keys%2Fdownload%2Feslint-visitor-keys-1.3.0.tgz"], ["typescript", "patch:typescript@npm%3A4.0.2%3A%3A__archiveUrl=https%253A%252F%252Fregistry.npm.taobao.org%252Ftypescript%252Fdownload%252Ftypescript-4.0.2.tgz#builtin::version=4.0.2&hash=5b02a2"] ], "packagePeers": [ @@ -3598,48 +3590,59 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], + ["@typescript-eslint/scope-manager", [ + ["npm:4.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40typescript-eslint%2Fscope-manager%2Fdownload%2F%40typescript-eslint%2Fscope-manager-4.0.0.tgz", { + "packageLocation": "./.yarn/cache/@typescript-eslint-scope-manager-npm-4.0.0-d58e0fbbbe-6645cfbceb.zip/node_modules/@typescript-eslint/scope-manager/", + "packageDependencies": [ + ["@typescript-eslint/scope-manager", "npm:4.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40typescript-eslint%2Fscope-manager%2Fdownload%2F%40typescript-eslint%2Fscope-manager-4.0.0.tgz"], + ["@typescript-eslint/types", "npm:4.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40typescript-eslint%2Ftypes%2Fdownload%2F%40typescript-eslint%2Ftypes-4.0.0.tgz"], + ["@typescript-eslint/visitor-keys", "npm:4.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40typescript-eslint%2Fvisitor-keys%2Fdownload%2F%40typescript-eslint%2Fvisitor-keys-4.0.0.tgz"] + ], + "linkType": "HARD", + }] + ]], ["@typescript-eslint/types", [ - ["npm:3.9.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40typescript-eslint%2Ftypes%2Fdownload%2F%40typescript-eslint%2Ftypes-3.9.1.tgz", { - "packageLocation": "./.yarn/cache/@typescript-eslint-types-npm-3.9.1-cb562eed7e-0591ff7691.zip/node_modules/@typescript-eslint/types/", + ["npm:4.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40typescript-eslint%2Ftypes%2Fdownload%2F%40typescript-eslint%2Ftypes-4.0.0.tgz", { + "packageLocation": "./.yarn/cache/@typescript-eslint-types-npm-4.0.0-d0674d7a56-143dc3d3d4.zip/node_modules/@typescript-eslint/types/", "packageDependencies": [ - ["@typescript-eslint/types", "npm:3.9.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40typescript-eslint%2Ftypes%2Fdownload%2F%40typescript-eslint%2Ftypes-3.9.1.tgz"] + ["@typescript-eslint/types", "npm:4.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40typescript-eslint%2Ftypes%2Fdownload%2F%40typescript-eslint%2Ftypes-4.0.0.tgz"] ], "linkType": "HARD", }] ]], ["@typescript-eslint/typescript-estree", [ - ["virtual:da4b860a853ff200ac4ab1f139143c870c7a3642b2e9964b44a445f50c829ca897de6d1c95ed33aa2b04e0f49f3c030468ee540e04dd8c5a3cbd692a0a24d0d4#npm:3.9.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40typescript-eslint%2Ftypescript-estree%2Fdownload%2F%40typescript-eslint%2Ftypescript-estree-3.9.1.tgz", { - "packageLocation": "./.yarn/$$virtual/@typescript-eslint-typescript-estree-virtual-da7cc4b7f8/0/cache/@typescript-eslint-typescript-estree-npm-3.9.1-79a46ee31e-f9a962b6ff.zip/node_modules/@typescript-eslint/typescript-estree/", + ["virtual:4a8395b7deb0d3993286ac711e436689d2b59efe25d1542ce3d6598858347eec1ac2f3a32f5db446b8f814f22c45ec15e85f608c128181d0866e9bf9eb4382b5#npm:4.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40typescript-eslint%2Ftypescript-estree%2Fdownload%2F%40typescript-eslint%2Ftypescript-estree-4.0.0.tgz", { + "packageLocation": "./.yarn/$$virtual/@typescript-eslint-typescript-estree-virtual-3bc9bdae9f/0/cache/@typescript-eslint-typescript-estree-npm-4.0.0-113ff1e5ab-9fc127bf0a.zip/node_modules/@typescript-eslint/typescript-estree/", "packageDependencies": [ - ["@typescript-eslint/typescript-estree", "virtual:da4b860a853ff200ac4ab1f139143c870c7a3642b2e9964b44a445f50c829ca897de6d1c95ed33aa2b04e0f49f3c030468ee540e04dd8c5a3cbd692a0a24d0d4#npm:3.9.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40typescript-eslint%2Ftypescript-estree%2Fdownload%2F%40typescript-eslint%2Ftypescript-estree-3.9.1.tgz"], - ["@typescript-eslint/types", "npm:3.9.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40typescript-eslint%2Ftypes%2Fdownload%2F%40typescript-eslint%2Ftypes-3.9.1.tgz"], - ["@typescript-eslint/visitor-keys", "npm:3.9.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40typescript-eslint%2Fvisitor-keys%2Fdownload%2F%40typescript-eslint%2Fvisitor-keys-3.9.1.tgz"], + ["@typescript-eslint/typescript-estree", "virtual:4a8395b7deb0d3993286ac711e436689d2b59efe25d1542ce3d6598858347eec1ac2f3a32f5db446b8f814f22c45ec15e85f608c128181d0866e9bf9eb4382b5#npm:4.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40typescript-eslint%2Ftypescript-estree%2Fdownload%2F%40typescript-eslint%2Ftypescript-estree-4.0.0.tgz"], + ["@typescript-eslint/types", "npm:4.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40typescript-eslint%2Ftypes%2Fdownload%2F%40typescript-eslint%2Ftypes-4.0.0.tgz"], + ["@typescript-eslint/visitor-keys", "npm:4.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40typescript-eslint%2Fvisitor-keys%2Fdownload%2F%40typescript-eslint%2Fvisitor-keys-4.0.0.tgz"], ["debug", "virtual:1e7fd7cbf468e9bdfe63ec86a342d405dc91ac84c8bb9894805983f8c23226bd5a88ff6423eed3317b91c46b7e9258b9c2030fa7275a62d19703cdbb018c2efc#npm:4.2.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fdebug%2Fdownload%2Fdebug-4.2.0.tgz"], - ["glob", "npm:7.1.6::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fglob%2Fdownload%2Fglob-7.1.6.tgz"], + ["globby", "npm:11.0.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fglobby%2Fdownload%2Fglobby-11.0.1.tgz"], ["is-glob", "npm:4.0.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fis-glob%2Fdownload%2Fis-glob-4.0.1.tgz"], ["lodash", "npm:4.17.19::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Flodash%2Fdownload%2Flodash-4.17.19.tgz"], ["semver", "npm:7.3.2::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fsemver%2Fdownload%2Fsemver-7.3.2.tgz"], - ["tsutils", "virtual:6b090f53c60ffa0b7aaef0ced1caa498287b02ac23431999e7b6354a77c6335756f1b98cede1091b7477651df82c3e43da90a593747dbb5c19e306c383157b84#npm:3.17.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Ftsutils%2Fdownload%2Ftsutils-3.17.1.tgz"], - ["typescript", "patch:typescript@npm%3A4.0.2%3A%3A__archiveUrl=https%253A%252F%252Fregistry.npm.taobao.org%252Ftypescript%252Fdownload%252Ftypescript-4.0.2.tgz#builtin::version=4.0.2&hash=5b02a2"] + ["tsutils", "virtual:3bc9bdae9fa27b66f969c980ce1611a273e2bd05656277a752cd8fc9e72855f63e3874a9bc052c5aa4a1d20282e78616c3b36e4d78cc2efa9cf9a5b60d4f8827#npm:3.17.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Ftsutils%2Fdownload%2Ftsutils-3.17.1.tgz"], + ["typescript", null] ], "packagePeers": [ "typescript" ], "linkType": "HARD", }], - ["virtual:df4286ad2dbd8990171b878316c1fe9598308406e0387a68bb607fb89d218a32cd304b92b54ad46892acd7ac98d4f454c214ce71de95737e5e709e231b69559a#npm:3.9.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40typescript-eslint%2Ftypescript-estree%2Fdownload%2F%40typescript-eslint%2Ftypescript-estree-3.9.1.tgz", { - "packageLocation": "./.yarn/$$virtual/@typescript-eslint-typescript-estree-virtual-74f3ce4730/0/cache/@typescript-eslint-typescript-estree-npm-3.9.1-79a46ee31e-f9a962b6ff.zip/node_modules/@typescript-eslint/typescript-estree/", + ["virtual:ee0c5b981620be6d6802688c21643864ec63033187eafe8707943e01009d1232386c22e6e883ac63d89d75a3be7604bc335360c0c8b90bd4a841142f596d3bf8#npm:4.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40typescript-eslint%2Ftypescript-estree%2Fdownload%2F%40typescript-eslint%2Ftypescript-estree-4.0.0.tgz", { + "packageLocation": "./.yarn/$$virtual/@typescript-eslint-typescript-estree-virtual-232ac72d2e/0/cache/@typescript-eslint-typescript-estree-npm-4.0.0-113ff1e5ab-9fc127bf0a.zip/node_modules/@typescript-eslint/typescript-estree/", "packageDependencies": [ - ["@typescript-eslint/typescript-estree", "virtual:df4286ad2dbd8990171b878316c1fe9598308406e0387a68bb607fb89d218a32cd304b92b54ad46892acd7ac98d4f454c214ce71de95737e5e709e231b69559a#npm:3.9.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40typescript-eslint%2Ftypescript-estree%2Fdownload%2F%40typescript-eslint%2Ftypescript-estree-3.9.1.tgz"], - ["@typescript-eslint/types", "npm:3.9.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40typescript-eslint%2Ftypes%2Fdownload%2F%40typescript-eslint%2Ftypes-3.9.1.tgz"], - ["@typescript-eslint/visitor-keys", "npm:3.9.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40typescript-eslint%2Fvisitor-keys%2Fdownload%2F%40typescript-eslint%2Fvisitor-keys-3.9.1.tgz"], + ["@typescript-eslint/typescript-estree", "virtual:ee0c5b981620be6d6802688c21643864ec63033187eafe8707943e01009d1232386c22e6e883ac63d89d75a3be7604bc335360c0c8b90bd4a841142f596d3bf8#npm:4.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40typescript-eslint%2Ftypescript-estree%2Fdownload%2F%40typescript-eslint%2Ftypescript-estree-4.0.0.tgz"], + ["@typescript-eslint/types", "npm:4.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40typescript-eslint%2Ftypes%2Fdownload%2F%40typescript-eslint%2Ftypes-4.0.0.tgz"], + ["@typescript-eslint/visitor-keys", "npm:4.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40typescript-eslint%2Fvisitor-keys%2Fdownload%2F%40typescript-eslint%2Fvisitor-keys-4.0.0.tgz"], ["debug", "virtual:1e7fd7cbf468e9bdfe63ec86a342d405dc91ac84c8bb9894805983f8c23226bd5a88ff6423eed3317b91c46b7e9258b9c2030fa7275a62d19703cdbb018c2efc#npm:4.2.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fdebug%2Fdownload%2Fdebug-4.2.0.tgz"], - ["glob", "npm:7.1.6::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fglob%2Fdownload%2Fglob-7.1.6.tgz"], + ["globby", "npm:11.0.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fglobby%2Fdownload%2Fglobby-11.0.1.tgz"], ["is-glob", "npm:4.0.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fis-glob%2Fdownload%2Fis-glob-4.0.1.tgz"], ["lodash", "npm:4.17.19::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Flodash%2Fdownload%2Flodash-4.17.19.tgz"], ["semver", "npm:7.3.2::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fsemver%2Fdownload%2Fsemver-7.3.2.tgz"], - ["tsutils", "virtual:74f3ce4730be82a39c05577948e092efbe436415fbae850d9d5d07bee35df41e3ecfd8a5b58d019a2990d0c5ad4c8a28fe10a468f8ed2307a1f4ca468609e708#npm:3.17.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Ftsutils%2Fdownload%2Ftsutils-3.17.1.tgz"], - ["typescript", null] + ["tsutils", "virtual:59fa95d1e1b45f175b2b0fe13055bb329362fd9201cf365c000bd2cdfb659cd47e66040b8775a7bbfcc4ef91002fac4d001fb7c414f5d53084426fa9cb85ceba#npm:3.17.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Ftsutils%2Fdownload%2Ftsutils-3.17.1.tgz"], + ["typescript", "patch:typescript@npm%3A4.0.2%3A%3A__archiveUrl=https%253A%252F%252Fregistry.npm.taobao.org%252Ftypescript%252Fdownload%252Ftypescript-4.0.2.tgz#builtin::version=4.0.2&hash=5b02a2"] ], "packagePeers": [ "typescript" @@ -3648,11 +3651,12 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { }] ]], ["@typescript-eslint/visitor-keys", [ - ["npm:3.9.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40typescript-eslint%2Fvisitor-keys%2Fdownload%2F%40typescript-eslint%2Fvisitor-keys-3.9.1.tgz", { - "packageLocation": "./.yarn/cache/@typescript-eslint-visitor-keys-npm-3.9.1-e091c000de-9e54d3a37a.zip/node_modules/@typescript-eslint/visitor-keys/", + ["npm:4.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40typescript-eslint%2Fvisitor-keys%2Fdownload%2F%40typescript-eslint%2Fvisitor-keys-4.0.0.tgz", { + "packageLocation": "./.yarn/cache/@typescript-eslint-visitor-keys-npm-4.0.0-1b65633389-e443755abf.zip/node_modules/@typescript-eslint/visitor-keys/", "packageDependencies": [ - ["@typescript-eslint/visitor-keys", "npm:3.9.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40typescript-eslint%2Fvisitor-keys%2Fdownload%2F%40typescript-eslint%2Fvisitor-keys-3.9.1.tgz"], - ["eslint-visitor-keys", "npm:1.3.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Feslint-visitor-keys%2Fdownload%2Feslint-visitor-keys-1.3.0.tgz"] + ["@typescript-eslint/visitor-keys", "npm:4.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40typescript-eslint%2Fvisitor-keys%2Fdownload%2F%40typescript-eslint%2Fvisitor-keys-4.0.0.tgz"], + ["@typescript-eslint/types", "npm:4.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40typescript-eslint%2Ftypes%2Fdownload%2F%40typescript-eslint%2Ftypes-4.0.0.tgz"], + ["eslint-visitor-keys", "npm:2.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Feslint-visitor-keys%2Fdownload%2Feslint-visitor-keys-2.0.0.tgz"] ], "linkType": "HARD", }] @@ -3881,26 +3885,27 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { }] ]], ["@yarnpkg/core", [ - ["npm:2.1.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40yarnpkg%2Fcore%2Fdownload%2F%40yarnpkg%2Fcore-2.1.1.tgz", { - "packageLocation": "./.yarn/cache/@yarnpkg-core-npm-2.1.1-4c0bb5e4cf-d01e24bde1.zip/node_modules/@yarnpkg/core/", + ["npm:2.2.2::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40yarnpkg%2Fcore%2Fdownload%2F%40yarnpkg%2Fcore-2.2.2.tgz", { + "packageLocation": "./.yarn/cache/@yarnpkg-core-npm-2.2.2-c2a9e57c10-d56fdc230e.zip/node_modules/@yarnpkg/core/", "packageDependencies": [ - ["@yarnpkg/core", "npm:2.1.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40yarnpkg%2Fcore%2Fdownload%2F%40yarnpkg%2Fcore-2.1.1.tgz"], + ["@yarnpkg/core", "npm:2.2.2::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40yarnpkg%2Fcore%2Fdownload%2F%40yarnpkg%2Fcore-2.2.2.tgz"], ["@arcanis/slice-ansi", "npm:1.0.2::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40arcanis%2Fslice-ansi%2Fdownload%2F%40arcanis%2Fslice-ansi-1.0.2.tgz"], - ["@yarnpkg/fslib", "npm:2.1.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40yarnpkg%2Ffslib%2Fdownload%2F%40yarnpkg%2Ffslib-2.1.0.tgz"], + ["@yarnpkg/fslib", "npm:2.2.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40yarnpkg%2Ffslib%2Fdownload%2F%40yarnpkg%2Ffslib-2.2.1.tgz"], ["@yarnpkg/json-proxy", "npm:2.1.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40yarnpkg%2Fjson-proxy%2Fdownload%2F%40yarnpkg%2Fjson-proxy-2.1.0.tgz"], - ["@yarnpkg/libzip", "npm:2.1.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40yarnpkg%2Flibzip%2Fdownload%2F%40yarnpkg%2Flibzip-2.1.0.tgz"], - ["@yarnpkg/parsers", "npm:2.1.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40yarnpkg%2Fparsers%2Fdownload%2F%40yarnpkg%2Fparsers-2.1.0.tgz"], - ["@yarnpkg/pnp", "npm:2.1.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40yarnpkg%2Fpnp%2Fdownload%2F%40yarnpkg%2Fpnp-2.1.0.tgz"], - ["@yarnpkg/shell", "npm:2.1.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40yarnpkg%2Fshell%2Fdownload%2F%40yarnpkg%2Fshell-2.1.0.tgz"], + ["@yarnpkg/libzip", "npm:2.2.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40yarnpkg%2Flibzip%2Fdownload%2F%40yarnpkg%2Flibzip-2.2.0.tgz"], + ["@yarnpkg/parsers", "npm:2.2.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40yarnpkg%2Fparsers%2Fdownload%2F%40yarnpkg%2Fparsers-2.2.0.tgz"], + ["@yarnpkg/pnp", "npm:2.2.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40yarnpkg%2Fpnp%2Fdownload%2F%40yarnpkg%2Fpnp-2.2.1.tgz"], + ["@yarnpkg/shell", "npm:2.2.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40yarnpkg%2Fshell%2Fdownload%2F%40yarnpkg%2Fshell-2.2.0.tgz"], ["camelcase", "npm:5.3.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fcamelcase%2Fdownload%2Fcamelcase-5.3.1.tgz"], ["chalk", "npm:3.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fchalk%2Fdownload%2Fchalk-3.0.0.tgz"], ["ci-info", "npm:2.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fci-info%2Fdownload%2Fci-info-2.0.0.tgz"], - ["clipanion", "npm:2.4.2::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fclipanion%2Fdownload%2Fclipanion-2.4.2.tgz"], + ["clipanion", "npm:2.5.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fclipanion%2Fdownload%2Fclipanion-2.5.0.tgz"], ["cross-spawn", "npm:7.0.3::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fcross-spawn%2Fdownload%2Fcross-spawn-7.0.3.tgz"], ["diff", "npm:4.0.2::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fdiff%2Fdownload%2Fdiff-4.0.2.tgz"], - ["globby", "npm:10.0.2::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fglobby%2Fdownload%2Fglobby-10.0.2.tgz"], + ["globby", "npm:11.0.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fglobby%2Fdownload%2Fglobby-11.0.1.tgz"], ["got", "npm:11.5.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fgot%2Fdownload%2Fgot-11.5.1.tgz"], ["json-file-plus", "npm:3.3.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fjson-file-plus%2Fdownload%2Fjson-file-plus-3.3.1.tgz"], + ["lodash", "npm:4.17.19::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Flodash%2Fdownload%2Flodash-4.17.19.tgz"], ["logic-solver", "npm:2.0.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Flogic-solver%2Fdownload%2Flogic-solver-2.0.1.tgz"], ["micromatch", "npm:4.0.2::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fmicromatch%2Fdownload%2Fmicromatch-4.0.2.tgz"], ["mkdirp", "npm:0.5.5::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fmkdirp%2Fdownload%2Fmkdirp-0.5.5.tgz"], @@ -3909,7 +3914,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["pretty-bytes", "npm:5.3.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fpretty-bytes%2Fdownload%2Fpretty-bytes-5.3.0.tgz"], ["semver", "npm:7.3.2::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fsemver%2Fdownload%2Fsemver-7.3.2.tgz"], ["stream-to-promise", "npm:2.2.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fstream-to-promise%2Fdownload%2Fstream-to-promise-2.2.0.tgz"], - ["tar", "npm:4.4.13::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Ftar%2Fdownload%2Ftar-4.4.13.tgz"], + ["tar-stream", "npm:2.1.3::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Ftar-stream%2Fdownload%2Ftar-stream-2.1.3.tgz"], ["tslib", "npm:1.13.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Ftslib%2Fdownload%2Ftslib-1.13.0.tgz"], ["tunnel", "npm:0.0.6::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Ftunnel%2Fdownload%2Ftunnel-0.0.6.tgz"] ], @@ -3925,6 +3930,15 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["tslib", "npm:1.13.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Ftslib%2Fdownload%2Ftslib-1.13.0.tgz"] ], "linkType": "HARD", + }], + ["npm:2.2.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40yarnpkg%2Ffslib%2Fdownload%2F%40yarnpkg%2Ffslib-2.2.1.tgz", { + "packageLocation": "./.yarn/cache/@yarnpkg-fslib-npm-2.2.1-87af5cec80-2d5dac27ef.zip/node_modules/@yarnpkg/fslib/", + "packageDependencies": [ + ["@yarnpkg/fslib", "npm:2.2.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40yarnpkg%2Ffslib%2Fdownload%2F%40yarnpkg%2Ffslib-2.2.1.tgz"], + ["@yarnpkg/libzip", "npm:2.2.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40yarnpkg%2Flibzip%2Fdownload%2F%40yarnpkg%2Flibzip-2.2.0.tgz"], + ["tslib", "npm:1.13.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Ftslib%2Fdownload%2Ftslib-1.13.0.tgz"] + ], + "linkType": "HARD", }] ]], ["@yarnpkg/json-proxy", [ @@ -3947,13 +3961,22 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["tslib", "npm:1.13.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Ftslib%2Fdownload%2Ftslib-1.13.0.tgz"] ], "linkType": "HARD", + }], + ["npm:2.2.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40yarnpkg%2Flibzip%2Fdownload%2F%40yarnpkg%2Flibzip-2.2.0.tgz", { + "packageLocation": "./.yarn/cache/@yarnpkg-libzip-npm-2.2.0-1c1734cc89-5546c330c8.zip/node_modules/@yarnpkg/libzip/", + "packageDependencies": [ + ["@yarnpkg/libzip", "npm:2.2.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40yarnpkg%2Flibzip%2Fdownload%2F%40yarnpkg%2Flibzip-2.2.0.tgz"], + ["@types/emscripten", "npm:1.39.4::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Femscripten%2Fdownload%2F%40types%2Femscripten-1.39.4.tgz"], + ["tslib", "npm:1.13.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Ftslib%2Fdownload%2Ftslib-1.13.0.tgz"] + ], + "linkType": "HARD", }] ]], ["@yarnpkg/parsers", [ - ["npm:2.1.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40yarnpkg%2Fparsers%2Fdownload%2F%40yarnpkg%2Fparsers-2.1.0.tgz", { - "packageLocation": "./.yarn/cache/@yarnpkg-parsers-npm-2.1.0-5652e2f6ba-3cd4e35435.zip/node_modules/@yarnpkg/parsers/", + ["npm:2.2.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40yarnpkg%2Fparsers%2Fdownload%2F%40yarnpkg%2Fparsers-2.2.0.tgz", { + "packageLocation": "./.yarn/cache/@yarnpkg-parsers-npm-2.2.0-2b072561da-4ff177009d.zip/node_modules/@yarnpkg/parsers/", "packageDependencies": [ - ["@yarnpkg/parsers", "npm:2.1.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40yarnpkg%2Fparsers%2Fdownload%2F%40yarnpkg%2Fparsers-2.1.0.tgz"], + ["@yarnpkg/parsers", "npm:2.2.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40yarnpkg%2Fparsers%2Fdownload%2F%40yarnpkg%2Fparsers-2.2.0.tgz"], ["js-yaml", "npm:3.14.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fjs-yaml%2Fdownload%2Fjs-yaml-3.14.0.tgz"], ["tslib", "npm:1.13.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Ftslib%2Fdownload%2Ftslib-1.13.0.tgz"] ], @@ -3961,27 +3984,27 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { }] ]], ["@yarnpkg/pnp", [ - ["npm:2.1.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40yarnpkg%2Fpnp%2Fdownload%2F%40yarnpkg%2Fpnp-2.1.0.tgz", { - "packageLocation": "./.yarn/cache/@yarnpkg-pnp-npm-2.1.0-0497f94088-610f875f59.zip/node_modules/@yarnpkg/pnp/", + ["npm:2.2.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40yarnpkg%2Fpnp%2Fdownload%2F%40yarnpkg%2Fpnp-2.2.1.tgz", { + "packageLocation": "./.yarn/cache/@yarnpkg-pnp-npm-2.2.1-e59427544a-0ea840774c.zip/node_modules/@yarnpkg/pnp/", "packageDependencies": [ - ["@yarnpkg/pnp", "npm:2.1.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40yarnpkg%2Fpnp%2Fdownload%2F%40yarnpkg%2Fpnp-2.1.0.tgz"], + ["@yarnpkg/pnp", "npm:2.2.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40yarnpkg%2Fpnp%2Fdownload%2F%40yarnpkg%2Fpnp-2.2.1.tgz"], ["@types/node", "npm:13.13.14::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Fnode%2Fdownload%2F%40types%2Fnode-13.13.14.tgz"], - ["@yarnpkg/fslib", "npm:2.1.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40yarnpkg%2Ffslib%2Fdownload%2F%40yarnpkg%2Ffslib-2.1.0.tgz"], + ["@yarnpkg/fslib", "npm:2.2.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40yarnpkg%2Ffslib%2Fdownload%2F%40yarnpkg%2Ffslib-2.2.1.tgz"], ["tslib", "npm:1.13.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Ftslib%2Fdownload%2Ftslib-1.13.0.tgz"] ], "linkType": "HARD", }] ]], ["@yarnpkg/pnpify", [ - ["virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:2.1.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40yarnpkg%2Fpnpify%2Fdownload%2F%40yarnpkg%2Fpnpify-2.1.0.tgz", { - "packageLocation": "./.yarn/$$virtual/@yarnpkg-pnpify-virtual-62a5ebbfc7/0/cache/@yarnpkg-pnpify-npm-2.1.0-0c86b1dc2b-334ce4cc6c.zip/node_modules/@yarnpkg/pnpify/", + ["virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:2.2.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40yarnpkg%2Fpnpify%2Fdownload%2F%40yarnpkg%2Fpnpify-2.2.1.tgz", { + "packageLocation": "./.yarn/$$virtual/@yarnpkg-pnpify-virtual-a47283da2b/0/cache/@yarnpkg-pnpify-npm-2.2.1-a9c4a3cd81-47a6fd0c11.zip/node_modules/@yarnpkg/pnpify/", "packageDependencies": [ - ["@yarnpkg/pnpify", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:2.1.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40yarnpkg%2Fpnpify%2Fdownload%2F%40yarnpkg%2Fpnpify-2.1.0.tgz"], - ["@yarnpkg/core", "npm:2.1.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40yarnpkg%2Fcore%2Fdownload%2F%40yarnpkg%2Fcore-2.1.1.tgz"], - ["@yarnpkg/fslib", "npm:2.1.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40yarnpkg%2Ffslib%2Fdownload%2F%40yarnpkg%2Ffslib-2.1.0.tgz"], - ["@yarnpkg/parsers", "npm:2.1.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40yarnpkg%2Fparsers%2Fdownload%2F%40yarnpkg%2Fparsers-2.1.0.tgz"], + ["@yarnpkg/pnpify", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:2.2.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40yarnpkg%2Fpnpify%2Fdownload%2F%40yarnpkg%2Fpnpify-2.2.1.tgz"], + ["@yarnpkg/core", "npm:2.2.2::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40yarnpkg%2Fcore%2Fdownload%2F%40yarnpkg%2Fcore-2.2.2.tgz"], + ["@yarnpkg/fslib", "npm:2.2.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40yarnpkg%2Ffslib%2Fdownload%2F%40yarnpkg%2Ffslib-2.2.1.tgz"], + ["@yarnpkg/parsers", "npm:2.2.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40yarnpkg%2Fparsers%2Fdownload%2F%40yarnpkg%2Fparsers-2.2.0.tgz"], ["chalk", "npm:3.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fchalk%2Fdownload%2Fchalk-3.0.0.tgz"], - ["clipanion", "npm:2.4.2::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fclipanion%2Fdownload%2Fclipanion-2.4.2.tgz"], + ["clipanion", "npm:2.5.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fclipanion%2Fdownload%2Fclipanion-2.5.0.tgz"], ["comment-json", "npm:2.4.2::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fcomment-json%2Fdownload%2Fcomment-json-2.4.2.tgz"], ["eslint", "npm:7.7.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Feslint%2Fdownload%2Feslint-7.7.0.tgz"], ["lodash", "npm:4.17.19::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Flodash%2Fdownload%2Flodash-4.17.19.tgz"], @@ -3996,13 +4019,13 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { }] ]], ["@yarnpkg/shell", [ - ["npm:2.1.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40yarnpkg%2Fshell%2Fdownload%2F%40yarnpkg%2Fshell-2.1.0.tgz", { - "packageLocation": "./.yarn/cache/@yarnpkg-shell-npm-2.1.0-4bd865f0b3-2e0b808965.zip/node_modules/@yarnpkg/shell/", + ["npm:2.2.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40yarnpkg%2Fshell%2Fdownload%2F%40yarnpkg%2Fshell-2.2.0.tgz", { + "packageLocation": "./.yarn/cache/@yarnpkg-shell-npm-2.2.0-7709e1bff2-23a26955ca.zip/node_modules/@yarnpkg/shell/", "packageDependencies": [ - ["@yarnpkg/shell", "npm:2.1.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40yarnpkg%2Fshell%2Fdownload%2F%40yarnpkg%2Fshell-2.1.0.tgz"], - ["@yarnpkg/fslib", "npm:2.1.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40yarnpkg%2Ffslib%2Fdownload%2F%40yarnpkg%2Ffslib-2.1.0.tgz"], - ["@yarnpkg/parsers", "npm:2.1.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40yarnpkg%2Fparsers%2Fdownload%2F%40yarnpkg%2Fparsers-2.1.0.tgz"], - ["clipanion", "npm:2.4.2::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fclipanion%2Fdownload%2Fclipanion-2.4.2.tgz"], + ["@yarnpkg/shell", "npm:2.2.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40yarnpkg%2Fshell%2Fdownload%2F%40yarnpkg%2Fshell-2.2.0.tgz"], + ["@yarnpkg/fslib", "npm:2.2.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40yarnpkg%2Ffslib%2Fdownload%2F%40yarnpkg%2Ffslib-2.2.1.tgz"], + ["@yarnpkg/parsers", "npm:2.2.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40yarnpkg%2Fparsers%2Fdownload%2F%40yarnpkg%2Fparsers-2.2.0.tgz"], + ["clipanion", "npm:2.5.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fclipanion%2Fdownload%2Fclipanion-2.5.0.tgz"], ["cross-spawn", "npm:7.0.3::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fcross-spawn%2Fdownload%2Fcross-spawn-7.0.3.tgz"], ["fast-glob", "npm:3.2.4::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Ffast-glob%2Fdownload%2Ffast-glob-3.2.4.tgz"], ["stream-buffers", "npm:3.0.2::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fstream-buffers%2Fdownload%2Fstream-buffers-3.0.2.tgz"], @@ -4082,6 +4105,17 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["uri-js", "npm:4.2.2::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Furi-js%2Fdownload%2Furi-js-4.2.2.tgz"] ], "linkType": "HARD", + }], + ["npm:6.12.4::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fajv%2Fdownload%2Fajv-6.12.4.tgz", { + "packageLocation": "./.yarn/cache/ajv-npm-6.12.4-1badbf9de4-50d72b0a10.zip/node_modules/ajv/", + "packageDependencies": [ + ["ajv", "npm:6.12.4::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fajv%2Fdownload%2Fajv-6.12.4.tgz"], + ["fast-deep-equal", "npm:3.1.3::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Ffast-deep-equal%2Fdownload%2Ffast-deep-equal-3.1.3.tgz"], + ["fast-json-stable-stringify", "npm:2.1.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Ffast-json-stable-stringify%2Fdownload%2Ffast-json-stable-stringify-2.1.0.tgz"], + ["json-schema-traverse", "npm:0.4.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fjson-schema-traverse%2Fdownload%2Fjson-schema-traverse-0.4.1.tgz"], + ["uri-js", "npm:4.2.2::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Furi-js%2Fdownload%2Furi-js-4.2.2.tgz"] + ], + "linkType": "HARD", }] ]], ["ajv-errors", [ @@ -4108,6 +4142,17 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "ajv" ], "linkType": "HARD", + }], + ["virtual:a06248938e0ae7b71c42d5ddc8d214f9ba6aee0ce451a00cc4d8908fd63a4496f9718e410c3ffbf3ba671de80b30ca2589060c47926cf21323ff14088d009d8a#npm:3.5.2::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fajv-keywords%2Fdownload%2Fajv-keywords-3.5.2.tgz", { + "packageLocation": "./.yarn/$$virtual/ajv-keywords-virtual-ee860929c8/0/cache/ajv-keywords-npm-3.5.2-c36e2f7ee0-01f26c2923.zip/node_modules/ajv-keywords/", + "packageDependencies": [ + ["ajv-keywords", "virtual:a06248938e0ae7b71c42d5ddc8d214f9ba6aee0ce451a00cc4d8908fd63a4496f9718e410c3ffbf3ba671de80b30ca2589060c47926cf21323ff14088d009d8a#npm:3.5.2::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fajv-keywords%2Fdownload%2Fajv-keywords-3.5.2.tgz"], + ["ajv", "npm:6.12.4::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fajv%2Fdownload%2Fajv-6.12.4.tgz"] + ], + "packagePeers": [ + "ajv" + ], + "linkType": "HARD", }] ]], ["ansi-colors", [ @@ -4779,6 +4824,18 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], + ["bl", [ + ["npm:4.0.3::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fbl%2Fdownload%2Fbl-4.0.3.tgz", { + "packageLocation": "./.yarn/cache/bl-npm-4.0.3-01d162e53b-1f33c5a3da.zip/node_modules/bl/", + "packageDependencies": [ + ["bl", "npm:4.0.3::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fbl%2Fdownload%2Fbl-4.0.3.tgz"], + ["buffer", "npm:5.6.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fbuffer%2Fdownload%2Fbuffer-5.6.0.tgz"], + ["inherits", "npm:2.0.4::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Finherits%2Fdownload%2Finherits-2.0.4.tgz"], + ["readable-stream", "npm:3.6.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Freadable-stream%2Fdownload%2Freadable-stream-3.6.0.tgz"] + ], + "linkType": "HARD", + }] + ]], ["bluebird", [ ["npm:3.7.2::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fbluebird%2Fdownload%2Fbluebird-3.7.2.tgz", { "packageLocation": "./.yarn/cache/bluebird-npm-3.7.2-6889014ddf-4f2288662f.zip/node_modules/bluebird/", @@ -4863,10 +4920,10 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { }] ]], ["bootstrap-icons", [ - ["npm:1.0.0-alpha5::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fbootstrap-icons%2Fdownload%2Fbootstrap-icons-1.0.0-alpha5.tgz", { - "packageLocation": "./.yarn/cache/bootstrap-icons-npm-1.0.0-alpha5-6b83807199-a97c110bc9.zip/node_modules/bootstrap-icons/", + ["npm:1.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fbootstrap-icons%2Fdownload%2Fbootstrap-icons-1.0.0.tgz", { + "packageLocation": "./.yarn/cache/bootstrap-icons-npm-1.0.0-b1f8003214-082d8d2aff.zip/node_modules/bootstrap-icons/", "packageDependencies": [ - ["bootstrap-icons", "npm:1.0.0-alpha5::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fbootstrap-icons%2Fdownload%2Fbootstrap-icons-1.0.0-alpha5.tgz"] + ["bootstrap-icons", "npm:1.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fbootstrap-icons%2Fdownload%2Fbootstrap-icons-1.0.0.tgz"] ], "linkType": "HARD", }] @@ -5031,6 +5088,15 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["isarray", "npm:1.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fisarray%2Fdownload%2Fisarray-1.0.0.tgz"] ], "linkType": "HARD", + }], + ["npm:5.6.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fbuffer%2Fdownload%2Fbuffer-5.6.0.tgz", { + "packageLocation": "./.yarn/cache/buffer-npm-5.6.0-48010737b7-e18fdf099c.zip/node_modules/buffer/", + "packageDependencies": [ + ["buffer", "npm:5.6.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fbuffer%2Fdownload%2Fbuffer-5.6.0.tgz"], + ["base64-js", "npm:1.3.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fbase64-js%2Fdownload%2Fbase64-js-1.3.1.tgz"], + ["ieee754", "npm:1.1.13::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fieee754%2Fdownload%2Fieee754-1.1.13.tgz"] + ], + "linkType": "HARD", }] ]], ["buffer-from", [ @@ -5454,10 +5520,10 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { }] ]], ["clipanion", [ - ["npm:2.4.2::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fclipanion%2Fdownload%2Fclipanion-2.4.2.tgz", { - "packageLocation": "./.yarn/cache/clipanion-npm-2.4.2-cb5dd08c9d-4a8aa1c126.zip/node_modules/clipanion/", + ["npm:2.5.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fclipanion%2Fdownload%2Fclipanion-2.5.0.tgz", { + "packageLocation": "./.yarn/cache/clipanion-npm-2.5.0-434a40cc1a-700ff428c9.zip/node_modules/clipanion/", "packageDependencies": [ - ["clipanion", "npm:2.4.2::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fclipanion%2Fdownload%2Fclipanion-2.4.2.tgz"] + ["clipanion", "npm:2.5.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fclipanion%2Fdownload%2Fclipanion-2.5.0.tgz"] ], "linkType": "HARD", }] @@ -5796,10 +5862,10 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { }] ]], ["copy-webpack-plugin", [ - ["virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:6.0.3::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fcopy-webpack-plugin%2Fdownload%2Fcopy-webpack-plugin-6.0.3.tgz", { - "packageLocation": "./.yarn/$$virtual/copy-webpack-plugin-virtual-b826aabc59/0/cache/copy-webpack-plugin-npm-6.0.3-64a6b9536d-02bb71e9ad.zip/node_modules/copy-webpack-plugin/", + ["virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:6.1.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fcopy-webpack-plugin%2Fdownload%2Fcopy-webpack-plugin-6.1.0.tgz", { + "packageLocation": "./.yarn/$$virtual/copy-webpack-plugin-virtual-060b35b0fd/0/cache/copy-webpack-plugin-npm-6.1.0-17dfb1f28f-bb289324cd.zip/node_modules/copy-webpack-plugin/", "packageDependencies": [ - ["copy-webpack-plugin", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:6.0.3::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fcopy-webpack-plugin%2Fdownload%2Fcopy-webpack-plugin-6.0.3.tgz"], + ["copy-webpack-plugin", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:6.1.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fcopy-webpack-plugin%2Fdownload%2Fcopy-webpack-plugin-6.1.0.tgz"], ["cacache", "npm:15.0.5::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fcacache%2Fdownload%2Fcacache-15.0.5.tgz"], ["fast-glob", "npm:3.2.4::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Ffast-glob%2Fdownload%2Ffast-glob-3.2.4.tgz"], ["find-cache-dir", "npm:3.3.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Ffind-cache-dir%2Fdownload%2Ffind-cache-dir-3.3.1.tgz"], @@ -5808,7 +5874,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["loader-utils", "npm:2.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Floader-utils%2Fdownload%2Floader-utils-2.0.0.tgz"], ["normalize-path", "npm:3.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fnormalize-path%2Fdownload%2Fnormalize-path-3.0.0.tgz"], ["p-limit", "npm:3.0.2::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fp-limit%2Fdownload%2Fp-limit-3.0.2.tgz"], - ["schema-utils", "npm:2.7.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fschema-utils%2Fdownload%2Fschema-utils-2.7.0.tgz"], + ["schema-utils", "npm:2.7.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fschema-utils%2Fdownload%2Fschema-utils-2.7.1.tgz"], ["serialize-javascript", "npm:4.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fserialize-javascript%2Fdownload%2Fserialize-javascript-4.0.0.tgz"], ["webpack", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:4.44.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fwebpack%2Fdownload%2Fwebpack-4.44.1.tgz"], ["webpack-sources", "npm:1.4.3::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fwebpack-sources%2Fdownload%2Fwebpack-sources-1.4.3.tgz"] @@ -6027,15 +6093,14 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { }] ]], ["css-loader", [ - ["virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:4.2.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fcss-loader%2Fdownload%2Fcss-loader-4.2.1.tgz", { - "packageLocation": "./.yarn/$$virtual/css-loader-virtual-217c7c2352/0/cache/css-loader-npm-4.2.1-093499999a-bd8188efac.zip/node_modules/css-loader/", + ["virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:4.2.2::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fcss-loader%2Fdownload%2Fcss-loader-4.2.2.tgz", { + "packageLocation": "./.yarn/$$virtual/css-loader-virtual-b969058b2f/0/cache/css-loader-npm-4.2.2-f0d86c1900-73d83e7f4f.zip/node_modules/css-loader/", "packageDependencies": [ - ["css-loader", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:4.2.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fcss-loader%2Fdownload%2Fcss-loader-4.2.1.tgz"], + ["css-loader", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:4.2.2::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fcss-loader%2Fdownload%2Fcss-loader-4.2.2.tgz"], ["camelcase", "npm:6.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fcamelcase%2Fdownload%2Fcamelcase-6.0.0.tgz"], ["cssesc", "npm:3.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fcssesc%2Fdownload%2Fcssesc-3.0.0.tgz"], ["icss-utils", "npm:4.1.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Ficss-utils%2Fdownload%2Ficss-utils-4.1.1.tgz"], ["loader-utils", "npm:2.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Floader-utils%2Fdownload%2Floader-utils-2.0.0.tgz"], - ["normalize-path", "npm:3.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fnormalize-path%2Fdownload%2Fnormalize-path-3.0.0.tgz"], ["postcss", "npm:7.0.32::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fpostcss%2Fdownload%2Fpostcss-7.0.32.tgz"], ["postcss-modules-extract-imports", "npm:2.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fpostcss-modules-extract-imports%2Fdownload%2Fpostcss-modules-extract-imports-2.0.0.tgz"], ["postcss-modules-local-by-default", "npm:3.0.3::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fpostcss-modules-local-by-default%2Fdownload%2Fpostcss-modules-local-by-default-3.0.3.tgz"], @@ -7014,7 +7079,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "packageDependencies": [ ["eslint-plugin-prettier", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:3.1.4::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Feslint-plugin-prettier%2Fdownload%2Feslint-plugin-prettier-3.1.4.tgz"], ["eslint", "npm:7.7.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Feslint%2Fdownload%2Feslint-7.7.0.tgz"], - ["prettier", "npm:2.1.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fprettier%2Fdownload%2Fprettier-2.1.0.tgz"], + ["prettier", "npm:2.1.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fprettier%2Fdownload%2Fprettier-2.1.1.tgz"], ["prettier-linter-helpers", "npm:1.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fprettier-linter-helpers%2Fdownload%2Fprettier-linter-helpers-1.0.0.tgz"] ], "packagePeers": [ @@ -7098,6 +7163,13 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["eslint-visitor-keys", "npm:1.3.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Feslint-visitor-keys%2Fdownload%2Feslint-visitor-keys-1.3.0.tgz"] ], "linkType": "HARD", + }], + ["npm:2.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Feslint-visitor-keys%2Fdownload%2Feslint-visitor-keys-2.0.0.tgz", { + "packageLocation": "./.yarn/cache/eslint-visitor-keys-npm-2.0.0-f733a5e58d-429dabdcab.zip/node_modules/eslint-visitor-keys/", + "packageDependencies": [ + ["eslint-visitor-keys", "npm:2.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Feslint-visitor-keys%2Fdownload%2Feslint-visitor-keys-2.0.0.tgz"] + ], + "linkType": "HARD", }] ]], ["espree", [ @@ -7474,12 +7546,12 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { }] ]], ["file-loader", [ - ["virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:6.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Ffile-loader%2Fdownload%2Ffile-loader-6.0.0.tgz", { - "packageLocation": "./.yarn/$$virtual/file-loader-virtual-9eddad773c/0/cache/file-loader-npm-6.0.0-d9747600a6-745f5ee763.zip/node_modules/file-loader/", + ["virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:6.1.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Ffile-loader%2Fdownload%2Ffile-loader-6.1.0.tgz", { + "packageLocation": "./.yarn/$$virtual/file-loader-virtual-56ec20141f/0/cache/file-loader-npm-6.1.0-77056709ee-ad930c9e7f.zip/node_modules/file-loader/", "packageDependencies": [ - ["file-loader", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:6.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Ffile-loader%2Fdownload%2Ffile-loader-6.0.0.tgz"], + ["file-loader", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:6.1.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Ffile-loader%2Fdownload%2Ffile-loader-6.1.0.tgz"], ["loader-utils", "npm:2.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Floader-utils%2Fdownload%2Floader-utils-2.0.0.tgz"], - ["schema-utils", "npm:2.7.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fschema-utils%2Fdownload%2Fschema-utils-2.7.0.tgz"], + ["schema-utils", "npm:2.7.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fschema-utils%2Fdownload%2Fschema-utils-2.7.1.tgz"], ["webpack", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:4.44.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fwebpack%2Fdownload%2Fwebpack-4.44.1.tgz"] ], "packagePeers": [ @@ -7732,6 +7804,15 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], + ["fs-constants", [ + ["npm:1.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Ffs-constants%2Fdownload%2Ffs-constants-1.0.0.tgz", { + "packageLocation": "./.yarn/cache/fs-constants-npm-1.0.0-dd82a48008-b8382395f5.zip/node_modules/fs-constants/", + "packageDependencies": [ + ["fs-constants", "npm:1.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Ffs-constants%2Fdownload%2Ffs-constants-1.0.0.tgz"] + ], + "linkType": "HARD", + }] + ]], ["fs-extra", [ ["npm:8.1.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Ffs-extra%2Fdownload%2Ffs-extra-8.1.0.tgz", { "packageLocation": "./.yarn/cache/fs-extra-npm-8.1.0-9656618adb-056a96d4f5.zip/node_modules/fs-extra/", @@ -7745,14 +7826,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { }] ]], ["fs-minipass", [ - ["npm:1.2.7::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Ffs-minipass%2Fdownload%2Ffs-minipass-1.2.7.tgz", { - "packageLocation": "./.yarn/cache/fs-minipass-npm-1.2.7-21b70badcf-eb59a93065.zip/node_modules/fs-minipass/", - "packageDependencies": [ - ["fs-minipass", "npm:1.2.7::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Ffs-minipass%2Fdownload%2Ffs-minipass-1.2.7.tgz"], - ["minipass", "npm:2.9.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fminipass%2Fdownload%2Fminipass-2.9.0.tgz"] - ], - "linkType": "HARD", - }], ["npm:2.1.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Ffs-minipass%2Fdownload%2Ffs-minipass-2.1.0.tgz", { "packageLocation": "./.yarn/cache/fs-minipass-npm-2.1.0-b5d5a6c13e-e14a490658.zip/node_modules/fs-minipass/", "packageDependencies": [ @@ -8025,21 +8098,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { }] ]], ["globby", [ - ["npm:10.0.2::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fglobby%2Fdownload%2Fglobby-10.0.2.tgz", { - "packageLocation": "./.yarn/cache/globby-npm-10.0.2-47410f9660-53924c2b46.zip/node_modules/globby/", - "packageDependencies": [ - ["globby", "npm:10.0.2::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fglobby%2Fdownload%2Fglobby-10.0.2.tgz"], - ["@types/glob", "npm:7.1.3::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Fglob%2Fdownload%2F%40types%2Fglob-7.1.3.tgz"], - ["array-union", "npm:2.1.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Farray-union%2Fdownload%2Farray-union-2.1.0.tgz"], - ["dir-glob", "npm:3.0.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fdir-glob%2Fdownload%2Fdir-glob-3.0.1.tgz"], - ["fast-glob", "npm:3.2.4::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Ffast-glob%2Fdownload%2Ffast-glob-3.2.4.tgz"], - ["glob", "npm:7.1.6::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fglob%2Fdownload%2Fglob-7.1.6.tgz"], - ["ignore", "npm:5.1.8::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fignore%2Fdownload%2Fignore-5.1.8.tgz"], - ["merge2", "npm:1.4.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fmerge2%2Fdownload%2Fmerge2-1.4.1.tgz"], - ["slash", "npm:3.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fslash%2Fdownload%2Fslash-3.0.0.tgz"] - ], - "linkType": "HARD", - }], ["npm:11.0.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fglobby%2Fdownload%2Fglobby-11.0.1.tgz", { "packageLocation": "./.yarn/cache/globby-npm-11.0.1-75d4900c36-e7239e9e46.zip/node_modules/globby/", "packageDependencies": [ @@ -8382,10 +8440,10 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { }] ]], ["html-webpack-plugin", [ - ["virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:4.3.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fhtml-webpack-plugin%2Fdownload%2Fhtml-webpack-plugin-4.3.0.tgz", { - "packageLocation": "./.yarn/$$virtual/html-webpack-plugin-virtual-15a3a0f996/0/cache/html-webpack-plugin-npm-4.3.0-295e66dc16-13c23547ac.zip/node_modules/html-webpack-plugin/", + ["virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:4.4.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fhtml-webpack-plugin%2Fdownload%2Fhtml-webpack-plugin-4.4.1.tgz", { + "packageLocation": "./.yarn/$$virtual/html-webpack-plugin-virtual-44e518319d/0/cache/html-webpack-plugin-npm-4.4.1-4f918b1bea-5bf56a8c24.zip/node_modules/html-webpack-plugin/", "packageDependencies": [ - ["html-webpack-plugin", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:4.3.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fhtml-webpack-plugin%2Fdownload%2Fhtml-webpack-plugin-4.3.0.tgz"], + ["html-webpack-plugin", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:4.4.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fhtml-webpack-plugin%2Fdownload%2Fhtml-webpack-plugin-4.4.1.tgz"], ["@types/html-minifier-terser", "npm:5.1.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Fhtml-minifier-terser%2Fdownload%2F%40types%2Fhtml-minifier-terser-5.1.0.tgz"], ["@types/tapable", "npm:1.0.6::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Ftapable%2Fdownload%2F%40types%2Ftapable-1.0.6.tgz"], ["@types/webpack", "npm:4.41.21::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Fwebpack%2Fdownload%2F%40types%2Fwebpack-4.41.21.tgz"], @@ -9510,10 +9568,10 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { }] ]], ["klona", [ - ["npm:1.1.2::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fklona%2Fdownload%2Fklona-1.1.2.tgz", { - "packageLocation": "./.yarn/cache/klona-npm-1.1.2-1ab33bab4c-357ff7d43c.zip/node_modules/klona/", + ["npm:2.0.3::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fklona%2Fdownload%2Fklona-2.0.3.tgz", { + "packageLocation": "./.yarn/cache/klona-npm-2.0.3-fb634e77f8-69df085420.zip/node_modules/klona/", "packageDependencies": [ - ["klona", "npm:1.1.2::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fklona%2Fdownload%2Fklona-1.1.2.tgz"] + ["klona", "npm:2.0.3::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fklona%2Fdownload%2Fklona-2.0.3.tgz"] ], "linkType": "HARD", }] @@ -10015,10 +10073,10 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { }] ]], ["mini-css-extract-plugin", [ - ["virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:0.10.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fmini-css-extract-plugin%2Fdownload%2Fmini-css-extract-plugin-0.10.0.tgz", { - "packageLocation": "./.yarn/$$virtual/mini-css-extract-plugin-virtual-c1d40f1e6a/0/cache/mini-css-extract-plugin-npm-0.10.0-64c7df264f-2e133cdd20.zip/node_modules/mini-css-extract-plugin/", + ["virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:0.11.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fmini-css-extract-plugin%2Fdownload%2Fmini-css-extract-plugin-0.11.0.tgz", { + "packageLocation": "./.yarn/$$virtual/mini-css-extract-plugin-virtual-855e3b0d8a/0/cache/mini-css-extract-plugin-npm-0.11.0-9fa42d9c43-79a4a06527.zip/node_modules/mini-css-extract-plugin/", "packageDependencies": [ - ["mini-css-extract-plugin", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:0.10.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fmini-css-extract-plugin%2Fdownload%2Fmini-css-extract-plugin-0.10.0.tgz"], + ["mini-css-extract-plugin", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:0.11.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fmini-css-extract-plugin%2Fdownload%2Fmini-css-extract-plugin-0.11.0.tgz"], ["loader-utils", "npm:1.4.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Floader-utils%2Fdownload%2Floader-utils-1.4.0.tgz"], ["normalize-url", "npm:1.9.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fnormalize-url%2Fdownload%2Fnormalize-url-1.9.1.tgz"], ["schema-utils", "npm:1.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fschema-utils%2Fdownload%2Fschema-utils-1.0.0.tgz"], @@ -10069,15 +10127,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { }] ]], ["minipass", [ - ["npm:2.9.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fminipass%2Fdownload%2Fminipass-2.9.0.tgz", { - "packageLocation": "./.yarn/cache/minipass-npm-2.9.0-12d0efb52b-57a49f9523.zip/node_modules/minipass/", - "packageDependencies": [ - ["minipass", "npm:2.9.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fminipass%2Fdownload%2Fminipass-2.9.0.tgz"], - ["safe-buffer", "npm:5.2.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fsafe-buffer%2Fdownload%2Fsafe-buffer-5.2.1.tgz"], - ["yallist", "npm:3.1.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fyallist%2Fdownload%2Fyallist-3.1.1.tgz"] - ], - "linkType": "HARD", - }], ["npm:3.1.3::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fminipass%2Fdownload%2Fminipass-3.1.3.tgz", { "packageLocation": "./.yarn/cache/minipass-npm-3.1.3-eb45a369b1-d12b95a845.zip/node_modules/minipass/", "packageDependencies": [ @@ -10118,14 +10167,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { }] ]], ["minizlib", [ - ["npm:1.3.3::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fminizlib%2Fdownload%2Fminizlib-1.3.3.tgz", { - "packageLocation": "./.yarn/cache/minizlib-npm-1.3.3-8fc7aa14ba-8d12782dd9.zip/node_modules/minizlib/", - "packageDependencies": [ - ["minizlib", "npm:1.3.3::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fminizlib%2Fdownload%2Fminizlib-1.3.3.tgz"], - ["minipass", "npm:2.9.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fminipass%2Fdownload%2Fminipass-2.9.0.tgz"] - ], - "linkType": "HARD", - }], ["npm:2.1.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fminizlib%2Fdownload%2Fminizlib-2.1.0.tgz", { "packageLocation": "./.yarn/cache/minizlib-npm-2.1.0-0922541795-665346bad8.zip/node_modules/minizlib/", "packageDependencies": [ @@ -11784,10 +11825,10 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { }] ]], ["prettier", [ - ["npm:2.1.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fprettier%2Fdownload%2Fprettier-2.1.0.tgz", { - "packageLocation": "./.yarn/cache/prettier-npm-2.1.0-55e4625555-f955d380d2.zip/node_modules/prettier/", + ["npm:2.1.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fprettier%2Fdownload%2Fprettier-2.1.1.tgz", { + "packageLocation": "./.yarn/cache/prettier-npm-2.1.1-742423e320-420a807cda.zip/node_modules/prettier/", "packageDependencies": [ - ["prettier", "npm:2.1.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fprettier%2Fdownload%2Fprettier-2.1.0.tgz"] + ["prettier", "npm:2.1.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fprettier%2Fdownload%2Fprettier-2.1.1.tgz"] ], "linkType": "HARD", }] @@ -12160,7 +12201,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "packageLocation": "./.yarn/$$virtual/react-hot-loader-virtual-03e83e357e/0/cache/react-hot-loader-npm-4.12.21-5b5b8661aa-371deabe49.zip/node_modules/react-hot-loader/", "packageDependencies": [ ["react-hot-loader", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:4.12.21::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Freact-hot-loader%2Fdownload%2Freact-hot-loader-4.12.21.tgz"], - ["@types/react", "npm:16.9.46::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Freact%2Fdownload%2F%40types%2Freact-16.9.46.tgz"], + ["@types/react", "npm:16.9.49::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Freact%2Fdownload%2F%40types%2Freact-16.9.49.tgz"], ["fast-levenshtein", "npm:2.0.6::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Ffast-levenshtein%2Fdownload%2Ffast-levenshtein-2.0.6.tgz"], ["global", "npm:4.4.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fglobal%2Fdownload%2Fglobal-4.4.0.tgz"], ["hoist-non-react-statics", "npm:3.3.2::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fhoist-non-react-statics%2Fdownload%2Fhoist-non-react-statics-3.3.2.tgz"], @@ -12181,10 +12222,10 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { }] ]], ["react-i18next", [ - ["virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:11.7.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Freact-i18next%2Fdownload%2Freact-i18next-11.7.1.tgz", { - "packageLocation": "./.yarn/$$virtual/react-i18next-virtual-b9c7e9253e/0/cache/react-i18next-npm-11.7.1-9665be9025-fc62ae35c3.zip/node_modules/react-i18next/", + ["virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:11.7.2::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Freact-i18next%2Fdownload%2Freact-i18next-11.7.2.tgz", { + "packageLocation": "./.yarn/$$virtual/react-i18next-virtual-092e49876b/0/cache/react-i18next-npm-11.7.2-db79043177-ea5c989d1b.zip/node_modules/react-i18next/", "packageDependencies": [ - ["react-i18next", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:11.7.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Freact-i18next%2Fdownload%2Freact-i18next-11.7.1.tgz"], + ["react-i18next", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:11.7.2::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Freact-i18next%2Fdownload%2Freact-i18next-11.7.2.tgz"], ["@babel/runtime", "npm:7.10.5::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fruntime%2Fdownload%2F%40babel%2Fruntime-7.10.5.tgz"], ["html-parse-stringify2", "npm:2.0.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fhtml-parse-stringify2%2Fdownload%2Fhtml-parse-stringify2-2.0.1.tgz"], ["i18next", "npm:19.7.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fi18next%2Fdownload%2Fi18next-19.7.0.tgz"], @@ -12921,12 +12962,12 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { }] ]], ["sass-loader", [ - ["virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:9.0.3::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fsass-loader%2Fdownload%2Fsass-loader-9.0.3.tgz", { - "packageLocation": "./.yarn/$$virtual/sass-loader-virtual-b3384a0ffc/0/cache/sass-loader-npm-9.0.3-681753a244-5ce94bb670.zip/node_modules/sass-loader/", + ["virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:10.0.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fsass-loader%2Fdownload%2Fsass-loader-10.0.1.tgz", { + "packageLocation": "./.yarn/$$virtual/sass-loader-virtual-8d4a3f636b/0/cache/sass-loader-npm-10.0.1-7886e9cb40-cc517f3dae.zip/node_modules/sass-loader/", "packageDependencies": [ - ["sass-loader", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:9.0.3::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fsass-loader%2Fdownload%2Fsass-loader-9.0.3.tgz"], + ["sass-loader", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:10.0.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fsass-loader%2Fdownload%2Fsass-loader-10.0.1.tgz"], ["fibers", null], - ["klona", "npm:1.1.2::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fklona%2Fdownload%2Fklona-1.1.2.tgz"], + ["klona", "npm:2.0.3::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fklona%2Fdownload%2Fklona-2.0.3.tgz"], ["loader-utils", "npm:2.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Floader-utils%2Fdownload%2Floader-utils-2.0.0.tgz"], ["neo-async", "npm:2.6.2::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fneo-async%2Fdownload%2Fneo-async-2.6.2.tgz"], ["node-sass", null], @@ -12975,6 +13016,16 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["ajv-keywords", "virtual:5a053ed7a5aaf24feb20a5f221f0cf99cd71c398eae5cd964b19ff1321e1f4b929d5643e1ddc2a0e5bf7584bf5da2ecbf8ea504506a83c90ac358cd78a8d6ec8#npm:3.5.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fajv-keywords%2Fdownload%2Fajv-keywords-3.5.1.tgz"] ], "linkType": "HARD", + }], + ["npm:2.7.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fschema-utils%2Fdownload%2Fschema-utils-2.7.1.tgz", { + "packageLocation": "./.yarn/cache/schema-utils-npm-2.7.1-a06248938e-3851bcc7e4.zip/node_modules/schema-utils/", + "packageDependencies": [ + ["schema-utils", "npm:2.7.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fschema-utils%2Fdownload%2Fschema-utils-2.7.1.tgz"], + ["@types/json-schema", "npm:7.0.5::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Fjson-schema%2Fdownload%2F%40types%2Fjson-schema-7.0.5.tgz"], + ["ajv", "npm:6.12.4::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fajv%2Fdownload%2Fajv-6.12.4.tgz"], + ["ajv-keywords", "virtual:a06248938e0ae7b71c42d5ddc8d214f9ba6aee0ce451a00cc4d8908fd63a4496f9718e410c3ffbf3ba671de80b30ca2589060c47926cf21323ff14088d009d8a#npm:3.5.2::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fajv-keywords%2Fdownload%2Fajv-keywords-3.5.2.tgz"] + ], + "linkType": "HARD", }] ]], ["secure-compare", [ @@ -13873,20 +13924,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { }] ]], ["tar", [ - ["npm:4.4.13::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Ftar%2Fdownload%2Ftar-4.4.13.tgz", { - "packageLocation": "./.yarn/cache/tar-npm-4.4.13-0a714d6118-d325c316ac.zip/node_modules/tar/", - "packageDependencies": [ - ["tar", "npm:4.4.13::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Ftar%2Fdownload%2Ftar-4.4.13.tgz"], - ["chownr", "npm:1.1.4::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fchownr%2Fdownload%2Fchownr-1.1.4.tgz"], - ["fs-minipass", "npm:1.2.7::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Ffs-minipass%2Fdownload%2Ffs-minipass-1.2.7.tgz"], - ["minipass", "npm:2.9.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fminipass%2Fdownload%2Fminipass-2.9.0.tgz"], - ["minizlib", "npm:1.3.3::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fminizlib%2Fdownload%2Fminizlib-1.3.3.tgz"], - ["mkdirp", "npm:0.5.5::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fmkdirp%2Fdownload%2Fmkdirp-0.5.5.tgz"], - ["safe-buffer", "npm:5.2.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fsafe-buffer%2Fdownload%2Fsafe-buffer-5.2.1.tgz"], - ["yallist", "npm:3.1.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fyallist%2Fdownload%2Fyallist-3.1.1.tgz"] - ], - "linkType": "HARD", - }], ["npm:6.0.2::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Ftar%2Fdownload%2Ftar-6.0.2.tgz", { "packageLocation": "./.yarn/cache/tar-npm-6.0.2-c08102c075-7d28cc13d7.zip/node_modules/tar/", "packageDependencies": [ @@ -13901,6 +13938,20 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], + ["tar-stream", [ + ["npm:2.1.3::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Ftar-stream%2Fdownload%2Ftar-stream-2.1.3.tgz", { + "packageLocation": "./.yarn/cache/tar-stream-npm-2.1.3-d6be928e6e-57d2284615.zip/node_modules/tar-stream/", + "packageDependencies": [ + ["tar-stream", "npm:2.1.3::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Ftar-stream%2Fdownload%2Ftar-stream-2.1.3.tgz"], + ["bl", "npm:4.0.3::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fbl%2Fdownload%2Fbl-4.0.3.tgz"], + ["end-of-stream", "npm:1.4.4::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fend-of-stream%2Fdownload%2Fend-of-stream-1.4.4.tgz"], + ["fs-constants", "npm:1.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Ffs-constants%2Fdownload%2Ffs-constants-1.0.0.tgz"], + ["inherits", "npm:2.0.4::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Finherits%2Fdownload%2Finherits-2.0.4.tgz"], + ["readable-stream", "npm:3.6.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Freadable-stream%2Fdownload%2Freadable-stream-3.6.0.tgz"] + ], + "linkType": "HARD", + }] + ]], ["temp-dir", [ ["npm:1.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Ftemp-dir%2Fdownload%2Ftemp-dir-1.0.0.tgz", { "packageLocation": "./.yarn/cache/temp-dir-npm-1.0.0-31731478b9-4cc703b6ac.zip/node_modules/temp-dir/", @@ -14002,9 +14053,9 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["@hot-loader/react-dom", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:16.13.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40hot-loader%2Freact-dom%2Fdownload%2F%40hot-loader%2Freact-dom-16.13.0.tgz"], ["@types/classnames", "npm:2.2.10::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Fclassnames%2Fdownload%2F%40types%2Fclassnames-2.2.10.tgz"], ["@types/crypto-js", "npm:3.1.47::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Fcrypto-js%2Fdownload%2F%40types%2Fcrypto-js-3.1.47.tgz"], - ["@types/lodash", "npm:4.14.159::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Flodash%2Fdownload%2F%40types%2Flodash-4.14.159.tgz"], - ["@types/node", "npm:14.6.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Fnode%2Fdownload%2F%40types%2Fnode-14.6.0.tgz"], - ["@types/react", "npm:16.9.46::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Freact%2Fdownload%2F%40types%2Freact-16.9.46.tgz"], + ["@types/lodash", "npm:4.14.161::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Flodash%2Fdownload%2F%40types%2Flodash-4.14.161.tgz"], + ["@types/node", "npm:14.6.2::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Fnode%2Fdownload%2F%40types%2Fnode-14.6.2.tgz"], + ["@types/react", "npm:16.9.49::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Freact%2Fdownload%2F%40types%2Freact-16.9.49.tgz"], ["@types/react-dom", "npm:16.9.8::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Freact-dom%2Fdownload%2F%40types%2Freact-dom-16.9.8.tgz"], ["@types/react-responsive", "npm:8.0.2::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Freact-responsive%2Fdownload%2F%40types%2Freact-responsive-8.0.2.tgz"], ["@types/react-router", "npm:5.1.8::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Freact-router%2Fdownload%2F%40types%2Freact-router-5.1.8.tgz"], @@ -14012,21 +14063,21 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["@types/reactstrap", "npm:8.5.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Freactstrap%2Fdownload%2F%40types%2Freactstrap-8.5.1.tgz"], ["@types/webpack-env", "npm:1.15.2::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Fwebpack-env%2Fdownload%2F%40types%2Fwebpack-env-1.15.2.tgz"], ["@types/xregexp", "npm:4.3.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Fxregexp%2Fdownload%2F%40types%2Fxregexp-4.3.0.tgz"], - ["@typescript-eslint/eslint-plugin", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:3.9.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40typescript-eslint%2Feslint-plugin%2Fdownload%2F%40typescript-eslint%2Feslint-plugin-3.9.1.tgz"], - ["@typescript-eslint/parser", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:3.9.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40typescript-eslint%2Fparser%2Fdownload%2F%40typescript-eslint%2Fparser-3.9.1.tgz"], - ["@yarnpkg/pnpify", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:2.1.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40yarnpkg%2Fpnpify%2Fdownload%2F%40yarnpkg%2Fpnpify-2.1.0.tgz"], + ["@typescript-eslint/eslint-plugin", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:4.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40typescript-eslint%2Feslint-plugin%2Fdownload%2F%40typescript-eslint%2Feslint-plugin-4.0.0.tgz"], + ["@typescript-eslint/parser", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:4.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40typescript-eslint%2Fparser%2Fdownload%2F%40typescript-eslint%2Fparser-4.0.0.tgz"], + ["@yarnpkg/pnpify", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:2.2.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40yarnpkg%2Fpnpify%2Fdownload%2F%40yarnpkg%2Fpnpify-2.2.1.tgz"], ["axios", "npm:0.20.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Faxios%2Fdownload%2Faxios-0.20.0.tgz"], ["babel-loader", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:8.1.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fbabel-loader%2Fdownload%2Fbabel-loader-8.1.0.tgz"], ["babel-plugin-transform-builtin-extend", "npm:1.1.2::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fbabel-plugin-transform-builtin-extend%2Fdownload%2Fbabel-plugin-transform-builtin-extend-1.1.2.tgz"], ["bootstrap", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:4.5.2::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fbootstrap%2Fdownload%2Fbootstrap-4.5.2.tgz"], - ["bootstrap-icons", "npm:1.0.0-alpha5::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fbootstrap-icons%2Fdownload%2Fbootstrap-icons-1.0.0-alpha5.tgz"], + ["bootstrap-icons", "npm:1.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fbootstrap-icons%2Fdownload%2Fbootstrap-icons-1.0.0.tgz"], ["classnames", "npm:2.2.6::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fclassnames%2Fdownload%2Fclassnames-2.2.6.tgz"], ["clean-webpack-plugin", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:3.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fclean-webpack-plugin%2Fdownload%2Fclean-webpack-plugin-3.0.0.tgz"], ["clsx", "npm:1.1.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fclsx%2Fdownload%2Fclsx-1.1.1.tgz"], - ["copy-webpack-plugin", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:6.0.3::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fcopy-webpack-plugin%2Fdownload%2Fcopy-webpack-plugin-6.0.3.tgz"], + ["copy-webpack-plugin", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:6.1.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fcopy-webpack-plugin%2Fdownload%2Fcopy-webpack-plugin-6.1.0.tgz"], ["core-js", "npm:3.6.5::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fcore-js%2Fdownload%2Fcore-js-3.6.5.tgz"], ["crypto-js", "npm:4.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fcrypto-js%2Fdownload%2Fcrypto-js-4.0.0.tgz"], - ["css-loader", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:4.2.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fcss-loader%2Fdownload%2Fcss-loader-4.2.1.tgz"], + ["css-loader", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:4.2.2::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fcss-loader%2Fdownload%2Fcss-loader-4.2.2.tgz"], ["eslint", "npm:7.7.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Feslint%2Fdownload%2Feslint-7.7.0.tgz"], ["eslint-config-prettier", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:6.11.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Feslint-config-prettier%2Fdownload%2Feslint-config-prettier-6.11.0.tgz"], ["eslint-import-resolver-webpack", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:0.12.2::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Feslint-import-resolver-webpack%2Fdownload%2Feslint-import-resolver-webpack-0.12.2.tgz"], @@ -14034,23 +14085,23 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["eslint-plugin-prettier", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:3.1.4::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Feslint-plugin-prettier%2Fdownload%2Feslint-plugin-prettier-3.1.4.tgz"], ["eslint-plugin-react", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:7.20.6::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Feslint-plugin-react%2Fdownload%2Feslint-plugin-react-7.20.6.tgz"], ["eslint-plugin-react-hooks", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:4.1.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Feslint-plugin-react-hooks%2Fdownload%2Feslint-plugin-react-hooks-4.1.0.tgz"], - ["file-loader", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:6.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Ffile-loader%2Fdownload%2Ffile-loader-6.0.0.tgz"], - ["html-webpack-plugin", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:4.3.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fhtml-webpack-plugin%2Fdownload%2Fhtml-webpack-plugin-4.3.0.tgz"], + ["file-loader", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:6.1.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Ffile-loader%2Fdownload%2Ffile-loader-6.1.0.tgz"], + ["html-webpack-plugin", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:4.4.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fhtml-webpack-plugin%2Fdownload%2Fhtml-webpack-plugin-4.4.1.tgz"], ["http-server", "npm:0.12.3::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fhttp-server%2Fdownload%2Fhttp-server-0.12.3.tgz"], ["i18next", "npm:19.7.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fi18next%2Fdownload%2Fi18next-19.7.0.tgz"], ["i18next-browser-languagedetector", "npm:6.0.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fi18next-browser-languagedetector%2Fdownload%2Fi18next-browser-languagedetector-6.0.1.tgz"], ["localforage", "npm:1.9.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Flocalforage%2Fdownload%2Flocalforage-1.9.0.tgz"], ["lodash", "npm:4.17.20::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Flodash%2Fdownload%2Flodash-4.17.20.tgz"], - ["mini-css-extract-plugin", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:0.10.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fmini-css-extract-plugin%2Fdownload%2Fmini-css-extract-plugin-0.10.0.tgz"], + ["mini-css-extract-plugin", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:0.11.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fmini-css-extract-plugin%2Fdownload%2Fmini-css-extract-plugin-0.11.0.tgz"], ["pepjs", "npm:0.5.2::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fpepjs%2Fdownload%2Fpepjs-0.5.2.tgz"], ["pnp-webpack-plugin", "npm:1.6.4::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fpnp-webpack-plugin%2Fdownload%2Fpnp-webpack-plugin-1.6.4.tgz"], ["postcss-loader", "npm:3.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fpostcss-loader%2Fdownload%2Fpostcss-loader-3.0.0.tgz"], ["postcss-preset-env", "npm:6.7.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fpostcss-preset-env%2Fdownload%2Fpostcss-preset-env-6.7.0.tgz"], - ["prettier", "npm:2.1.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fprettier%2Fdownload%2Fprettier-2.1.0.tgz"], + ["prettier", "npm:2.1.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fprettier%2Fdownload%2Fprettier-2.1.1.tgz"], ["react", "npm:16.13.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Freact%2Fdownload%2Freact-16.13.1.tgz"], ["react-dom", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:16.13.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Freact-dom%2Fdownload%2Freact-dom-16.13.1.tgz"], ["react-hot-loader", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:4.12.21::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Freact-hot-loader%2Fdownload%2Freact-hot-loader-4.12.21.tgz"], - ["react-i18next", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:11.7.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Freact-i18next%2Fdownload%2Freact-i18next-11.7.1.tgz"], + ["react-i18next", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:11.7.2::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Freact-i18next%2Fdownload%2Freact-i18next-11.7.2.tgz"], ["react-inlinesvg", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:2.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Freact-inlinesvg%2Fdownload%2Freact-inlinesvg-2.0.0.tgz"], ["react-responsive", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:8.1.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Freact-responsive%2Fdownload%2Freact-responsive-8.1.0.tgz"], ["react-router", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:5.2.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Freact-router%2Fdownload%2Freact-router-5.2.0.tgz"], @@ -14059,7 +14110,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["regenerator-runtime", "npm:0.13.7::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fregenerator-runtime%2Fdownload%2Fregenerator-runtime-0.13.7.tgz"], ["rxjs", "npm:6.6.2::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Frxjs%2Fdownload%2Frxjs-6.6.2.tgz"], ["sass", "npm:1.26.10::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fsass%2Fdownload%2Fsass-1.26.10.tgz"], - ["sass-loader", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:9.0.3::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fsass-loader%2Fdownload%2Fsass-loader-9.0.3.tgz"], + ["sass-loader", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:10.0.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fsass-loader%2Fdownload%2Fsass-loader-10.0.1.tgz"], ["style-loader", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:1.2.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fstyle-loader%2Fdownload%2Fstyle-loader-1.2.1.tgz"], ["ts-loader", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:8.0.3::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fts-loader%2Fdownload%2Fts-loader-8.0.3.tgz"], ["typescript", "patch:typescript@npm%3A4.0.2%3A%3A__archiveUrl=https%253A%252F%252Fregistry.npm.taobao.org%252Ftypescript%252Fdownload%252Ftypescript-4.0.2.tgz#builtin::version=4.0.2&hash=5b02a2"], @@ -14247,24 +14298,24 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { }] ]], ["tsutils", [ - ["virtual:6b090f53c60ffa0b7aaef0ced1caa498287b02ac23431999e7b6354a77c6335756f1b98cede1091b7477651df82c3e43da90a593747dbb5c19e306c383157b84#npm:3.17.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Ftsutils%2Fdownload%2Ftsutils-3.17.1.tgz", { - "packageLocation": "./.yarn/$$virtual/tsutils-virtual-099ba65825/0/cache/tsutils-npm-3.17.1-e58260689c-bed8ff7998.zip/node_modules/tsutils/", + ["virtual:3bc9bdae9fa27b66f969c980ce1611a273e2bd05656277a752cd8fc9e72855f63e3874a9bc052c5aa4a1d20282e78616c3b36e4d78cc2efa9cf9a5b60d4f8827#npm:3.17.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Ftsutils%2Fdownload%2Ftsutils-3.17.1.tgz", { + "packageLocation": "./.yarn/$$virtual/tsutils-virtual-f1d0f5113f/0/cache/tsutils-npm-3.17.1-e58260689c-bed8ff7998.zip/node_modules/tsutils/", "packageDependencies": [ - ["tsutils", "virtual:6b090f53c60ffa0b7aaef0ced1caa498287b02ac23431999e7b6354a77c6335756f1b98cede1091b7477651df82c3e43da90a593747dbb5c19e306c383157b84#npm:3.17.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Ftsutils%2Fdownload%2Ftsutils-3.17.1.tgz"], + ["tsutils", "virtual:3bc9bdae9fa27b66f969c980ce1611a273e2bd05656277a752cd8fc9e72855f63e3874a9bc052c5aa4a1d20282e78616c3b36e4d78cc2efa9cf9a5b60d4f8827#npm:3.17.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Ftsutils%2Fdownload%2Ftsutils-3.17.1.tgz"], ["tslib", "npm:1.13.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Ftslib%2Fdownload%2Ftslib-1.13.0.tgz"], - ["typescript", "patch:typescript@npm%3A4.0.2%3A%3A__archiveUrl=https%253A%252F%252Fregistry.npm.taobao.org%252Ftypescript%252Fdownload%252Ftypescript-4.0.2.tgz#builtin::version=4.0.2&hash=5b02a2"] + ["typescript", null] ], "packagePeers": [ "typescript" ], "linkType": "HARD", }], - ["virtual:74f3ce4730be82a39c05577948e092efbe436415fbae850d9d5d07bee35df41e3ecfd8a5b58d019a2990d0c5ad4c8a28fe10a468f8ed2307a1f4ca468609e708#npm:3.17.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Ftsutils%2Fdownload%2Ftsutils-3.17.1.tgz", { - "packageLocation": "./.yarn/$$virtual/tsutils-virtual-4142714550/0/cache/tsutils-npm-3.17.1-e58260689c-bed8ff7998.zip/node_modules/tsutils/", + ["virtual:59fa95d1e1b45f175b2b0fe13055bb329362fd9201cf365c000bd2cdfb659cd47e66040b8775a7bbfcc4ef91002fac4d001fb7c414f5d53084426fa9cb85ceba#npm:3.17.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Ftsutils%2Fdownload%2Ftsutils-3.17.1.tgz", { + "packageLocation": "./.yarn/$$virtual/tsutils-virtual-9bf0fabee1/0/cache/tsutils-npm-3.17.1-e58260689c-bed8ff7998.zip/node_modules/tsutils/", "packageDependencies": [ - ["tsutils", "virtual:74f3ce4730be82a39c05577948e092efbe436415fbae850d9d5d07bee35df41e3ecfd8a5b58d019a2990d0c5ad4c8a28fe10a468f8ed2307a1f4ca468609e708#npm:3.17.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Ftsutils%2Fdownload%2Ftsutils-3.17.1.tgz"], + ["tsutils", "virtual:59fa95d1e1b45f175b2b0fe13055bb329362fd9201cf365c000bd2cdfb659cd47e66040b8775a7bbfcc4ef91002fac4d001fb7c414f5d53084426fa9cb85ceba#npm:3.17.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Ftsutils%2Fdownload%2Ftsutils-3.17.1.tgz"], ["tslib", "npm:1.13.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Ftslib%2Fdownload%2Ftslib-1.13.0.tgz"], - ["typescript", null] + ["typescript", "patch:typescript@npm%3A4.0.2%3A%3A__archiveUrl=https%253A%252F%252Fregistry.npm.taobao.org%252Ftypescript%252Fdownload%252Ftypescript-4.0.2.tgz#builtin::version=4.0.2&hash=5b02a2"] ], "packagePeers": [ "typescript" @@ -14564,7 +14615,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "packageLocation": "./.yarn/$$virtual/url-loader-virtual-e526163f68/0/cache/url-loader-npm-4.1.0-f69f6e3e88-5870b96968.zip/node_modules/url-loader/", "packageDependencies": [ ["url-loader", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:4.1.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Furl-loader%2Fdownload%2Furl-loader-4.1.0.tgz"], - ["file-loader", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:6.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Ffile-loader%2Fdownload%2Ffile-loader-6.0.0.tgz"], + ["file-loader", "virtual:71f98ed0939a4e8e7ea376e302a494701bc5b6aa7a7eb81870139ee3950a7c417a3d13b346b5b526d93952a598dffe628a0fac2148047debade23536cb3d7957#npm:6.1.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Ffile-loader%2Fdownload%2Ffile-loader-6.1.0.tgz"], ["loader-utils", "npm:2.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Floader-utils%2Fdownload%2Floader-utils-2.0.0.tgz"], ["mime-types", "npm:2.1.27::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fmime-types%2Fdownload%2Fmime-types-2.1.27.tgz"], ["schema-utils", "npm:2.7.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fschema-utils%2Fdownload%2Fschema-utils-2.7.0.tgz"], diff --git a/Timeline/ClientApp/package.json b/Timeline/ClientApp/package.json index b633630b..6da643a8 100644 --- a/Timeline/ClientApp/package.json +++ b/Timeline/ClientApp/package.json @@ -8,7 +8,7 @@ "dependencies": { "axios": "^0.20.0", "bootstrap": "^4.5.2", - "bootstrap-icons": "^1.0.0-alpha5", + "bootstrap-icons": "^1.0.0", "classnames": "^2.2.6", "clsx": "^1.1.1", "core-js": "^3.6.5", @@ -20,7 +20,7 @@ "react": "^16.13.1", "react-dom": "^16.13.1", "react-hot-loader": "^4.12.21", - "react-i18next": "^11.7.1", + "react-i18next": "^11.7.2", "react-inlinesvg": "^2.0.0", "react-responsive": "^8.1.0", "react-router": "^5.2.0", @@ -65,9 +65,9 @@ "@hot-loader/react-dom": "^16.13.0", "@types/classnames": "^2.2.10", "@types/crypto-js": "^3.1.47", - "@types/lodash": "^4.14.159", - "@types/node": "^14.6.0", - "@types/react": "^16.9.46", + "@types/lodash": "^4.14.161", + "@types/node": "^14.6.2", + "@types/react": "^16.9.49", "@types/react-dom": "^16.9.8", "@types/react-responsive": "^8.0.2", "@types/react-router": "^5.1.8", @@ -75,15 +75,15 @@ "@types/reactstrap": "^8.5.1", "@types/webpack-env": "^1.15.2", "@types/xregexp": "^4.3.0", - "@typescript-eslint/eslint-plugin": "^3.9.1", - "@typescript-eslint/parser": "^3.9.1", - "@yarnpkg/pnpify": "^2.1.0", + "@typescript-eslint/eslint-plugin": "^4.0.0", + "@typescript-eslint/parser": "^4.0.0", + "@yarnpkg/pnpify": "^2.2.1", "babel-loader": "^8.1.0", "babel-plugin-transform-builtin-extend": "^1.1.2", "clean-webpack-plugin": "^3.0.0", - "copy-webpack-plugin": "^6.0.3", + "copy-webpack-plugin": "^6.1.0", "crypto-js": "^4.0.0", - "css-loader": "^4.2.1", + "css-loader": "^4.2.2", "eslint": "^7.7.0", "eslint-config-prettier": "^6.11.0", "eslint-import-resolver-webpack": "^0.12.2", @@ -91,16 +91,16 @@ "eslint-plugin-prettier": "^3.1.4", "eslint-plugin-react": "^7.20.6", "eslint-plugin-react-hooks": "^4.1.0", - "file-loader": "^6.0.0", - "html-webpack-plugin": "^4.3.0", + "file-loader": "^6.1.0", + "html-webpack-plugin": "^4.4.1", "http-server": "^0.12.3", - "mini-css-extract-plugin": "^0.10.0", + "mini-css-extract-plugin": "^0.11.0", "pnp-webpack-plugin": "^1.6.4", "postcss-loader": "^3.0.0", "postcss-preset-env": "^6.7.0", - "prettier": "^2.1.0", + "prettier": "^2.1.1", "sass": "^1.26.10", - "sass-loader": "^9.0.3", + "sass-loader": "^10.0.1", "style-loader": "^1.2.1", "ts-loader": "^8.0.3", "typescript": "^4.0.2", diff --git a/Timeline/ClientApp/yarn.lock b/Timeline/ClientApp/yarn.lock index 8562343a..78bae865 100644 --- a/Timeline/ClientApp/yarn.lock +++ b/Timeline/ClientApp/yarn.lock @@ -1776,13 +1776,6 @@ __metadata: languageName: node linkType: hard -"@types/eslint-visitor-keys@npm:^1.0.0": - version: 1.0.0 - resolution: "@types/eslint-visitor-keys@npm:1.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Feslint-visitor-keys%2Fdownload%2F%40types%2Feslint-visitor-keys-1.0.0.tgz" - checksum: 48d1f3263148ac822afbc1e54358b423851a2a28c41aef4d7803b052b4f6c3ebfb219daed419b8a4f2b6ac34b545dab4def916d15e69d2bf3f128f7abc0e6132 - languageName: node - linkType: hard - "@types/estree@npm:*": version: 0.0.45 resolution: "@types/estree@npm:0.0.45::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Festree%2Fdownload%2F%40types%2Festree-0.0.45.tgz" @@ -1828,7 +1821,7 @@ __metadata: languageName: node linkType: hard -"@types/json-schema@npm:^7.0.3, @types/json-schema@npm:^7.0.4": +"@types/json-schema@npm:^7.0.3, @types/json-schema@npm:^7.0.4, @types/json-schema@npm:^7.0.5": version: 7.0.5 resolution: "@types/json-schema@npm:7.0.5::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Fjson-schema%2Fdownload%2F%40types%2Fjson-schema-7.0.5.tgz" checksum: 6290f9fe93ac957b244262d5ff56cfd3045c63da6ed88dcc2d5b84131e6284c8e6213bf0cb81423a4f940182647bcd69057309c982f8db64dfff8f65f800ef80 @@ -1851,10 +1844,10 @@ __metadata: languageName: node linkType: hard -"@types/lodash@npm:^4.14.159": - version: 4.14.159 - resolution: "@types/lodash@npm:4.14.159::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Flodash%2Fdownload%2F%40types%2Flodash-4.14.159.tgz" - checksum: 15fc1b690916aec2e169327759fbc71b0cb25ca0cd0003c4d20d2e5b4ed14b7bc2feb2835d643e0335eabc1536e7c11b499de591eb794fbb764523cb06c66eee +"@types/lodash@npm:^4.14.161": + version: 4.14.161 + resolution: "@types/lodash@npm:4.14.161::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Flodash%2Fdownload%2F%40types%2Flodash-4.14.161.tgz" + checksum: c3657517dab1a23059d86ae379c4e7538212f05ba9c3e108ab694167f086118a5b44ed04bcb6e5cd0a89778c29909668302a755df576e3597df16fda8b603443 languageName: node linkType: hard @@ -1879,10 +1872,10 @@ __metadata: languageName: node linkType: hard -"@types/node@npm:^14.6.0": - version: 14.6.0 - resolution: "@types/node@npm:14.6.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Fnode%2Fdownload%2F%40types%2Fnode-14.6.0.tgz" - checksum: ff23553ab77516d0f90b6710a041a49723fc1b1ca33a5379d5746068cac8b9ddbec428d3a4913afaee50bcdc69b9583843740a18a3519ae997982694f5287c6b +"@types/node@npm:^14.6.2": + version: 14.6.2 + resolution: "@types/node@npm:14.6.2::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Fnode%2Fdownload%2F%40types%2Fnode-14.6.2.tgz" + checksum: e88f4749fd95d7358a719dee27bb3c02aa7d93a69d95887b4baaeae3fcc21c65d8675e6a9633e499de22fa77651fc12188fd3657a6729dbce9e44a4fe5c0691b languageName: node linkType: hard @@ -1942,13 +1935,13 @@ __metadata: languageName: node linkType: hard -"@types/react@npm:^16.9.46": - version: 16.9.46 - resolution: "@types/react@npm:16.9.46::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Freact%2Fdownload%2F%40types%2Freact-16.9.46.tgz" +"@types/react@npm:^16.9.49": + version: 16.9.49 + resolution: "@types/react@npm:16.9.49::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Freact%2Fdownload%2F%40types%2Freact-16.9.49.tgz" dependencies: "@types/prop-types": "*" csstype: ^3.0.2 - checksum: ad8d07195dc483f9186dc7886bba15c39e6f9c29bbbd97e52a7a117016f07ac6f923b202749d40833bef62fe60f4af6847b82dcfa0c539495e541ad6d819e49a + checksum: cf5dabe1e9c893a19a338002ac4bf5a86c9bc3cd88eb07aece54e6d2e5af35debe2a8c72d211ad9133391543ce115219cd7c7f2a6f649b81caf4548e1cc99061 languageName: node linkType: hard @@ -2042,11 +2035,12 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/eslint-plugin@npm:^3.9.1": - version: 3.9.1 - resolution: "@typescript-eslint/eslint-plugin@npm:3.9.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40typescript-eslint%2Feslint-plugin%2Fdownload%2F%40typescript-eslint%2Feslint-plugin-3.9.1.tgz" +"@typescript-eslint/eslint-plugin@npm:^4.0.0": + version: 4.0.0 + resolution: "@typescript-eslint/eslint-plugin@npm:4.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40typescript-eslint%2Feslint-plugin%2Fdownload%2F%40typescript-eslint%2Feslint-plugin-4.0.0.tgz" dependencies: - "@typescript-eslint/experimental-utils": 3.9.1 + "@typescript-eslint/experimental-utils": 4.0.0 + "@typescript-eslint/scope-manager": 4.0.0 debug: ^4.1.1 functional-red-black-tree: ^1.0.1 regexpp: ^3.0.0 @@ -2059,59 +2053,69 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 252bab346c02c78f97bc389686230ba0260409f1471c1bea91764ea1ff0aa30d941294c4ae59a4d3f5ca2f8e73311a080709f0d0e5923b9dd4c013b0d9a1f583 + checksum: 4a6d9fa69ae174610df67cf3ad3bbaa4a0f3d0b90ab9eb62b9c25efb4760273c925fe39b28a1b8b537fd9cf0f8433d0a55057965647c6e8eb7c7d051fa0fbbdc languageName: node linkType: hard -"@typescript-eslint/experimental-utils@npm:3.9.1": - version: 3.9.1 - resolution: "@typescript-eslint/experimental-utils@npm:3.9.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40typescript-eslint%2Fexperimental-utils%2Fdownload%2F%40typescript-eslint%2Fexperimental-utils-3.9.1.tgz" +"@typescript-eslint/experimental-utils@npm:4.0.0": + version: 4.0.0 + resolution: "@typescript-eslint/experimental-utils@npm:4.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40typescript-eslint%2Fexperimental-utils%2Fdownload%2F%40typescript-eslint%2Fexperimental-utils-4.0.0.tgz" dependencies: "@types/json-schema": ^7.0.3 - "@typescript-eslint/types": 3.9.1 - "@typescript-eslint/typescript-estree": 3.9.1 + "@typescript-eslint/scope-manager": 4.0.0 + "@typescript-eslint/types": 4.0.0 + "@typescript-eslint/typescript-estree": 4.0.0 eslint-scope: ^5.0.0 eslint-utils: ^2.0.0 peerDependencies: eslint: "*" - checksum: 8092e411ad2b8c57bb848507f9feffd7c305ca469b494b3d97b4176b73f0d9dbff20715a66537e9916a17b9aff9d76c446a0849967712bac2fba9b237f3df395 + checksum: bce9fe3ac34e4a587654010fd7ca08d3713186c09b910d8da7cd0b12e84b9e0abed2474856e796601b579b5b3956b126700458ba43ed5a4ab3f290b5834de091 languageName: node linkType: hard -"@typescript-eslint/parser@npm:^3.9.1": - version: 3.9.1 - resolution: "@typescript-eslint/parser@npm:3.9.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40typescript-eslint%2Fparser%2Fdownload%2F%40typescript-eslint%2Fparser-3.9.1.tgz" +"@typescript-eslint/parser@npm:^4.0.0": + version: 4.0.0 + resolution: "@typescript-eslint/parser@npm:4.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40typescript-eslint%2Fparser%2Fdownload%2F%40typescript-eslint%2Fparser-4.0.0.tgz" dependencies: - "@types/eslint-visitor-keys": ^1.0.0 - "@typescript-eslint/experimental-utils": 3.9.1 - "@typescript-eslint/types": 3.9.1 - "@typescript-eslint/typescript-estree": 3.9.1 - eslint-visitor-keys: ^1.1.0 + "@typescript-eslint/scope-manager": 4.0.0 + "@typescript-eslint/types": 4.0.0 + "@typescript-eslint/typescript-estree": 4.0.0 + debug: ^4.1.1 peerDependencies: eslint: ^5.0.0 || ^6.0.0 || ^7.0.0 typescript: "*" peerDependenciesMeta: typescript: optional: true - checksum: ed9a4829697cb02b18d47b83d8bb5678f37a5eb996e47ded58387dda22f0b6e51800f0a3ecf6136f23827bfc654cd0f38d5082ca66a93c593ee0afa370edcc10 + checksum: 665ffa76988beae5acefe776b8b6873e5e3e7452cf780b80b67f4137769dc01ec72810e59a25dc9c4dd73e3a44d71f9d8d5ea5aef46b7fc90a1e3cc89a04bbde languageName: node linkType: hard -"@typescript-eslint/types@npm:3.9.1": - version: 3.9.1 - resolution: "@typescript-eslint/types@npm:3.9.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40typescript-eslint%2Ftypes%2Fdownload%2F%40typescript-eslint%2Ftypes-3.9.1.tgz" - checksum: 0591ff76919fb8d19af26f0657329d8fa499484f64e073a76ae2f846ea39074c3cd888ae185d16aa2974eb1ca3a0aa6083040c13872a2aaafb8a10e1b8a38a9e +"@typescript-eslint/scope-manager@npm:4.0.0": + version: 4.0.0 + resolution: "@typescript-eslint/scope-manager@npm:4.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40typescript-eslint%2Fscope-manager%2Fdownload%2F%40typescript-eslint%2Fscope-manager-4.0.0.tgz" + dependencies: + "@typescript-eslint/types": 4.0.0 + "@typescript-eslint/visitor-keys": 4.0.0 + checksum: 6645cfbceb08617b86429bf2b7d43ea96088d0044ceab7d74916b0938cec322a0100860fbf397b438e24a8e67d83a97e65ab240c1f520af30a42b20b764b38d5 languageName: node linkType: hard -"@typescript-eslint/typescript-estree@npm:3.9.1": - version: 3.9.1 - resolution: "@typescript-eslint/typescript-estree@npm:3.9.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40typescript-eslint%2Ftypescript-estree%2Fdownload%2F%40typescript-eslint%2Ftypescript-estree-3.9.1.tgz" +"@typescript-eslint/types@npm:4.0.0": + version: 4.0.0 + resolution: "@typescript-eslint/types@npm:4.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40typescript-eslint%2Ftypes%2Fdownload%2F%40typescript-eslint%2Ftypes-4.0.0.tgz" + checksum: 143dc3d3d45706f88589da6c86b5e401c9e97caff6286d56d411039132bd3a303e995d187e2b758acee5e76a559600c2c515d572d495296956a2660a222d5c89 + languageName: node + linkType: hard + +"@typescript-eslint/typescript-estree@npm:4.0.0": + version: 4.0.0 + resolution: "@typescript-eslint/typescript-estree@npm:4.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40typescript-eslint%2Ftypescript-estree%2Fdownload%2F%40typescript-eslint%2Ftypescript-estree-4.0.0.tgz" dependencies: - "@typescript-eslint/types": 3.9.1 - "@typescript-eslint/visitor-keys": 3.9.1 + "@typescript-eslint/types": 4.0.0 + "@typescript-eslint/visitor-keys": 4.0.0 debug: ^4.1.1 - glob: ^7.1.6 + globby: ^11.0.1 is-glob: ^4.0.1 lodash: ^4.17.15 semver: ^7.3.2 @@ -2121,16 +2125,17 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: f9a962b6ffd557aff680ccc8a67e79e305002dd90f7637f0d22b9e6e9112dc143f629d12af769e6252c17025cc4bc381218a425fdb4bf9dbd8c12a235356d8c1 + checksum: 9fc127bf0a9c1853f72192b9db8bd1dc2c5858d7cc95a153494610fb99a6bac090a6f806da9db2a81adf7f0c4c3fa78c957bc91a25f641758bad91a540c58119 languageName: node linkType: hard -"@typescript-eslint/visitor-keys@npm:3.9.1": - version: 3.9.1 - resolution: "@typescript-eslint/visitor-keys@npm:3.9.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40typescript-eslint%2Fvisitor-keys%2Fdownload%2F%40typescript-eslint%2Fvisitor-keys-3.9.1.tgz" +"@typescript-eslint/visitor-keys@npm:4.0.0": + version: 4.0.0 + resolution: "@typescript-eslint/visitor-keys@npm:4.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40typescript-eslint%2Fvisitor-keys%2Fdownload%2F%40typescript-eslint%2Fvisitor-keys-4.0.0.tgz" dependencies: - eslint-visitor-keys: ^1.1.0 - checksum: 9e54d3a37aef8c177872dcfba59ead2de6bd5c7370a735dc5965d499ca7d93cef6edf297bc07f114098291e797b561cae1bbc74e3e426a3c521d02f03cef95e0 + "@typescript-eslint/types": 4.0.0 + eslint-visitor-keys: ^2.0.0 + checksum: e443755abfd9b898c16b53cb57367cd0ce3039c5b18c272ec7cfb24416ed0f6c4486ee40cf464222c830977c6bafdedd028b5527fdcffd0065fd4c7693814556 languageName: node linkType: hard @@ -2329,26 +2334,27 @@ __metadata: languageName: node linkType: hard -"@yarnpkg/core@npm:^2.1.0": - version: 2.1.1 - resolution: "@yarnpkg/core@npm:2.1.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40yarnpkg%2Fcore%2Fdownload%2F%40yarnpkg%2Fcore-2.1.1.tgz" +"@yarnpkg/core@npm:^2.2.1": + version: 2.2.2 + resolution: "@yarnpkg/core@npm:2.2.2::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40yarnpkg%2Fcore%2Fdownload%2F%40yarnpkg%2Fcore-2.2.2.tgz" dependencies: "@arcanis/slice-ansi": ^1.0.2 - "@yarnpkg/fslib": ^2.1.0 + "@yarnpkg/fslib": ^2.2.1 "@yarnpkg/json-proxy": ^2.1.0 - "@yarnpkg/libzip": ^2.1.0 - "@yarnpkg/parsers": ^2.1.0 - "@yarnpkg/pnp": ^2.1.0 - "@yarnpkg/shell": ^2.1.0 + "@yarnpkg/libzip": ^2.2.0 + "@yarnpkg/parsers": ^2.2.0 + "@yarnpkg/pnp": ^2.2.1 + "@yarnpkg/shell": ^2.2.0 camelcase: ^5.3.1 chalk: ^3.0.0 ci-info: ^2.0.0 - clipanion: ^2.4.2 + clipanion: ^2.4.4 cross-spawn: 7.0.3 diff: ^4.0.1 - globby: ^10.0.1 + globby: ^11.0.1 got: ^11.1.3 json-file-plus: ^3.3.1 + lodash: ^4.17.15 logic-solver: ^2.0.1 micromatch: ^4.0.2 mkdirp: ^0.5.1 @@ -2357,10 +2363,10 @@ __metadata: pretty-bytes: ^5.1.0 semver: ^7.1.2 stream-to-promise: ^2.2.0 - tar: ^4.4.6 + tar-stream: ^2.0.1 tslib: ^1.13.0 tunnel: ^0.0.6 - checksum: d01e24bde194feab28bc0524adc8bf1e386041148d436bbb368624f47b1dd5f5e6ca13d3adf9b7501bafff5953d14073880c8c7234cbbaea64bdff793b92bb7d + checksum: d56fdc230ee7ebc88c4d06f6e37ab4c2c3ddef0376dc0bc6ebc68ca8aec25d02e666f080a89ad815e1e703c7dc15ebfc459c2428ca8a224408234a3d295dbed0 languageName: node linkType: hard @@ -2374,6 +2380,16 @@ __metadata: languageName: node linkType: hard +"@yarnpkg/fslib@npm:^2.2.0, @yarnpkg/fslib@npm:^2.2.1": + version: 2.2.1 + resolution: "@yarnpkg/fslib@npm:2.2.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40yarnpkg%2Ffslib%2Fdownload%2F%40yarnpkg%2Ffslib-2.2.1.tgz" + dependencies: + "@yarnpkg/libzip": ^2.2.0 + tslib: ^1.13.0 + checksum: 2d5dac27eff03191bd6ff5f894a8a941459d1cb8ccccbb8802da8089533cb832b92857825b579336eee74228ca1efe1a4f013f8489c514ed44b55387e0227f16 + languageName: node + linkType: hard + "@yarnpkg/json-proxy@npm:^2.1.0": version: 2.1.0 resolution: "@yarnpkg/json-proxy@npm:2.1.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40yarnpkg%2Fjson-proxy%2Fdownload%2F%40yarnpkg%2Fjson-proxy-2.1.0.tgz" @@ -2394,36 +2410,46 @@ __metadata: languageName: node linkType: hard -"@yarnpkg/parsers@npm:^2.1.0": - version: 2.1.0 - resolution: "@yarnpkg/parsers@npm:2.1.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40yarnpkg%2Fparsers%2Fdownload%2F%40yarnpkg%2Fparsers-2.1.0.tgz" +"@yarnpkg/libzip@npm:^2.2.0": + version: 2.2.0 + resolution: "@yarnpkg/libzip@npm:2.2.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40yarnpkg%2Flibzip%2Fdownload%2F%40yarnpkg%2Flibzip-2.2.0.tgz" + dependencies: + "@types/emscripten": ^1.38.0 + tslib: ^1.13.0 + checksum: 5546c330c8bf7e288f7446a30dc8b50677746421d14418b12e89c5b7fac33fb90e3c6019ac9fb6ffaa01708759a3540485b7bc69e245d4908f4d3f8436cdc059 + languageName: node + linkType: hard + +"@yarnpkg/parsers@npm:^2.2.0": + version: 2.2.0 + resolution: "@yarnpkg/parsers@npm:2.2.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40yarnpkg%2Fparsers%2Fdownload%2F%40yarnpkg%2Fparsers-2.2.0.tgz" dependencies: js-yaml: ^3.10.0 tslib: ^1.13.0 - checksum: 3cd4e354358ec1562c07872a1132c6eb2817ec3521c89043071b0b2d3fc03946176815f31e930ae24a689321584bf204c7a0fe3a347dc17d359a34062ade6774 + checksum: 4ff177009d65ace26b4816cd3bffa1377734919b9e9e8d4a21d5491ec218a57de3d87545e4ed0eb06001f00f51e13d0646e33f676df09b2a34f5d892ec2d7348 languageName: node linkType: hard -"@yarnpkg/pnp@npm:^2.1.0": - version: 2.1.0 - resolution: "@yarnpkg/pnp@npm:2.1.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40yarnpkg%2Fpnp%2Fdownload%2F%40yarnpkg%2Fpnp-2.1.0.tgz" +"@yarnpkg/pnp@npm:^2.2.1": + version: 2.2.1 + resolution: "@yarnpkg/pnp@npm:2.2.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40yarnpkg%2Fpnp%2Fdownload%2F%40yarnpkg%2Fpnp-2.2.1.tgz" dependencies: "@types/node": ^13.7.0 - "@yarnpkg/fslib": ^2.1.0 + "@yarnpkg/fslib": ^2.2.1 tslib: ^1.13.0 - checksum: 610f875f597d6453da5aea96b2abcb1e6e22e210bd9dface366ed2a6e401fa6fdb5ee0884d3fe083c34ab566e59afeb92d13e0dafa8314d57f9cebf5b723a401 + checksum: 0ea840774c1bba2c357275ad86dbd3f4154a1cb035a8773a94bf37467051180c9ecaf30ed6d64a2474ed8bd9617de56aec5c08b35dd976f285ef666081782e7d languageName: node linkType: hard -"@yarnpkg/pnpify@npm:^2.1.0": - version: 2.1.0 - resolution: "@yarnpkg/pnpify@npm:2.1.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40yarnpkg%2Fpnpify%2Fdownload%2F%40yarnpkg%2Fpnpify-2.1.0.tgz" +"@yarnpkg/pnpify@npm:^2.2.1": + version: 2.2.1 + resolution: "@yarnpkg/pnpify@npm:2.2.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40yarnpkg%2Fpnpify%2Fdownload%2F%40yarnpkg%2Fpnpify-2.2.1.tgz" dependencies: - "@yarnpkg/core": ^2.1.0 - "@yarnpkg/fslib": ^2.1.0 - "@yarnpkg/parsers": ^2.1.0 + "@yarnpkg/core": ^2.2.1 + "@yarnpkg/fslib": ^2.2.1 + "@yarnpkg/parsers": ^2.2.0 chalk: ^3.0.0 - clipanion: ^2.4.2 + clipanion: ^2.4.4 comment-json: ^2.2.0 lodash: ^4.17.15 tslib: ^1.13.0 @@ -2437,24 +2463,24 @@ __metadata: optional: true bin: pnpify: ./lib/cli.js - checksum: 334ce4cc6c6c154fb5aa4f6b4847bb5d7869596d295cc429dedecaa9b4865cbaf104855d856fbbf5f186da09dd7e63bf7d774665f2ce5439c2664c13ef142cf0 + checksum: 47a6fd0c11403d1dffdac76765ba995bdc8b5430666997e15c70ca285747e01fe831fd875a13fb1118d22458c019bbe1fab60945f9cb0a6ef46d291a5759b244 languageName: node linkType: hard -"@yarnpkg/shell@npm:^2.1.0": - version: 2.1.0 - resolution: "@yarnpkg/shell@npm:2.1.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40yarnpkg%2Fshell%2Fdownload%2F%40yarnpkg%2Fshell-2.1.0.tgz" +"@yarnpkg/shell@npm:^2.2.0": + version: 2.2.0 + resolution: "@yarnpkg/shell@npm:2.2.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2F%40yarnpkg%2Fshell%2Fdownload%2F%40yarnpkg%2Fshell-2.2.0.tgz" dependencies: - "@yarnpkg/fslib": ^2.1.0 - "@yarnpkg/parsers": ^2.1.0 - clipanion: ^2.4.2 + "@yarnpkg/fslib": ^2.2.0 + "@yarnpkg/parsers": ^2.2.0 + clipanion: ^2.4.4 cross-spawn: 7.0.3 fast-glob: ^3.2.2 stream-buffers: ^3.0.2 tslib: ^1.13.0 bin: shell: ./lib/cli.js - checksum: 2e0b808965c7b3c8fcce64da6889f087550cd25604cf963ac06a037e08a17d020561457126d1e4bfe19459da3f192f1aef7ed0636895fd619e3c801f64216d4c + checksum: 23a26955caf2c3435a1c2dbd960a36a898c001159d36aabf93029c2e19fbb3c0161ada53f6167bcb26a28a038ade347c101bd98771adb7fa1fc5e1aa3542ea32 languageName: node linkType: hard @@ -2530,6 +2556,15 @@ __metadata: languageName: node linkType: hard +"ajv-keywords@npm:^3.5.2": + version: 3.5.2 + resolution: "ajv-keywords@npm:3.5.2::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fajv-keywords%2Fdownload%2Fajv-keywords-3.5.2.tgz" + peerDependencies: + ajv: ^6.9.1 + checksum: 01f26c292304870c03a1cd14fc1ddcf7c713a05611a122c5193694d4050063d5fba46cbf8b5b2ebde364166fddd3c2e0abdcd97df655b7a7fbb3e6634eeb056a + languageName: node + linkType: hard + "ajv@npm:^6.1.0, ajv@npm:^6.10.0, ajv@npm:^6.10.2, ajv@npm:^6.12.2, ajv@npm:^6.5.5": version: 6.12.3 resolution: "ajv@npm:6.12.3::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fajv%2Fdownload%2Fajv-6.12.3.tgz" @@ -2542,6 +2577,18 @@ __metadata: languageName: node linkType: hard +"ajv@npm:^6.12.4": + version: 6.12.4 + resolution: "ajv@npm:6.12.4::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fajv%2Fdownload%2Fajv-6.12.4.tgz" + dependencies: + fast-deep-equal: ^3.1.1 + fast-json-stable-stringify: ^2.0.0 + json-schema-traverse: ^0.4.1 + uri-js: ^4.2.2 + checksum: 50d72b0a10326732072f5481b1b6bd5a43f8d770878b8f88ba5bb232abb745cefbf7f87a0e64679bd477d4a8bba0b3aea084675bd34943db5279c15907ee658f + languageName: node + linkType: hard + "ansi-colors@npm:^3.0.0": version: 3.2.4 resolution: "ansi-colors@npm:3.2.4::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fansi-colors%2Fdownload%2Fansi-colors-3.2.4.tgz" @@ -3130,6 +3177,17 @@ __metadata: languageName: node linkType: hard +"bl@npm:^4.0.1": + version: 4.0.3 + resolution: "bl@npm:4.0.3::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fbl%2Fdownload%2Fbl-4.0.3.tgz" + dependencies: + buffer: ^5.5.0 + inherits: ^2.0.4 + readable-stream: ^3.4.0 + checksum: 1f33c5a3da08a87260a7f11acadf088ef331ebb4b86db1160ec332be9326afdf9f73dca1fd5cc431dba7cc9d5574b508192f9fd7c37a9a11c9e4a50025917690 + languageName: node + linkType: hard + "bluebird@npm:^3.5.5": version: 3.7.2 resolution: "bluebird@npm:3.7.2::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fbluebird%2Fdownload%2Fbluebird-3.7.2.tgz" @@ -3190,10 +3248,10 @@ __metadata: languageName: node linkType: hard -"bootstrap-icons@npm:^1.0.0-alpha5": - version: 1.0.0-alpha5 - resolution: "bootstrap-icons@npm:1.0.0-alpha5::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fbootstrap-icons%2Fdownload%2Fbootstrap-icons-1.0.0-alpha5.tgz" - checksum: a97c110bc9bc4ad7497eb2d9b6424a684930a35a0eaddd4577589b97c5efcce52d37270feb372394885d9a81934cf4cb9ece10d7d0c23adaa7fd9e3e86cf74e3 +"bootstrap-icons@npm:^1.0.0": + version: 1.0.0 + resolution: "bootstrap-icons@npm:1.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fbootstrap-icons%2Fdownload%2Fbootstrap-icons-1.0.0.tgz" + checksum: 082d8d2aff07217289b3a78c0d8eb80c45b42f2ee63ffa27a87c83072745177dd12b0b12d1b73d95a5e69d45b54c739b5db6e2eb7e310aa8b54b6b08ec57772c languageName: node linkType: hard @@ -3384,6 +3442,16 @@ __metadata: languageName: node linkType: hard +"buffer@npm:^5.5.0": + version: 5.6.0 + resolution: "buffer@npm:5.6.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fbuffer%2Fdownload%2Fbuffer-5.6.0.tgz" + dependencies: + base64-js: ^1.0.2 + ieee754: ^1.1.4 + checksum: e18fdf099c25cae354d673c7deee0391978bde5a47b785cf81e118c75853f0f36838b0a5ea5ee7adf8c02eedb9664292608efdcac9945f4f4f514d14054656f7 + languageName: node + linkType: hard + "builtin-modules@npm:^3.1.0": version: 3.1.0 resolution: "builtin-modules@npm:3.1.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fbuiltin-modules%2Fdownload%2Fbuiltin-modules-3.1.0.tgz" @@ -3435,7 +3503,7 @@ __metadata: languageName: node linkType: hard -"cacache@npm:^15.0.4": +"cacache@npm:^15.0.5": version: 15.0.5 resolution: "cacache@npm:15.0.5::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fcacache%2Fdownload%2Fcacache-15.0.5.tgz" dependencies: @@ -3749,10 +3817,10 @@ __metadata: languageName: node linkType: hard -"clipanion@npm:^2.4.2": - version: 2.4.2 - resolution: "clipanion@npm:2.4.2::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fclipanion%2Fdownload%2Fclipanion-2.4.2.tgz" - checksum: 4a8aa1c126f7c3c0c4555dc5dbf30b9ce49b0654bcac5dd7195e8e0d45b12df411e8a534b3354d46267876f16db885935c4ab67e8fa5efbe4262c9713ea72763 +"clipanion@npm:^2.4.4": + version: 2.5.0 + resolution: "clipanion@npm:2.5.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fclipanion%2Fdownload%2Fclipanion-2.5.0.tgz" + checksum: 700ff428c971413f4c88e40a47047f98e84e7ce53dd00f9a2f44e603038645f673319a1fb17a27b3dda5c4314f79ef6b2bf7cbbcdc5b94827e377c43f72fa4c3 languageName: node linkType: hard @@ -4040,24 +4108,24 @@ __metadata: languageName: node linkType: hard -"copy-webpack-plugin@npm:^6.0.3": - version: 6.0.3 - resolution: "copy-webpack-plugin@npm:6.0.3::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fcopy-webpack-plugin%2Fdownload%2Fcopy-webpack-plugin-6.0.3.tgz" +"copy-webpack-plugin@npm:^6.1.0": + version: 6.1.0 + resolution: "copy-webpack-plugin@npm:6.1.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fcopy-webpack-plugin%2Fdownload%2Fcopy-webpack-plugin-6.1.0.tgz" dependencies: - cacache: ^15.0.4 + cacache: ^15.0.5 fast-glob: ^3.2.4 find-cache-dir: ^3.3.1 glob-parent: ^5.1.1 globby: ^11.0.1 loader-utils: ^2.0.0 normalize-path: ^3.0.0 - p-limit: ^3.0.1 - schema-utils: ^2.7.0 + p-limit: ^3.0.2 + schema-utils: ^2.7.1 serialize-javascript: ^4.0.0 webpack-sources: ^1.4.3 peerDependencies: webpack: ^4.37.0 || ^5.0.0 - checksum: 02bb71e9ad0959c883a6bb3ca7351f15d7d4123cec21aa48d7a879a6116fa07bdc1465cc57eb3616482c0844bfa100acf33123c1c49545e1e0eee0996da782c3 + checksum: bb289324cdda4c8aaa7fd508c34e499cdad96771703e0c5081c78a0f821eeb133500dcc10439e6258f723c32903dd3df46a5cf07585266e6cd0600c57e8db082 languageName: node linkType: hard @@ -4248,15 +4316,14 @@ __metadata: languageName: node linkType: hard -"css-loader@npm:^4.2.1": - version: 4.2.1 - resolution: "css-loader@npm:4.2.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fcss-loader%2Fdownload%2Fcss-loader-4.2.1.tgz" +"css-loader@npm:^4.2.2": + version: 4.2.2 + resolution: "css-loader@npm:4.2.2::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fcss-loader%2Fdownload%2Fcss-loader-4.2.2.tgz" dependencies: camelcase: ^6.0.0 cssesc: ^3.0.0 icss-utils: ^4.1.1 loader-utils: ^2.0.0 - normalize-path: ^3.0.0 postcss: ^7.0.32 postcss-modules-extract-imports: ^2.0.0 postcss-modules-local-by-default: ^3.0.3 @@ -4267,7 +4334,7 @@ __metadata: semver: ^7.3.2 peerDependencies: webpack: ^4.27.0 || ^5.0.0 - checksum: bd8188efac141352371d3c87e90b6c5658018fa2e9438a22ad38e7a3896e492edb535ae7fe7e56e4c14e43ccc88fbb3a235a0929d4f9302bbf12215687edc9fa + checksum: 73d83e7f4f0b6288802e654a108e2f7a0476d0504c41ac3718a92354cefa9c3fe25e462cdf262a3943efef73dc2ea12c1962f97bbda44807f6f81b4661717dcb languageName: node linkType: hard @@ -4852,7 +4919,7 @@ __metadata: languageName: node linkType: hard -"end-of-stream@npm:^1.0.0, end-of-stream@npm:^1.1.0": +"end-of-stream@npm:^1.0.0, end-of-stream@npm:^1.1.0, end-of-stream@npm:^1.4.1": version: 1.4.4 resolution: "end-of-stream@npm:1.4.4::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fend-of-stream%2Fdownload%2Fend-of-stream-1.4.4.tgz" dependencies: @@ -5148,6 +5215,13 @@ __metadata: languageName: node linkType: hard +"eslint-visitor-keys@npm:^2.0.0": + version: 2.0.0 + resolution: "eslint-visitor-keys@npm:2.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Feslint-visitor-keys%2Fdownload%2Feslint-visitor-keys-2.0.0.tgz" + checksum: 429dabdcab3c1cf5e65d44843afc513398d4ee32a37f93edc93bb5ba59a12b78fa67d87ff23c752c170b5e4f9085050f45b3c036cdfb23d40a724f2614048140 + languageName: node + linkType: hard + "eslint@npm:^7.7.0": version: 7.7.0 resolution: "eslint@npm:7.7.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Feslint%2Fdownload%2Feslint-7.7.0.tgz" @@ -5456,7 +5530,7 @@ __metadata: languageName: node linkType: hard -"fast-glob@npm:^3.0.3, fast-glob@npm:^3.1.1, fast-glob@npm:^3.2.2, fast-glob@npm:^3.2.4": +"fast-glob@npm:^3.1.1, fast-glob@npm:^3.2.2, fast-glob@npm:^3.2.4": version: 3.2.4 resolution: "fast-glob@npm:3.2.4::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Ffast-glob%2Fdownload%2Ffast-glob-3.2.4.tgz" dependencies: @@ -5527,15 +5601,15 @@ __metadata: languageName: node linkType: hard -"file-loader@npm:^6.0.0": - version: 6.0.0 - resolution: "file-loader@npm:6.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Ffile-loader%2Fdownload%2Ffile-loader-6.0.0.tgz" +"file-loader@npm:^6.1.0": + version: 6.1.0 + resolution: "file-loader@npm:6.1.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Ffile-loader%2Fdownload%2Ffile-loader-6.1.0.tgz" dependencies: loader-utils: ^2.0.0 - schema-utils: ^2.6.5 + schema-utils: ^2.7.1 peerDependencies: webpack: ^4.0.0 || ^5.0.0 - checksum: 745f5ee763fe46f2124921ff4fb7ed601eb1e8d6ac975cf6af321b3957ce8f78afb885c636e64a9031634e85e26dc9e8ef40ebeb532153c6a80a7d06b56876cb + checksum: ad930c9e7fa8c85cc048afa44c370c86f6a9ba89149b2b24e362f0b279595a05d039211263bf65117e4850f337f9bff251288ddff426dda997468a00e1268ffc languageName: node linkType: hard @@ -5760,6 +5834,13 @@ __metadata: languageName: node linkType: hard +"fs-constants@npm:^1.0.0": + version: 1.0.0 + resolution: "fs-constants@npm:1.0.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Ffs-constants%2Fdownload%2Ffs-constants-1.0.0.tgz" + checksum: b8382395f555012591b20bddf08d258723f660b4e7312943d10431a893e2af879295fefc15a917df43c9ed52d80d2f014c0ca8ca359367969be5c8a133e39742 + languageName: node + linkType: hard + "fs-extra@npm:^8.1.0": version: 8.1.0 resolution: "fs-extra@npm:8.1.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Ffs-extra%2Fdownload%2Ffs-extra-8.1.0.tgz" @@ -5771,15 +5852,6 @@ __metadata: languageName: node linkType: hard -"fs-minipass@npm:^1.2.5": - version: 1.2.7 - resolution: "fs-minipass@npm:1.2.7::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Ffs-minipass%2Fdownload%2Ffs-minipass-1.2.7.tgz" - dependencies: - minipass: ^2.6.0 - checksum: eb59a93065f25457e5d1d10a064e22565e704b03140d5ef86a71a57155b13aa645811126fed2a5a282df8dc9c40df9c9d696f6b2d93c181071a971221d0a454b - languageName: node - linkType: hard - "fs-minipass@npm:^2.0.0": version: 2.1.0 resolution: "fs-minipass@npm:2.1.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Ffs-minipass%2Fdownload%2Ffs-minipass-2.1.0.tgz" @@ -6048,22 +6120,6 @@ fsevents@~2.1.2: languageName: node linkType: hard -"globby@npm:^10.0.1": - version: 10.0.2 - resolution: "globby@npm:10.0.2::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fglobby%2Fdownload%2Fglobby-10.0.2.tgz" - dependencies: - "@types/glob": ^7.1.1 - array-union: ^2.1.0 - dir-glob: ^3.0.1 - fast-glob: ^3.0.3 - glob: ^7.1.3 - ignore: ^5.1.1 - merge2: ^1.2.3 - slash: ^3.0.0 - checksum: 53924c2b46f104d99a6b15da92b9f9f1e9f004bce745fdf56cf985afd615897bd6fd8fe01303f5758943e643c0885e8abaae0b5a596c13523c9431bf071c3f23 - languageName: node - linkType: hard - "globby@npm:^11.0.1": version: 11.0.1 resolution: "globby@npm:11.0.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fglobby%2Fdownload%2Fglobby-11.0.1.tgz" @@ -6372,9 +6428,9 @@ fsevents@~2.1.2: languageName: node linkType: hard -"html-webpack-plugin@npm:^4.3.0": - version: 4.3.0 - resolution: "html-webpack-plugin@npm:4.3.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fhtml-webpack-plugin%2Fdownload%2Fhtml-webpack-plugin-4.3.0.tgz" +"html-webpack-plugin@npm:^4.4.1": + version: 4.4.1 + resolution: "html-webpack-plugin@npm:4.4.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fhtml-webpack-plugin%2Fdownload%2Fhtml-webpack-plugin-4.4.1.tgz" dependencies: "@types/html-minifier-terser": ^5.0.0 "@types/tapable": ^1.0.5 @@ -6387,7 +6443,7 @@ fsevents@~2.1.2: util.promisify: 1.0.0 peerDependencies: webpack: ">=4.0.0 < 6.0.0" - checksum: 13c23547ac83142f2e23a993a82e6fa6beb21291848a856c09d27e0dae12f5781699ed9c1fe798fb03cf317a6a4dce164d224fd6023640abc167f222b59a0fa0 + checksum: 5bf56a8c24ddea93468f028de2b9aacbb5686fdf226e911616aec6bb7fe26aa6e3ed63096faabc10eb0bbef659e422be47056d2eb7d186e5f603261ea5db0eb5 languageName: node linkType: hard @@ -6593,7 +6649,7 @@ fsevents@~2.1.2: languageName: node linkType: hard -"ignore@npm:^5.1.1, ignore@npm:^5.1.4": +"ignore@npm:^5.1.4": version: 5.1.8 resolution: "ignore@npm:5.1.8::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fignore%2Fdownload%2Fignore-5.1.8.tgz" checksum: b08e3d5b5d94eca13475f29a5d47d221060e9cdd7e38d7647088e29d90130669a970fecbc4cdb41b8fa295c6673740c729d3dc05dadc381f593efb42282cbf9f @@ -7379,10 +7435,10 @@ fsevents@~2.1.2: languageName: node linkType: hard -"klona@npm:^1.1.2": - version: 1.1.2 - resolution: "klona@npm:1.1.2::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fklona%2Fdownload%2Fklona-1.1.2.tgz" - checksum: 357ff7d43ca2dbaacda91c37bea559642a128e583a2836c09c252881dd71d054d97e7185e91a899ebd65e2b76f7d4e25ca3d06fd0a0b51a5304586d00157a06d +"klona@npm:^2.0.3": + version: 2.0.3 + resolution: "klona@npm:2.0.3::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fklona%2Fdownload%2Fklona-2.0.3.tgz" + checksum: 69df085420a280e8d2e8c6564e23ba2bee2a03e8ca5f51e9409276bb54c6178a74adee0f94c2fc62caa584e1d2109e679abd8cc4f1114fbcd9e2886b909e8e62 languageName: node linkType: hard @@ -7713,7 +7769,7 @@ fsevents@~2.1.2: languageName: node linkType: hard -"merge2@npm:^1.2.3, merge2@npm:^1.3.0": +"merge2@npm:^1.3.0": version: 1.4.1 resolution: "merge2@npm:1.4.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fmerge2%2Fdownload%2Fmerge2-1.4.1.tgz" checksum: 7ad40d8b140a5ed4e621b916858410e4f0dd4ced1e5a2b675563347e70f0661d95ba6c3c8007dd3c4e242d0b8eee44559fa75bb90a146cf168debffc0cbc18f3 @@ -7840,9 +7896,9 @@ fsevents@~2.1.2: languageName: node linkType: hard -"mini-css-extract-plugin@npm:^0.10.0": - version: 0.10.0 - resolution: "mini-css-extract-plugin@npm:0.10.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fmini-css-extract-plugin%2Fdownload%2Fmini-css-extract-plugin-0.10.0.tgz" +"mini-css-extract-plugin@npm:^0.11.0": + version: 0.11.0 + resolution: "mini-css-extract-plugin@npm:0.11.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fmini-css-extract-plugin%2Fdownload%2Fmini-css-extract-plugin-0.11.0.tgz" dependencies: loader-utils: ^1.1.0 normalize-url: 1.9.1 @@ -7850,7 +7906,7 @@ fsevents@~2.1.2: webpack-sources: ^1.1.0 peerDependencies: webpack: ^4.4.0 || ^5.0.0 - checksum: 2e133cdd20e9de9acbaffc1820be4124432fe6b44078ab08f1eab4b632656ca9e226afb14e4703db8b873435aadecb4b5fa750e1dd439ed370f22897df247aad + checksum: 79a4a065274194461389237200ba66e255f6c7d0b8b928dd46f35a7fa13e2013aa0a761528aff4608a0e61f545192b57f56196e47891504a558c169588d6272c languageName: node linkType: hard @@ -7911,16 +7967,6 @@ fsevents@~2.1.2: languageName: node linkType: hard -"minipass@npm:^2.6.0, minipass@npm:^2.8.6, minipass@npm:^2.9.0": - version: 2.9.0 - resolution: "minipass@npm:2.9.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fminipass%2Fdownload%2Fminipass-2.9.0.tgz" - dependencies: - safe-buffer: ^5.1.2 - yallist: ^3.0.0 - checksum: 57a49f9523fdc495625184f4ef5a101615d3ee0c06f0c37e2ed7140c12deeecbd404539bd605b985100836006409b11b627a3148941dcc4ade24f0f078557836 - languageName: node - linkType: hard - "minipass@npm:^3.0.0, minipass@npm:^3.1.1": version: 3.1.3 resolution: "minipass@npm:3.1.3::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fminipass%2Fdownload%2Fminipass-3.1.3.tgz" @@ -7930,15 +7976,6 @@ fsevents@~2.1.2: languageName: node linkType: hard -"minizlib@npm:^1.2.1": - version: 1.3.3 - resolution: "minizlib@npm:1.3.3::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fminizlib%2Fdownload%2Fminizlib-1.3.3.tgz" - dependencies: - minipass: ^2.9.0 - checksum: 8d12782dd943ea92bb3e8e5dc4fe21201b56e77e5f12723c29159cf01dd0d50330dd071897dec270b3861994fb07a982b2473e5c2f42bf5f4b180ab18bf81c06 - languageName: node - linkType: hard - "minizlib@npm:^2.1.0": version: 2.1.0 resolution: "minizlib@npm:2.1.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fminizlib%2Fdownload%2Fminizlib-2.1.0.tgz" @@ -7977,7 +8014,7 @@ fsevents@~2.1.2: languageName: node linkType: hard -"mkdirp@npm:^0.5.0, mkdirp@npm:^0.5.1, mkdirp@npm:^0.5.3": +"mkdirp@npm:^0.5.1, mkdirp@npm:^0.5.3": version: 0.5.5 resolution: "mkdirp@npm:0.5.5::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fmkdirp%2Fdownload%2Fmkdirp-0.5.5.tgz" dependencies: @@ -8578,7 +8615,7 @@ fsevents@~2.1.2: languageName: node linkType: hard -"p-limit@npm:^3.0.1": +"p-limit@npm:^3.0.2": version: 3.0.2 resolution: "p-limit@npm:3.0.2::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fp-limit%2Fdownload%2Fp-limit-3.0.2.tgz" dependencies: @@ -9445,12 +9482,12 @@ fsevents@~2.1.2: languageName: node linkType: hard -"prettier@npm:^2.1.0": - version: 2.1.0 - resolution: "prettier@npm:2.1.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fprettier%2Fdownload%2Fprettier-2.1.0.tgz" +"prettier@npm:^2.1.1": + version: 2.1.1 + resolution: "prettier@npm:2.1.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fprettier%2Fdownload%2Fprettier-2.1.1.tgz" bin: prettier: bin-prettier.js - checksum: f955d380d2892284c28bbfe32b1601cfcd7a24d8b310832937773b9e733f624e38ff70cbafee80e9553bdb3c55783d019163667bf9a69df4e6f7c2acdcf54c06 + checksum: 420a807cda2d482388daa42379a7aaa1be629cb71f5792362a971a5ed1c6ff377d2e311818d24e9390a4a8a6eac3fda9ebe6975f2168efec893790c0497d8a93 languageName: node linkType: hard @@ -9771,16 +9808,16 @@ fsevents@~2.1.2: languageName: node linkType: hard -"react-i18next@npm:^11.7.1": - version: 11.7.1 - resolution: "react-i18next@npm:11.7.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Freact-i18next%2Fdownload%2Freact-i18next-11.7.1.tgz" +"react-i18next@npm:^11.7.2": + version: 11.7.2 + resolution: "react-i18next@npm:11.7.2::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Freact-i18next%2Fdownload%2Freact-i18next-11.7.2.tgz" dependencies: "@babel/runtime": ^7.3.1 html-parse-stringify2: 2.0.1 peerDependencies: i18next: ">= 19.0.0" react: ">= 16.8.0" - checksum: fc62ae35c36cc332a6f2cc3bf52a4d41ec7cdfe77614b998f37707a28c72a2d3410754460543475da50c3d0ee17fa0b4f8395fa72dc3af3c0274aabc808e5f9c + checksum: ea5c989d1b129d74b2d4b4d90cd33709f883cf89df90bcfbb78e3c97ac0c052cbf3369554e9e5beb405f1dbab5d7f7b77aa29fc0e13716d78d456f4f8408771c languageName: node linkType: hard @@ -9956,7 +9993,7 @@ fsevents@~2.1.2: languageName: node linkType: hard -"readable-stream@npm:^3.0.6, readable-stream@npm:^3.1.1, readable-stream@npm:^3.6.0": +"readable-stream@npm:^3.0.6, readable-stream@npm:^3.1.1, readable-stream@npm:^3.4.0, readable-stream@npm:^3.6.0": version: 3.6.0 resolution: "readable-stream@npm:3.6.0::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Freadable-stream%2Fdownload%2Freadable-stream-3.6.0.tgz" dependencies: @@ -10432,11 +10469,11 @@ fsevents@~2.1.2: languageName: node linkType: hard -"sass-loader@npm:^9.0.3": - version: 9.0.3 - resolution: "sass-loader@npm:9.0.3::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fsass-loader%2Fdownload%2Fsass-loader-9.0.3.tgz" +"sass-loader@npm:^10.0.1": + version: 10.0.1 + resolution: "sass-loader@npm:10.0.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fsass-loader%2Fdownload%2Fsass-loader-10.0.1.tgz" dependencies: - klona: ^1.1.2 + klona: ^2.0.3 loader-utils: ^2.0.0 neo-async: ^2.6.2 schema-utils: ^2.7.0 @@ -10453,7 +10490,7 @@ fsevents@~2.1.2: optional: true sass: optional: true - checksum: 5ce94bb6708507c3ca6af5cada1c3442d06242aa16df38fcea059aafad0c49ad0ae980fece7c3844cc82cc2e373d7b3192c55046bf506337811b759ccdb23a4f + checksum: cc517f3daeb1d74c602e935f427d08b4f0b2e1592cbbdd02ee55327506f18baf4069e8ae00db35507958c06ce789efd9810f86063c50b8e1f135ba8cd3770bdc languageName: node linkType: hard @@ -10500,6 +10537,17 @@ fsevents@~2.1.2: languageName: node linkType: hard +"schema-utils@npm:^2.7.1": + version: 2.7.1 + resolution: "schema-utils@npm:2.7.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fschema-utils%2Fdownload%2Fschema-utils-2.7.1.tgz" + dependencies: + "@types/json-schema": ^7.0.5 + ajv: ^6.12.4 + ajv-keywords: ^3.5.2 + checksum: 3851bcc7e44a3f35d3ca96e460c598aa24cec9fe395b196395316a043dc111d25735a9a49b1a115e4b52d5ed0d8bbcfb9fe1bfd077610f192b613e020d3f3ef2 + languageName: node + linkType: hard + "secure-compare@npm:3.0.1": version: 3.0.1 resolution: "secure-compare@npm:3.0.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fsecure-compare%2Fdownload%2Fsecure-compare-3.0.1.tgz" @@ -11322,18 +11370,16 @@ fsevents@~2.1.2: languageName: node linkType: hard -"tar@npm:^4.4.6": - version: 4.4.13 - resolution: "tar@npm:4.4.13::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Ftar%2Fdownload%2Ftar-4.4.13.tgz" +"tar-stream@npm:^2.0.1": + version: 2.1.3 + resolution: "tar-stream@npm:2.1.3::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Ftar-stream%2Fdownload%2Ftar-stream-2.1.3.tgz" dependencies: - chownr: ^1.1.1 - fs-minipass: ^1.2.5 - minipass: ^2.8.6 - minizlib: ^1.2.1 - mkdirp: ^0.5.0 - safe-buffer: ^5.1.2 - yallist: ^3.0.3 - checksum: d325c316ac329ecb18f2b8cd3f85a80ab4a4105ada601b9253aaafae3fc14268e3cd874ccc265b6a08e60ebd17fbc31bd3dbc0d1018f874b536eb2a6e8ef6d9c + bl: ^4.0.1 + end-of-stream: ^1.4.1 + fs-constants: ^1.0.0 + inherits: ^2.0.3 + readable-stream: ^3.1.1 + checksum: 57d2284615ee76ae5e41bcb8a1cb61c00b9ba514a1b8b86168d53967ea0f45581a3f00c8bdf7653fbd208490edea69ff39caaa845d7e04420cdddd56bea17e92 languageName: node linkType: hard @@ -11441,9 +11487,9 @@ fsevents@~2.1.2: "@hot-loader/react-dom": ^16.13.0 "@types/classnames": ^2.2.10 "@types/crypto-js": ^3.1.47 - "@types/lodash": ^4.14.159 - "@types/node": ^14.6.0 - "@types/react": ^16.9.46 + "@types/lodash": ^4.14.161 + "@types/node": ^14.6.2 + "@types/react": ^16.9.49 "@types/react-dom": ^16.9.8 "@types/react-responsive": ^8.0.2 "@types/react-router": ^5.1.8 @@ -11451,21 +11497,21 @@ fsevents@~2.1.2: "@types/reactstrap": ^8.5.1 "@types/webpack-env": ^1.15.2 "@types/xregexp": ^4.3.0 - "@typescript-eslint/eslint-plugin": ^3.9.1 - "@typescript-eslint/parser": ^3.9.1 - "@yarnpkg/pnpify": ^2.1.0 + "@typescript-eslint/eslint-plugin": ^4.0.0 + "@typescript-eslint/parser": ^4.0.0 + "@yarnpkg/pnpify": ^2.2.1 axios: ^0.20.0 babel-loader: ^8.1.0 babel-plugin-transform-builtin-extend: ^1.1.2 bootstrap: ^4.5.2 - bootstrap-icons: ^1.0.0-alpha5 + bootstrap-icons: ^1.0.0 classnames: ^2.2.6 clean-webpack-plugin: ^3.0.0 clsx: ^1.1.1 - copy-webpack-plugin: ^6.0.3 + copy-webpack-plugin: ^6.1.0 core-js: ^3.6.5 crypto-js: ^4.0.0 - css-loader: ^4.2.1 + css-loader: ^4.2.2 eslint: ^7.7.0 eslint-config-prettier: ^6.11.0 eslint-import-resolver-webpack: ^0.12.2 @@ -11473,23 +11519,23 @@ fsevents@~2.1.2: eslint-plugin-prettier: ^3.1.4 eslint-plugin-react: ^7.20.6 eslint-plugin-react-hooks: ^4.1.0 - file-loader: ^6.0.0 - html-webpack-plugin: ^4.3.0 + file-loader: ^6.1.0 + html-webpack-plugin: ^4.4.1 http-server: ^0.12.3 i18next: ^19.7.0 i18next-browser-languagedetector: ^6.0.1 localforage: ^1.9.0 lodash: ^4.17.20 - mini-css-extract-plugin: ^0.10.0 + mini-css-extract-plugin: ^0.11.0 pepjs: ^0.5.2 pnp-webpack-plugin: ^1.6.4 postcss-loader: ^3.0.0 postcss-preset-env: ^6.7.0 - prettier: ^2.1.0 + prettier: ^2.1.1 react: ^16.13.1 react-dom: ^16.13.1 react-hot-loader: ^4.12.21 - react-i18next: ^11.7.1 + react-i18next: ^11.7.2 react-inlinesvg: ^2.0.0 react-responsive: ^8.1.0 react-router: ^5.2.0 @@ -11498,7 +11544,7 @@ fsevents@~2.1.2: regenerator-runtime: ^0.13.7 rxjs: ^6.6.2 sass: ^1.26.10 - sass-loader: ^9.0.3 + sass-loader: ^10.0.1 style-loader: ^1.2.1 ts-loader: ^8.0.3 typescript: ^4.0.2 @@ -12594,7 +12640,7 @@ typescript@^4.0.2: languageName: node linkType: hard -"yallist@npm:^3.0.0, yallist@npm:^3.0.2, yallist@npm:^3.0.3": +"yallist@npm:^3.0.2": version: 3.1.1 resolution: "yallist@npm:3.1.1::__archiveUrl=https%3A%2F%2Fregistry.npm.taobao.org%2Fyallist%2Fdownload%2Fyallist-3.1.1.tgz" checksum: f352c93b92f601bb0399210bca37272e669c961e9bd886bac545380598765cbfdfb4f166e7b6c57ca4ec8a5af4ab3fa0fd78a47f9a7d655a3d580ff0fc9e7d79 -- cgit v1.2.3 From a314b5350e269676e8c39eda4cc7842751b1a7fc Mon Sep 17 00:00:00 2001 From: crupest Date: Tue, 1 Sep 2020 02:32:06 +0800 Subject: ... --- Timeline/ClientApp/.eslintrc.js | 17 +- Timeline/ClientApp/src/app/App.tsx | 24 +- Timeline/ClientApp/src/app/about/About.tsx | 172 ----- Timeline/ClientApp/src/app/about/about.sass | 4 - Timeline/ClientApp/src/app/about/author-avatar.png | Bin 12038 -> 0 bytes Timeline/ClientApp/src/app/about/github.png | Bin 4268 -> 0 bytes Timeline/ClientApp/src/app/admin/Admin.tsx | 78 --- Timeline/ClientApp/src/app/admin/UserAdmin.tsx | 463 -------------- Timeline/ClientApp/src/app/common/AlertHost.tsx | 96 --- Timeline/ClientApp/src/app/common/AppBar.tsx | 107 ---- Timeline/ClientApp/src/app/common/BlobImage.tsx | 29 - Timeline/ClientApp/src/app/common/FileInput.tsx | 41 -- Timeline/ClientApp/src/app/common/ImageCropper.tsx | 306 --------- Timeline/ClientApp/src/app/common/LoadingPage.tsx | 12 - .../ClientApp/src/app/common/OperationDialog.tsx | 381 ----------- Timeline/ClientApp/src/app/common/SearchInput.tsx | 63 -- Timeline/ClientApp/src/app/common/TimelineLogo.tsx | 26 - .../ClientApp/src/app/common/UserTimelineLogo.tsx | 26 - Timeline/ClientApp/src/app/common/alert-service.ts | 61 -- Timeline/ClientApp/src/app/common/alert.sass | 15 - Timeline/ClientApp/src/app/common/common.sass | 33 - Timeline/ClientApp/src/app/data/DataHub.ts | 225 ------- Timeline/ClientApp/src/app/data/common.ts | 23 - Timeline/ClientApp/src/app/data/timeline.ts | 702 --------------------- Timeline/ClientApp/src/app/data/user.ts | 392 ------------ Timeline/ClientApp/src/app/home/BoardWithUser.tsx | 101 --- .../ClientApp/src/app/home/BoardWithoutUser.tsx | 60 -- Timeline/ClientApp/src/app/home/Home.tsx | 102 --- Timeline/ClientApp/src/app/home/OfflineBoard.tsx | 61 -- Timeline/ClientApp/src/app/home/TimelineBoard.tsx | 73 --- .../src/app/home/TimelineCreateDialog.tsx | 53 -- Timeline/ClientApp/src/app/home/home.sass | 13 - Timeline/ClientApp/src/app/index.sass | 14 +- Timeline/ClientApp/src/app/service-worker.tsx | 2 +- Timeline/ClientApp/src/app/services/DataHub.ts | 225 +++++++ Timeline/ClientApp/src/app/services/alert.ts | 61 ++ Timeline/ClientApp/src/app/services/common.ts | 23 + Timeline/ClientApp/src/app/services/timeline.ts | 702 +++++++++++++++++++++ Timeline/ClientApp/src/app/services/user.ts | 393 ++++++++++++ Timeline/ClientApp/src/app/settings/Settings.tsx | 221 ------- Timeline/ClientApp/src/app/timeline/Timeline.tsx | 88 --- .../src/app/timeline/TimelineDeleteDialog.tsx | 54 -- .../src/app/timeline/TimelineInfoCard.tsx | 110 ---- .../ClientApp/src/app/timeline/TimelineItem.tsx | 181 ------ .../ClientApp/src/app/timeline/TimelineMember.tsx | 218 ------- .../ClientApp/src/app/timeline/TimelinePage.tsx | 37 -- .../src/app/timeline/TimelinePageTemplate.tsx | 190 ------ .../src/app/timeline/TimelinePageTemplateUI.tsx | 324 ---------- .../ClientApp/src/app/timeline/TimelinePageUI.tsx | 21 - .../src/app/timeline/TimelinePostEdit.tsx | 232 ------- .../app/timeline/TimelinePropertyChangeDialog.tsx | 71 --- .../ClientApp/src/app/timeline/timeline-ui.sass | 35 - Timeline/ClientApp/src/app/timeline/timeline.sass | 131 ---- .../ClientApp/src/app/user/ChangeAvatarDialog.tsx | 306 --------- .../src/app/user/ChangeNicknameDialog.tsx | 28 - Timeline/ClientApp/src/app/user/Login.tsx | 147 ----- Timeline/ClientApp/src/app/user/User.tsx | 71 --- Timeline/ClientApp/src/app/user/UserInfoCard.tsx | 104 --- Timeline/ClientApp/src/app/user/UserPage.tsx | 19 - Timeline/ClientApp/src/app/user/user-page.sass | 10 - Timeline/ClientApp/src/app/utilities/type.ts | 1 - Timeline/ClientApp/src/app/views/about/about.sass | 4 + .../src/app/views/about/author-avatar.png | Bin 0 -> 12038 bytes Timeline/ClientApp/src/app/views/about/github.png | Bin 0 -> 4268 bytes Timeline/ClientApp/src/app/views/about/index.tsx | 172 +++++ Timeline/ClientApp/src/app/views/admin/Admin.tsx | 78 +++ .../ClientApp/src/app/views/admin/UserAdmin.tsx | 463 ++++++++++++++ Timeline/ClientApp/src/app/views/common/AppBar.tsx | 107 ++++ .../ClientApp/src/app/views/common/BlobImage.tsx | 27 + .../ClientApp/src/app/views/common/FileInput.tsx | 36 ++ .../src/app/views/common/ImageCropper.tsx | 306 +++++++++ .../ClientApp/src/app/views/common/LoadingPage.tsx | 12 + .../src/app/views/common/OperationDialog.tsx | 381 +++++++++++ .../ClientApp/src/app/views/common/SearchInput.tsx | 63 ++ .../src/app/views/common/TimelineLogo.tsx | 26 + .../src/app/views/common/UserTimelineLogo.tsx | 26 + .../src/app/views/common/alert/AlertHost.tsx | 96 +++ .../src/app/views/common/alert/alert.sass | 15 + .../ClientApp/src/app/views/common/common.sass | 33 + .../ClientApp/src/app/views/home/BoardWithUser.tsx | 101 +++ .../src/app/views/home/BoardWithoutUser.tsx | 60 ++ .../ClientApp/src/app/views/home/OfflineBoard.tsx | 61 ++ .../ClientApp/src/app/views/home/TimelineBoard.tsx | 73 +++ .../src/app/views/home/TimelineCreateDialog.tsx | 53 ++ Timeline/ClientApp/src/app/views/home/home.sass | 13 + Timeline/ClientApp/src/app/views/home/index.tsx | 102 +++ Timeline/ClientApp/src/app/views/login/index.tsx | 148 +++++ .../ClientApp/src/app/views/settings/index.tsx | 221 +++++++ .../src/app/views/timeline-common/Timeline.tsx | 88 +++ .../src/app/views/timeline-common/TimelineItem.tsx | 182 ++++++ .../app/views/timeline-common/TimelineMember.tsx | 219 +++++++ .../views/timeline-common/TimelinePageTemplate.tsx | 189 ++++++ .../timeline-common/TimelinePageTemplateUI.tsx | 325 ++++++++++ .../app/views/timeline-common/TimelinePostEdit.tsx | 234 +++++++ .../TimelinePropertyChangeDialog.tsx | 72 +++ .../app/views/timeline-common/timeline-common.sass | 149 +++++ .../app/views/timeline/TimelineDeleteDialog.tsx | 55 ++ .../src/app/views/timeline/TimelineInfoCard.tsx | 110 ++++ .../src/app/views/timeline/TimelinePageUI.tsx | 20 + .../ClientApp/src/app/views/timeline/index.tsx | 37 ++ .../ClientApp/src/app/views/timeline/timeline.sass | 14 + .../src/app/views/user/ChangeAvatarDialog.tsx | 307 +++++++++ .../src/app/views/user/ChangeNicknameDialog.tsx | 28 + .../ClientApp/src/app/views/user/UserInfoCard.tsx | 105 +++ .../ClientApp/src/app/views/user/UserPageUI.tsx | 18 + Timeline/ClientApp/src/app/views/user/index.tsx | 72 +++ Timeline/ClientApp/src/app/views/user/user.sass | 10 + Timeline/ClientApp/src/tsconfig.json | 8 +- Timeline/ClientApp/webpack.common.js | 2 + 109 files changed, 6345 insertions(+), 6354 deletions(-) delete mode 100644 Timeline/ClientApp/src/app/about/About.tsx delete mode 100644 Timeline/ClientApp/src/app/about/about.sass delete mode 100644 Timeline/ClientApp/src/app/about/author-avatar.png delete mode 100644 Timeline/ClientApp/src/app/about/github.png delete mode 100644 Timeline/ClientApp/src/app/admin/Admin.tsx delete mode 100644 Timeline/ClientApp/src/app/admin/UserAdmin.tsx delete mode 100644 Timeline/ClientApp/src/app/common/AlertHost.tsx delete mode 100644 Timeline/ClientApp/src/app/common/AppBar.tsx delete mode 100644 Timeline/ClientApp/src/app/common/BlobImage.tsx delete mode 100644 Timeline/ClientApp/src/app/common/FileInput.tsx delete mode 100644 Timeline/ClientApp/src/app/common/ImageCropper.tsx delete mode 100644 Timeline/ClientApp/src/app/common/LoadingPage.tsx delete mode 100644 Timeline/ClientApp/src/app/common/OperationDialog.tsx delete mode 100644 Timeline/ClientApp/src/app/common/SearchInput.tsx delete mode 100644 Timeline/ClientApp/src/app/common/TimelineLogo.tsx delete mode 100644 Timeline/ClientApp/src/app/common/UserTimelineLogo.tsx delete mode 100644 Timeline/ClientApp/src/app/common/alert-service.ts delete mode 100644 Timeline/ClientApp/src/app/common/alert.sass delete mode 100644 Timeline/ClientApp/src/app/common/common.sass delete mode 100644 Timeline/ClientApp/src/app/data/DataHub.ts delete mode 100644 Timeline/ClientApp/src/app/data/common.ts delete mode 100644 Timeline/ClientApp/src/app/data/timeline.ts delete mode 100644 Timeline/ClientApp/src/app/data/user.ts delete mode 100644 Timeline/ClientApp/src/app/home/BoardWithUser.tsx delete mode 100644 Timeline/ClientApp/src/app/home/BoardWithoutUser.tsx delete mode 100644 Timeline/ClientApp/src/app/home/Home.tsx delete mode 100644 Timeline/ClientApp/src/app/home/OfflineBoard.tsx delete mode 100644 Timeline/ClientApp/src/app/home/TimelineBoard.tsx delete mode 100644 Timeline/ClientApp/src/app/home/TimelineCreateDialog.tsx delete mode 100644 Timeline/ClientApp/src/app/home/home.sass create mode 100644 Timeline/ClientApp/src/app/services/DataHub.ts create mode 100644 Timeline/ClientApp/src/app/services/alert.ts create mode 100644 Timeline/ClientApp/src/app/services/common.ts create mode 100644 Timeline/ClientApp/src/app/services/timeline.ts create mode 100644 Timeline/ClientApp/src/app/services/user.ts delete mode 100644 Timeline/ClientApp/src/app/settings/Settings.tsx delete mode 100644 Timeline/ClientApp/src/app/timeline/Timeline.tsx delete mode 100644 Timeline/ClientApp/src/app/timeline/TimelineDeleteDialog.tsx delete mode 100644 Timeline/ClientApp/src/app/timeline/TimelineInfoCard.tsx delete mode 100644 Timeline/ClientApp/src/app/timeline/TimelineItem.tsx delete mode 100644 Timeline/ClientApp/src/app/timeline/TimelineMember.tsx delete mode 100644 Timeline/ClientApp/src/app/timeline/TimelinePage.tsx delete mode 100644 Timeline/ClientApp/src/app/timeline/TimelinePageTemplate.tsx delete mode 100644 Timeline/ClientApp/src/app/timeline/TimelinePageTemplateUI.tsx delete mode 100644 Timeline/ClientApp/src/app/timeline/TimelinePageUI.tsx delete mode 100644 Timeline/ClientApp/src/app/timeline/TimelinePostEdit.tsx delete mode 100644 Timeline/ClientApp/src/app/timeline/TimelinePropertyChangeDialog.tsx delete mode 100644 Timeline/ClientApp/src/app/timeline/timeline-ui.sass delete mode 100644 Timeline/ClientApp/src/app/timeline/timeline.sass delete mode 100644 Timeline/ClientApp/src/app/user/ChangeAvatarDialog.tsx delete mode 100644 Timeline/ClientApp/src/app/user/ChangeNicknameDialog.tsx delete mode 100644 Timeline/ClientApp/src/app/user/Login.tsx delete mode 100644 Timeline/ClientApp/src/app/user/User.tsx delete mode 100644 Timeline/ClientApp/src/app/user/UserInfoCard.tsx delete mode 100644 Timeline/ClientApp/src/app/user/UserPage.tsx delete mode 100644 Timeline/ClientApp/src/app/user/user-page.sass delete mode 100644 Timeline/ClientApp/src/app/utilities/type.ts create mode 100644 Timeline/ClientApp/src/app/views/about/about.sass create mode 100644 Timeline/ClientApp/src/app/views/about/author-avatar.png create mode 100644 Timeline/ClientApp/src/app/views/about/github.png create mode 100644 Timeline/ClientApp/src/app/views/about/index.tsx create mode 100644 Timeline/ClientApp/src/app/views/admin/Admin.tsx create mode 100644 Timeline/ClientApp/src/app/views/admin/UserAdmin.tsx create mode 100644 Timeline/ClientApp/src/app/views/common/AppBar.tsx create mode 100644 Timeline/ClientApp/src/app/views/common/BlobImage.tsx create mode 100644 Timeline/ClientApp/src/app/views/common/FileInput.tsx create mode 100644 Timeline/ClientApp/src/app/views/common/ImageCropper.tsx create mode 100644 Timeline/ClientApp/src/app/views/common/LoadingPage.tsx create mode 100644 Timeline/ClientApp/src/app/views/common/OperationDialog.tsx create mode 100644 Timeline/ClientApp/src/app/views/common/SearchInput.tsx create mode 100644 Timeline/ClientApp/src/app/views/common/TimelineLogo.tsx create mode 100644 Timeline/ClientApp/src/app/views/common/UserTimelineLogo.tsx create mode 100644 Timeline/ClientApp/src/app/views/common/alert/AlertHost.tsx create mode 100644 Timeline/ClientApp/src/app/views/common/alert/alert.sass create mode 100644 Timeline/ClientApp/src/app/views/common/common.sass create mode 100644 Timeline/ClientApp/src/app/views/home/BoardWithUser.tsx create mode 100644 Timeline/ClientApp/src/app/views/home/BoardWithoutUser.tsx create mode 100644 Timeline/ClientApp/src/app/views/home/OfflineBoard.tsx create mode 100644 Timeline/ClientApp/src/app/views/home/TimelineBoard.tsx create mode 100644 Timeline/ClientApp/src/app/views/home/TimelineCreateDialog.tsx create mode 100644 Timeline/ClientApp/src/app/views/home/home.sass create mode 100644 Timeline/ClientApp/src/app/views/home/index.tsx create mode 100644 Timeline/ClientApp/src/app/views/login/index.tsx create mode 100644 Timeline/ClientApp/src/app/views/settings/index.tsx create mode 100644 Timeline/ClientApp/src/app/views/timeline-common/Timeline.tsx create mode 100644 Timeline/ClientApp/src/app/views/timeline-common/TimelineItem.tsx create mode 100644 Timeline/ClientApp/src/app/views/timeline-common/TimelineMember.tsx create mode 100644 Timeline/ClientApp/src/app/views/timeline-common/TimelinePageTemplate.tsx create mode 100644 Timeline/ClientApp/src/app/views/timeline-common/TimelinePageTemplateUI.tsx create mode 100644 Timeline/ClientApp/src/app/views/timeline-common/TimelinePostEdit.tsx create mode 100644 Timeline/ClientApp/src/app/views/timeline-common/TimelinePropertyChangeDialog.tsx create mode 100644 Timeline/ClientApp/src/app/views/timeline-common/timeline-common.sass create mode 100644 Timeline/ClientApp/src/app/views/timeline/TimelineDeleteDialog.tsx create mode 100644 Timeline/ClientApp/src/app/views/timeline/TimelineInfoCard.tsx create mode 100644 Timeline/ClientApp/src/app/views/timeline/TimelinePageUI.tsx create mode 100644 Timeline/ClientApp/src/app/views/timeline/index.tsx create mode 100644 Timeline/ClientApp/src/app/views/timeline/timeline.sass create mode 100644 Timeline/ClientApp/src/app/views/user/ChangeAvatarDialog.tsx create mode 100644 Timeline/ClientApp/src/app/views/user/ChangeNicknameDialog.tsx create mode 100644 Timeline/ClientApp/src/app/views/user/UserInfoCard.tsx create mode 100644 Timeline/ClientApp/src/app/views/user/UserPageUI.tsx create mode 100644 Timeline/ClientApp/src/app/views/user/index.tsx create mode 100644 Timeline/ClientApp/src/app/views/user/user.sass diff --git a/Timeline/ClientApp/.eslintrc.js b/Timeline/ClientApp/.eslintrc.js index 830a2aa4..900489ed 100644 --- a/Timeline/ClientApp/.eslintrc.js +++ b/Timeline/ClientApp/.eslintrc.js @@ -1,5 +1,3 @@ -const path = require("path"); - module.exports = { env: { browser: true, @@ -15,8 +13,6 @@ module.exports = { "prettier/react", "prettier/@typescript-eslint", "plugin:react-hooks/recommended", - "plugin:import/recommended", - "plugin:import/typescript", ], globals: { Atomics: "readonly", @@ -30,16 +26,11 @@ module.exports = { }, sourceType: "module", }, - plugins: ["react", "@typescript-eslint", "react-hooks", "import"], + plugins: ["react", "@typescript-eslint", "react-hooks"], settings: { react: { version: "detect", }, - "import/resolver": { - webpack: { - config: path.resolve(__dirname, "webpack.config.dev.js"), - }, - }, }, rules: { "react/prop-types": "off", @@ -52,11 +43,5 @@ module.exports = { allowHigherOrderFunctions: true, }, ], - "import/order": [ - "warn", - { - "newlines-between": "always", - }, - ], }, }; diff --git a/Timeline/ClientApp/src/app/App.tsx b/Timeline/ClientApp/src/app/App.tsx index 74deddda..b64414b7 100644 --- a/Timeline/ClientApp/src/app/App.tsx +++ b/Timeline/ClientApp/src/app/App.tsx @@ -2,17 +2,17 @@ import React from "react"; import { BrowserRouter as Router, Route, Switch } from "react-router-dom"; import { hot } from "react-hot-loader/root"; -import AppBar from "./common/AppBar"; -import LoadingPage from "./common/LoadingPage"; -import Home from "./home/Home"; -import Login from "./user/Login"; -import Settings from "./settings/Settings"; -import About from "./about/About"; -import User from "./user/User"; -import TimelinePage from "./timeline/TimelinePage"; -import AlertHost from "./common/AlertHost"; -import { dataStorage } from "./data/common"; -import { userService, useRawUser } from "./data/user"; +import AppBar from "./views/common/AppBar"; +import LoadingPage from "./views/common/LoadingPage"; +import Home from "./views/home"; +import Login from "./views/login"; +import Settings from "./views/settings"; +import About from "./views/about"; +import User from "./views/user"; +import TimelinePage from "./views/timeline"; +import AlertHost from "./views/common/alert/AlertHost"; +import { dataStorage } from "./services/common"; +import { userService, useRawUser } from "./services/user"; const NoMatch: React.FC = () => { return ( @@ -25,7 +25,7 @@ const NoMatch: React.FC = () => { }; const LazyAdmin = React.lazy( - () => import(/* webpackChunkName: "admin" */ "./admin/Admin") + () => import(/* webpackChunkName: "admin" */ "./views/admin/Admin") ); const App: React.FC = () => { diff --git a/Timeline/ClientApp/src/app/about/About.tsx b/Timeline/ClientApp/src/app/about/About.tsx deleted file mode 100644 index 519eef18..00000000 --- a/Timeline/ClientApp/src/app/about/About.tsx +++ /dev/null @@ -1,172 +0,0 @@ -import React from "react"; -import { useTranslation, Trans } from "react-i18next"; - -import AppBar from "../common/AppBar"; - -import authorAvatarUrl from "./author-avatar.png"; -import githubLogoUrl from "./github.png"; - -const frontendCredits: { - name: string; - url: string; -}[] = [ - { - name: "reactjs", - url: "https://reactjs.org", - }, - { - name: "typescript", - url: "https://www.typescriptlang.org", - }, - { - name: "bootstrap", - url: "https://getbootstrap.com", - }, - { - name: "reactstrap", - url: "https://reactstrap.github.io", - }, - { - name: "babeljs", - url: "https://babeljs.io", - }, - { - name: "webpack", - url: "https://webpack.js.org", - }, - { - name: "sass", - url: "https://sass-lang.com", - }, - { - name: "eslint", - url: "https://eslint.org", - }, - { - name: "prettier", - url: "https://prettier.io", - }, - { - name: "pepjs", - url: "https://github.com/jquery/PEP", - }, - { - name: "react-inlinesvg", - url: "https://github.com/gilbarbara/react-inlinesvg", - }, -]; - -const backendCredits: { - name: string; - url: string; -}[] = [ - { - name: "ASP.NET Core", - url: "https://dotnet.microsoft.com/learn/aspnet/what-is-aspnet-core", - }, - { name: "sqlite", url: "https://sqlite.org" }, - { - name: "ImageSharp", - url: "https://github.com/SixLabors/ImageSharp", - }, -]; - -const About: React.FC = () => { - const { t } = useTranslation(); - - return ( - <> - -
-
-

{t("about.author.title")}

-
-
- -
-

- {t("about.author.fullname")} - 杨宇千 -

-

- {t("about.author.nickname")} - crupest -

-

- {t("about.author.introduction")} - {t("about.author.introductionContent")} -

-
-
-

- {t("about.author.links")} - - - -

-
-
-
-

{t("about.site.title")}

-

- - 01234 - 56 - -

-

- - {t("about.site.repo")} - -

-
-
-

{t("about.credits.title")}

-

{t("about.credits.content")}

-

{t("about.credits.frontend")}

-
    - {frontendCredits.map((item, index) => { - return ( -
  • - - {item.name} - -
  • - ); - })} -
  • ...
  • -
-

{t("about.credits.backend")}

-
    - {backendCredits.map((item, index) => { - return ( -
  • - - {item.name} - -
  • - ); - })} -
  • ...
  • -
-
-
- - ); -}; - -export default About; diff --git a/Timeline/ClientApp/src/app/about/about.sass b/Timeline/ClientApp/src/app/about/about.sass deleted file mode 100644 index 3b5840cd..00000000 --- a/Timeline/ClientApp/src/app/about/about.sass +++ /dev/null @@ -1,4 +0,0 @@ -.about-link-icon - @extend .mx-2 - width: 1.2em - height: 1.2em diff --git a/Timeline/ClientApp/src/app/about/author-avatar.png b/Timeline/ClientApp/src/app/about/author-avatar.png deleted file mode 100644 index d890d8d0..00000000 Binary files a/Timeline/ClientApp/src/app/about/author-avatar.png and /dev/null differ diff --git a/Timeline/ClientApp/src/app/about/github.png b/Timeline/ClientApp/src/app/about/github.png deleted file mode 100644 index ea6ff545..00000000 Binary files a/Timeline/ClientApp/src/app/about/github.png and /dev/null differ diff --git a/Timeline/ClientApp/src/app/admin/Admin.tsx b/Timeline/ClientApp/src/app/admin/Admin.tsx deleted file mode 100644 index e2f71091..00000000 --- a/Timeline/ClientApp/src/app/admin/Admin.tsx +++ /dev/null @@ -1,78 +0,0 @@ -import React, { Fragment } from "react"; -import { Nav, NavItem, NavLink } from "reactstrap"; -import { - Redirect, - Route, - Switch, - useRouteMatch, - useHistory, -} from "react-router"; -import classnames from "classnames"; - -import AppBar from "../common/AppBar"; -import { UserWithToken } from "../data/user"; - -import UserAdmin from "./UserAdmin"; - -interface AdminProps { - user: UserWithToken; -} - -const Admin: React.FC = (props) => { - const match = useRouteMatch(); - const history = useHistory(); - type TabNames = "users" | "more"; - - const tabName = history.location.pathname.replace(match.path + "/", ""); - - function toggle(newTab: TabNames): void { - history.push(`${match.url}/${newTab}`); - } - - const createRoute = ( - name: string, - body: React.ReactNode - ): React.ReactNode => { - return ( - - -
- - {body} - - ); - }; - - return ( - - - - {createRoute("users", )} - {createRoute("more",
More Page Works
)} -
-
- ); -}; - -export default Admin; diff --git a/Timeline/ClientApp/src/app/admin/UserAdmin.tsx b/Timeline/ClientApp/src/app/admin/UserAdmin.tsx deleted file mode 100644 index 1bf3bda1..00000000 --- a/Timeline/ClientApp/src/app/admin/UserAdmin.tsx +++ /dev/null @@ -1,463 +0,0 @@ -import React, { useState, useEffect } from "react"; -import { - ListGroupItem, - Row, - Col, - UncontrolledDropdown, - DropdownToggle, - DropdownMenu, - DropdownItem, - Spinner, - Button, -} from "reactstrap"; -import axios from "axios"; - -import OperationDialog from "../common/OperationDialog"; -import { User, UserWithToken } from "../data/user"; - -const apiBaseUrl = "/api"; - -async function fetchUserList(_token: string): Promise { - const res = await axios.get(`${apiBaseUrl}/users`); - return res.data; -} - -interface CreateUserInfo { - username: string; - password: string; - administrator: boolean; -} - -async function createUser(user: CreateUserInfo, token: string): Promise { - const res = await axios.post( - `${apiBaseUrl}/userop/createuser?token=${token}`, - user - ); - return res.data; -} - -function deleteUser(username: string, token: string): Promise { - return axios.delete(`${apiBaseUrl}/users/${username}?token=${token}`); -} - -function changeUsername( - oldUsername: string, - newUsername: string, - token: string -): Promise { - return axios.patch(`${apiBaseUrl}/users/${oldUsername}?token=${token}`, { - username: newUsername, - }); -} - -function changePassword( - username: string, - newPassword: string, - token: string -): Promise { - return axios.patch(`${apiBaseUrl}/users/${username}?token=${token}`, { - password: newPassword, - }); -} - -function changePermission( - username: string, - newPermission: boolean, - token: string -): Promise { - return axios.patch(`${apiBaseUrl}/users/${username}?token=${token}`, { - administrator: newPermission, - }); -} - -const kChangeUsername = "changeusername"; -const kChangePassword = "changepassword"; -const kChangePermission = "changepermission"; -const kDelete = "delete"; - -type TChangeUsername = typeof kChangeUsername; -type TChangePassword = typeof kChangePassword; -type TChangePermission = typeof kChangePermission; -type TDelete = typeof kDelete; - -type ContextMenuItem = - | TChangeUsername - | TChangePassword - | TChangePermission - | TDelete; - -interface UserCardProps { - onContextMenu: (item: ContextMenuItem) => void; - user: User; -} - -const UserItem: React.FC = (props) => { - const user = props.user; - - const createClickCallback = (item: ContextMenuItem): (() => void) => { - return () => { - props.onContextMenu(item); - }; - }; - - return ( - - - -

{user.username}

- - {user.administrator ? "administrator" : "user"} - - - - - - Manage - - - - Change Username - - - Change Password - - - Change Permission - - - Delete - - - - -
-
- ); -}; - -interface DialogProps { - open: boolean; - close: () => void; -} - -interface CreateUserDialogProps extends DialogProps { - process: (user: CreateUserInfo) => Promise; -} - -const CreateUserDialog: React.FC = (props) => { - return ( - - props.process({ - username: username as string, - password: password as string, - administrator: administrator as boolean, - }) - } - close={props.close} - open={props.open} - /> - ); -}; - -const UsernameLabel: React.FC = (props) => { - return {props.children}; -}; - -interface UserDeleteDialogProps extends DialogProps { - username: string; - process: () => Promise; -} - -const UserDeleteDialog: React.FC = (props) => { - return ( - ( - <> - {"You are deleting user "} - {props.username} - {" !"} - - )} - onProcess={props.process} - /> - ); -}; - -interface UserModifyDialogProps extends DialogProps { - username: string; - process: (value: T) => Promise; -} - -const UserChangeUsernameDialog: React.FC> = ( - props -) => { - return ( - ( - <> - {"You are change the username of user "} - {props.username} - {" !"} - - )} - inputScheme={[{ type: "text", label: "New Username" }]} - onProcess={([newUsername]) => { - return props.process(newUsername as string); - }} - /> - ); -}; - -const UserChangePasswordDialog: React.FC> = ( - props -) => { - return ( - ( - <> - {"You are change the password of user "} - {props.username} - {" !"} - - )} - inputScheme={[{ type: "text", label: "New Password" }]} - onProcess={([newPassword]) => { - return props.process(newPassword as string); - }} - /> - ); -}; - -interface UserChangePermissionDialogProps extends DialogProps { - username: string; - newPermission: boolean; - process: () => Promise; -} - -const UserChangePermissionDialog: React.FC = ( - props -) => { - return ( - ( - <> - {"You are change user "} - {props.username} - {" to "} - - {props.newPermission ? "administrator" : "normal user"} - - {" !"} - - )} - onProcess={props.process} - /> - ); -}; - -interface UserAdminProps { - user: UserWithToken; -} - -const UserAdmin: React.FC = (props) => { - type DialogInfo = - | null - | { - type: "create"; - } - | { type: TDelete; username: string } - | { - type: TChangeUsername; - username: string; - } - | { - type: TChangePassword; - username: string; - } - | { - type: TChangePermission; - username: string; - newPermission: boolean; - }; - - const [users, setUsers] = useState(null); - const [dialog, setDialog] = useState(null); - - const token = props.user.token; - - useEffect(() => { - let subscribe = true; - void fetchUserList(props.user.token).then((us) => { - if (subscribe) { - setUsers(us); - } - }); - return () => { - subscribe = false; - }; - }, [props.user]); - - let dialogNode: React.ReactNode; - if (dialog) - switch (dialog.type) { - case "create": - dialogNode = ( - setDialog(null)} - process={async (user) => { - const u = await createUser(user, token); - setUsers((oldUsers) => [...(oldUsers ?? []), u]); - }} - /> - ); - break; - case "delete": - dialogNode = ( - setDialog(null)} - username={dialog.username} - process={async () => { - await deleteUser(dialog.username, token); - setUsers((oldUsers) => - (oldUsers ?? []).filter((u) => u.username !== dialog.username) - ); - }} - /> - ); - break; - case kChangeUsername: - dialogNode = ( - setDialog(null)} - username={dialog.username} - process={async (newUsername) => { - await changeUsername(dialog.username, newUsername, token); - setUsers((oldUsers) => { - const users = (oldUsers ?? []).slice(); - const findedUser = users.find( - (u) => u.username === dialog.username - ); - if (findedUser) findedUser.username = newUsername; - return users; - }); - }} - /> - ); - break; - case kChangePassword: - dialogNode = ( - setDialog(null)} - username={dialog.username} - process={async (newPassword) => { - await changePassword(dialog.username, newPassword, token); - }} - /> - ); - break; - case kChangePermission: { - const newPermission = dialog.newPermission; - dialogNode = ( - setDialog(null)} - username={dialog.username} - newPermission={newPermission} - process={async () => { - await changePermission(dialog.username, newPermission, token); - setUsers((oldUsers) => { - const users = (oldUsers ?? []).slice(); - const findedUser = users.find( - (u) => u.username === dialog.username - ); - if (findedUser) findedUser.administrator = newPermission; - return users; - }); - }} - /> - ); - break; - } - } - - if (users) { - const userComponents = users.map((user) => { - return ( - { - setDialog( - item === kChangePermission - ? { - type: kChangePermission, - username: user.username, - newPermission: !user.administrator, - } - : { - type: item, - username: user.username, - } - ); - }} - /> - ); - }); - - return ( - <> - - {userComponents} - {dialogNode} - - ); - } else { - return ; - } -}; - -export default UserAdmin; diff --git a/Timeline/ClientApp/src/app/common/AlertHost.tsx b/Timeline/ClientApp/src/app/common/AlertHost.tsx deleted file mode 100644 index bfcf5c00..00000000 --- a/Timeline/ClientApp/src/app/common/AlertHost.tsx +++ /dev/null @@ -1,96 +0,0 @@ -import React, { useCallback } from "react"; -import { Alert } from "reactstrap"; -import without from "lodash/without"; -import concat from "lodash/concat"; -import { useTranslation } from "react-i18next"; - -import { - alertService, - AlertInfoEx, - kAlertHostId, - AlertInfo, -} from "./alert-service"; - -interface AutoCloseAlertProps { - alert: AlertInfo; - close: () => void; -} - -export const AutoCloseAlert: React.FC = (props) => { - const { alert } = props; - const { dismissTime } = alert; - - const { t } = useTranslation(); - - React.useEffect(() => { - const tag = - dismissTime === "never" - ? null - : typeof dismissTime === "number" - ? window.setTimeout(props.close, dismissTime) - : window.setTimeout(props.close, 5000); - return () => { - if (tag != null) { - window.clearTimeout(tag); - } - }; - }, [dismissTime, props.close]); - - return ( - - {(() => { - const { message } = alert; - if (typeof message === "function") { - const Message = message; - return ; - } else if (typeof message === "object" && message.type === "i18n") { - return t(message.key); - } else return alert.message; - })()} - - ); -}; - -// oh what a bad name! -interface AlertInfoExEx extends AlertInfoEx { - close: () => void; -} - -const AlertHost: React.FC = () => { - const [alerts, setAlerts] = React.useState([]); - - // react guarantee that state setters are stable, so we don't need to add it to dependency list - - const consume = useCallback((alert: AlertInfoEx): void => { - const alertEx: AlertInfoExEx = { - ...alert, - close: () => { - setAlerts((oldAlerts) => { - return without(oldAlerts, alertEx); - }); - }, - }; - setAlerts((oldAlerts) => { - return concat(oldAlerts, alertEx); - }); - }, []); - - React.useEffect(() => { - alertService.registerConsumer(consume); - return () => { - alertService.unregisterConsumer(consume); - }; - }, [consume]); - - return ( -
- {alerts.map((alert) => { - return ( - - ); - })} -
- ); -}; - -export default AlertHost; diff --git a/Timeline/ClientApp/src/app/common/AppBar.tsx b/Timeline/ClientApp/src/app/common/AppBar.tsx deleted file mode 100644 index 59239696..00000000 --- a/Timeline/ClientApp/src/app/common/AppBar.tsx +++ /dev/null @@ -1,107 +0,0 @@ -import React from "react"; -import { useHistory, matchPath } from "react-router"; -import { Link, NavLink } from "react-router-dom"; -import { Navbar, NavbarToggler, Collapse, Nav, NavItem } from "reactstrap"; -import { useMediaQuery } from "react-responsive"; -import { useTranslation } from "react-i18next"; - -import { useUser, useAvatar } from "../data/user"; - -import TimelineLogo from "./TimelineLogo"; -import BlobImage from "./BlobImage"; - -const AppBar: React.FC = (_) => { - const history = useHistory(); - const user = useUser(); - const avatar = useAvatar(user?.username); - - const { t } = useTranslation(); - - const isUpMd = useMediaQuery({ - minWidth: getComputedStyle(document.documentElement).getPropertyValue( - "--breakpoint-md" - ), - }); - - const [isMenuOpen, setIsMenuOpen] = React.useState(false); - - const toggleMenu = React.useCallback((): void => { - setIsMenuOpen((oldIsMenuOpen) => !oldIsMenuOpen); - }, []); - - const isAdministrator = user && user.administrator; - - const rightArea = ( -
- {user != null ? ( - - - - ) : ( - - {t("nav.login")} - - )} -
- ); - - return ( - - - - Timeline - - - {isUpMd ? null : rightArea} - - - - - {isUpMd ? rightArea : null} - - - ); -}; - -export default AppBar; diff --git a/Timeline/ClientApp/src/app/common/BlobImage.tsx b/Timeline/ClientApp/src/app/common/BlobImage.tsx deleted file mode 100644 index 8602f550..00000000 --- a/Timeline/ClientApp/src/app/common/BlobImage.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import React from "react"; - -import { ExcludeKey } from "../utilities/type"; - -const BlobImage: React.FC< - ExcludeKey, "src"> & { - blob?: Blob | unknown; - } -> = (props) => { - const { blob, ...otherProps } = props; - - const [url, setUrl] = React.useState(undefined); - - React.useEffect(() => { - if (blob instanceof Blob) { - const url = URL.createObjectURL(blob); - setUrl(url); - return () => { - URL.revokeObjectURL(url); - }; - } else { - setUrl(undefined); - } - }, [blob]); - - return ; -}; - -export default BlobImage; diff --git a/Timeline/ClientApp/src/app/common/FileInput.tsx b/Timeline/ClientApp/src/app/common/FileInput.tsx deleted file mode 100644 index 3d1bc2b3..00000000 --- a/Timeline/ClientApp/src/app/common/FileInput.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import React from "react"; -import clsx from "clsx"; - -import { ExcludeKey } from "../utilities/type"; - -export interface FileInputProps - extends ExcludeKey< - React.InputHTMLAttributes, - "type" | "id" - > { - inputId?: string; - labelText: string; - color?: string; - className?: string; -} - -const FileInput: React.FC = (props) => { - const { inputId, labelText, color, className, ...otherProps } = props; - - const realInputId = React.useMemo(() => { - if (inputId != null) return inputId; - return ( - "file-input-" + - (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1) - ); - }, [inputId]); - - return ( - <> - - - - ); -}; - -export default FileInput; diff --git a/Timeline/ClientApp/src/app/common/ImageCropper.tsx b/Timeline/ClientApp/src/app/common/ImageCropper.tsx deleted file mode 100644 index cd510969..00000000 --- a/Timeline/ClientApp/src/app/common/ImageCropper.tsx +++ /dev/null @@ -1,306 +0,0 @@ -import * as React from "react"; -import clsx from "clsx"; - -import { UiLogicError } from "../common"; - -export interface Clip { - left: number; - top: number; - width: number; -} - -interface NormailizedClip extends Clip { - height: number; -} - -interface ImageInfo { - width: number; - height: number; - landscape: boolean; - ratio: number; - maxClipWidth: number; - maxClipHeight: number; -} - -interface ImageCropperSavedState { - clip: NormailizedClip; - x: number; - y: number; - pointerId: number; -} - -export interface ImageCropperProps { - clip: Clip | null; - imageUrl: string; - onChange: (clip: Clip) => void; - imageElementCallback?: (element: HTMLImageElement | null) => void; - className?: string; -} - -const ImageCropper = (props: ImageCropperProps): React.ReactElement => { - const { clip, imageUrl, onChange, imageElementCallback, className } = props; - - const [oldState, setOldState] = React.useState( - null - ); - const [imageInfo, setImageInfo] = React.useState(null); - - const normalizeClip = (c: Clip | null | undefined): NormailizedClip => { - if (c == null) { - return { left: 0, top: 0, width: 0, height: 0 }; - } - - return { - left: c.left || 0, - top: c.top || 0, - width: c.width || 0, - height: imageInfo != null ? (c.width || 0) / imageInfo.ratio : 0, - }; - }; - - const c = normalizeClip(clip); - - const imgElementRef = React.useRef(null); - - const onImageRef = React.useCallback( - (e: HTMLImageElement | null) => { - imgElementRef.current = e; - if (imageElementCallback != null && e == null) { - imageElementCallback(null); - } - }, - [imageElementCallback] - ); - - const onImageLoad = React.useCallback( - (e: React.SyntheticEvent) => { - const img = e.currentTarget; - const landscape = img.naturalWidth >= img.naturalHeight; - - const info = { - width: img.naturalWidth, - height: img.naturalHeight, - landscape, - ratio: img.naturalHeight / img.naturalWidth, - maxClipWidth: landscape ? img.naturalHeight / img.naturalWidth : 1, - maxClipHeight: landscape ? 1 : img.naturalWidth / img.naturalHeight, - }; - setImageInfo(info); - onChange({ left: 0, top: 0, width: info.maxClipWidth }); - if (imageElementCallback != null) { - imageElementCallback(img); - } - }, - [onChange, imageElementCallback] - ); - - const onPointerDown = React.useCallback( - (e: React.PointerEvent) => { - if (oldState != null) return; - e.currentTarget.setPointerCapture(e.pointerId); - setOldState({ - x: e.clientX, - y: e.clientY, - clip: c, - pointerId: e.pointerId, - }); - }, - [oldState, c] - ); - - const onPointerUp = React.useCallback( - (e: React.PointerEvent) => { - if (oldState == null || oldState.pointerId !== e.pointerId) return; - e.currentTarget.releasePointerCapture(e.pointerId); - setOldState(null); - }, - [oldState] - ); - - const onPointerMove = React.useCallback( - (e: React.PointerEvent) => { - if (oldState == null) return; - - const oldClip = oldState.clip; - - const movement = { x: e.clientX - oldState.x, y: e.clientY - oldState.y }; - - const { current: imgElement } = imgElementRef; - - if (imgElement == null) throw new UiLogicError("Image element is null."); - - const moveRatio = { - x: movement.x / imgElement.width, - y: movement.y / imgElement.height, - }; - - const newRatio = { - x: oldClip.left + moveRatio.x, - y: oldClip.top + moveRatio.y, - }; - if (newRatio.x < 0) { - newRatio.x = 0; - } else if (newRatio.x > 1 - oldClip.width) { - newRatio.x = 1 - oldClip.width; - } - if (newRatio.y < 0) { - newRatio.y = 0; - } else if (newRatio.y > 1 - oldClip.height) { - newRatio.y = 1 - oldClip.height; - } - - onChange({ left: newRatio.x, top: newRatio.y, width: oldClip.width }); - }, - [oldState, onChange] - ); - - const onHandlerPointerMove = React.useCallback( - (e: React.PointerEvent) => { - if (oldState == null) return; - - const oldClip = oldState.clip; - - const movement = { x: e.clientX - oldState.x, y: e.clientY - oldState.y }; - - const ratio = imageInfo == null ? 1 : imageInfo.ratio; - - const { current: imgElement } = imgElementRef; - - if (imgElement == null) throw new UiLogicError("Image element is null."); - - const moveRatio = { - x: movement.x / imgElement.width, - y: movement.x / imgElement.width / ratio, - }; - - const newRatio = { - x: oldClip.width + moveRatio.x, - y: oldClip.height + moveRatio.y, - }; - - const maxRatio = { - x: Math.min(1 - oldClip.left, newRatio.x), - y: Math.min(1 - oldClip.top, newRatio.y), - }; - - const maxWidthRatio = Math.min(maxRatio.x, maxRatio.y * ratio); - - let newWidth; - if (newRatio.x < 0) { - newWidth = 0; - } else if (newRatio.x > maxWidthRatio) { - newWidth = maxWidthRatio; - } else { - newWidth = newRatio.x; - } - - onChange({ left: oldClip.left, top: oldClip.top, width: newWidth }); - }, - [imageInfo, oldState, onChange] - ); - - const toPercentage = (n: number): string => `${n}%`; - - // fuck!!! I just can't find a better way to implement this in pure css - const containerStyle: React.CSSProperties = (() => { - if (imageInfo == null) { - return { width: "100%", paddingTop: "100%", height: 0 }; - } else { - if (imageInfo.ratio > 1) { - return { - width: toPercentage(100 / imageInfo.ratio), - paddingTop: "100%", - height: 0, - }; - } else { - return { - width: "100%", - paddingTop: toPercentage(100 * imageInfo.ratio), - height: 0, - }; - } - } - })(); - - return ( -
- to crop -
-
-
-
-
- ); -}; - -export default ImageCropper; - -export function applyClipToImage( - image: HTMLImageElement, - clip: Clip, - mimeType: string -): Promise { - return new Promise((resolve, reject) => { - const naturalSize = { - width: image.naturalWidth, - height: image.naturalHeight, - }; - const clipArea = { - x: naturalSize.width * clip.left, - y: naturalSize.height * clip.top, - length: naturalSize.width * clip.width, - }; - - const canvas = document.createElement("canvas"); - canvas.width = clipArea.length; - canvas.height = clipArea.length; - const context = canvas.getContext("2d"); - - if (context == null) throw new Error("Failed to create context."); - - context.drawImage( - image, - clipArea.x, - clipArea.y, - clipArea.length, - clipArea.length, - 0, - 0, - clipArea.length, - clipArea.length - ); - - canvas.toBlob((blob) => { - if (blob == null) { - reject(new Error("canvas.toBlob returns null")); - } else { - resolve(blob); - } - }, mimeType); - }); -} diff --git a/Timeline/ClientApp/src/app/common/LoadingPage.tsx b/Timeline/ClientApp/src/app/common/LoadingPage.tsx deleted file mode 100644 index a849126d..00000000 --- a/Timeline/ClientApp/src/app/common/LoadingPage.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import React from "react"; -import { Spinner } from "reactstrap"; - -const LoadingPage: React.FC = () => { - return ( -
- -
- ); -}; - -export default LoadingPage; diff --git a/Timeline/ClientApp/src/app/common/OperationDialog.tsx b/Timeline/ClientApp/src/app/common/OperationDialog.tsx deleted file mode 100644 index bca4580c..00000000 --- a/Timeline/ClientApp/src/app/common/OperationDialog.tsx +++ /dev/null @@ -1,381 +0,0 @@ -import React, { useState } from "react"; -import { useTranslation } from "react-i18next"; -import { - Spinner, - Container, - ModalBody, - Label, - Input, - FormGroup, - FormFeedback, - ModalFooter, - Button, - Modal, - ModalHeader, - FormText, -} from "reactstrap"; - -import { UiLogicError } from "../common"; - -const DefaultProcessPrompt: React.FC = (_) => { - return ( - - - - ); -}; - -interface DefaultErrorPromptProps { - error?: string; -} - -const DefaultErrorPrompt: React.FC = (props) => { - const { t } = useTranslation(); - - let result =

{t("operationDialog.error")}

; - - if (props.error != null) { - result = ( - <> - {result} -

{props.error}

- - ); - } - - return result; -}; - -export type OperationInputOptionalError = undefined | null | string; - -export interface OperationInputErrorInfo { - [index: number]: OperationInputOptionalError; -} - -export type OperationInputValidator = ( - value: TValue, - values: (string | boolean)[] -) => OperationInputOptionalError | OperationInputErrorInfo; - -export interface OperationTextInputInfo { - type: "text"; - password?: boolean; - label?: string; - initValue?: string; - textFieldProps?: Omit< - React.InputHTMLAttributes, - "type" | "value" | "onChange" - >; - helperText?: string; - validator?: OperationInputValidator; -} - -export interface OperationBoolInputInfo { - type: "bool"; - label: string; - initValue?: boolean; -} - -export interface OperationSelectInputInfoOption { - value: string; - label: string; - icon?: React.ReactElement; -} - -export interface OperationSelectInputInfo { - type: "select"; - label: string; - options: OperationSelectInputInfoOption[]; - initValue?: string; -} - -export type OperationInputInfo = - | OperationTextInputInfo - | OperationBoolInputInfo - | OperationSelectInputInfo; - -interface OperationResult { - type: "success" | "failure"; - data: unknown; -} - -interface OperationDialogProps { - open: boolean; - close: () => void; - title: React.ReactNode; - titleColor?: "default" | "dangerous" | "create" | string; - onProcess: (inputs: (string | boolean)[]) => Promise; - inputScheme?: OperationInputInfo[]; - inputPrompt?: string | (() => React.ReactNode); - processPrompt?: () => React.ReactNode; - successPrompt?: (data: unknown) => React.ReactNode; - failurePrompt?: (error: unknown) => React.ReactNode; - onSuccessAndClose?: () => void; -} - -const OperationDialog: React.FC = (props) => { - const inputScheme = props.inputScheme ?? []; - - const { t } = useTranslation(); - - type Step = "input" | "process" | OperationResult; - const [step, setStep] = useState("input"); - const [values, setValues] = useState<(boolean | string)[]>( - inputScheme.map((i) => { - if (i.type === "bool") { - return i.initValue ?? false; - } else if (i.type === "text" || i.type === "select") { - return i.initValue ?? ""; - } else { - throw new UiLogicError("Unknown input scheme."); - } - }) - ); - const [inputError, setInputError] = useState({}); - - const close = (): void => { - if (step !== "process") { - props.close(); - if ( - typeof step === "object" && - step.type === "success" && - props.onSuccessAndClose - ) { - props.onSuccessAndClose(); - } - } else { - console.log("Attempt to close modal when processing."); - } - }; - - const onConfirm = (): void => { - setStep("process"); - props.onProcess(values).then( - (d: unknown) => { - setStep({ - type: "success", - data: d, - }); - }, - (e: unknown) => { - setStep({ - type: "failure", - data: e, - }); - } - ); - }; - - let body: React.ReactNode; - if (step === "input") { - let inputPrompt = - typeof props.inputPrompt === "function" - ? props.inputPrompt() - : props.inputPrompt; - inputPrompt =
{inputPrompt}
; - - const updateValue = ( - index: number, - newValue: string | boolean - ): (string | boolean)[] => { - const oldValues = values; - const newValues = oldValues.slice(); - newValues[index] = newValue; - setValues(newValues); - return newValues; - }; - - const testErrorInfo = (errorInfo: OperationInputErrorInfo): boolean => { - for (let i = 0; i < inputScheme.length; i++) { - if (inputScheme[i].type === "text" && errorInfo[i] != null) { - return true; - } - } - return false; - }; - - const calculateError = ( - oldError: OperationInputErrorInfo, - index: number, - newError: OperationInputOptionalError | OperationInputErrorInfo - ): OperationInputErrorInfo => { - if (newError === undefined) { - return oldError; - } else if (newError === null || typeof newError === "string") { - return { ...oldError, [index]: newError }; - } else { - const newInputError: OperationInputErrorInfo = { ...oldError }; - for (const [index, error] of Object.entries(newError)) { - if (error !== undefined) { - newInputError[+index] = error as OperationInputOptionalError; - } - } - return newInputError; - } - }; - - const validateAll = (): boolean => { - let newInputError = inputError; - for (let i = 0; i < inputScheme.length; i++) { - const item = inputScheme[i]; - if (item.type === "text") { - newInputError = calculateError( - newInputError, - i, - item.validator?.(values[i] as string, values) - ); - } - } - const result = !testErrorInfo(newInputError); - setInputError(newInputError); - return result; - }; - - body = ( - <> - - {inputPrompt} - {inputScheme.map((item, index) => { - const value = values[index]; - const error: string | undefined = ((e) => - typeof e === "string" ? t(e) : undefined)(inputError?.[index]); - - if (item.type === "text") { - return ( - - {item.label && } - { - const v = e.target.value; - const newValues = updateValue(index, v); - setInputError( - calculateError( - inputError, - index, - item.validator?.(v, newValues) - ) - ); - }} - invalid={error != null} - {...item.textFieldProps} - /> - {error != null && {error}} - {item.helperText && {t(item.helperText)}} - - ); - } else if (item.type === "bool") { - return ( - - { - updateValue( - index, - (e.target as HTMLInputElement).checked - ); - }} - /> - - - ); - } else if (item.type === "select") { - return ( - - - { - updateValue(index, event.target.value); - }} - > - {item.options.map((option, i) => { - return ( - - ); - })} - - - ); - } - })} - - - - - - - ); - } else if (step === "process") { - body = ( - - {props.processPrompt?.() ?? } - - ); - } else { - let content: React.ReactNode; - const result = step; - if (result.type === "success") { - content = - props.successPrompt?.(result.data) ?? t("operationDialog.success"); - if (typeof content === "string") - content =

{content}

; - } else { - content = props.failurePrompt?.(result.data) ?? ; - if (typeof content === "string") - content = ; - } - body = ( - <> - {content} - - - - - ); - } - - const title = typeof props.title === "string" ? t(props.title) : props.title; - - return ( - - - {title} - - {body} - - ); -}; - -export default OperationDialog; diff --git a/Timeline/ClientApp/src/app/common/SearchInput.tsx b/Timeline/ClientApp/src/app/common/SearchInput.tsx deleted file mode 100644 index 5a0b0eaa..00000000 --- a/Timeline/ClientApp/src/app/common/SearchInput.tsx +++ /dev/null @@ -1,63 +0,0 @@ -import React, { useCallback } from "react"; -import clsx from "clsx"; -import { Spinner, Input, Button } from "reactstrap"; -import { useTranslation } from "react-i18next"; - -export interface SearchInputProps { - value: string; - onChange: (value: string) => void; - onButtonClick: () => void; - className?: string; - loading?: boolean; - buttonText?: string; - placeholder?: string; - additionalButton?: React.ReactNode; -} - -const SearchInput: React.FC = (props) => { - const { onChange, onButtonClick } = props; - - const { t } = useTranslation(); - - const onInputChange = useCallback( - (event: React.ChangeEvent): void => { - onChange(event.currentTarget.value); - }, - [onChange] - ); - - const onInputKeyPress = useCallback( - (event: React.KeyboardEvent): void => { - if (event.key === "Enter") { - onButtonClick(); - } - }, - [onButtonClick] - ); - - return ( -
- -
- {props.additionalButton} -
-
- {props.loading ? ( - - ) : ( - - )} -
-
- ); -}; - -export default SearchInput; diff --git a/Timeline/ClientApp/src/app/common/TimelineLogo.tsx b/Timeline/ClientApp/src/app/common/TimelineLogo.tsx deleted file mode 100644 index 27d188fc..00000000 --- a/Timeline/ClientApp/src/app/common/TimelineLogo.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import React, { SVGAttributes } from "react"; - -export interface TimelineLogoProps extends SVGAttributes { - color?: string; -} - -const TimelineLogo: React.FC = (props) => { - const { color, ...forwardProps } = props; - const coercedColor = color ?? "currentcolor"; - return ( - - - - - - ); -}; - -export default TimelineLogo; diff --git a/Timeline/ClientApp/src/app/common/UserTimelineLogo.tsx b/Timeline/ClientApp/src/app/common/UserTimelineLogo.tsx deleted file mode 100644 index 29f6a69f..00000000 --- a/Timeline/ClientApp/src/app/common/UserTimelineLogo.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import React, { SVGAttributes } from "react"; - -export interface UserTimelineLogoProps extends SVGAttributes { - color?: string; -} - -const UserTimelineLogo: React.FC = (props) => { - const { color, ...forwardProps } = props; - const coercedColor = color ?? "currentcolor"; - - return ( - - - - - - - - - - - - ); -}; - -export default UserTimelineLogo; diff --git a/Timeline/ClientApp/src/app/common/alert-service.ts b/Timeline/ClientApp/src/app/common/alert-service.ts deleted file mode 100644 index e4c0e653..00000000 --- a/Timeline/ClientApp/src/app/common/alert-service.ts +++ /dev/null @@ -1,61 +0,0 @@ -import React from "react"; -import pull from "lodash/pull"; - -export interface AlertInfo { - type?: "primary" | "secondary" | "success" | "danger" | "warning" | "info"; - message: string | React.FC | { type: "i18n"; key: string }; - dismissTime?: number | "never"; -} - -export interface AlertInfoEx extends AlertInfo { - id: number; -} - -export type AlertConsumer = (alerts: AlertInfoEx) => void; - -export class AlertService { - private consumers: AlertConsumer[] = []; - private savedAlerts: AlertInfoEx[] = []; - private currentId = 1; - - private produce(alert: AlertInfoEx): void { - for (const consumer of this.consumers) { - consumer(alert); - } - } - - registerConsumer(consumer: AlertConsumer): void { - this.consumers.push(consumer); - if (this.savedAlerts.length !== 0) { - for (const alert of this.savedAlerts) { - this.produce(alert); - } - this.savedAlerts = []; - } - } - - unregisterConsumer(consumer: AlertConsumer): void { - pull(this.consumers, consumer); - } - - push(alert: AlertInfo): void { - const newAlert: AlertInfoEx = { ...alert, id: this.currentId++ }; - if (this.consumers.length === 0) { - this.savedAlerts.push(newAlert); - } else { - this.produce(newAlert); - } - } -} - -export const alertService = new AlertService(); - -export function pushAlert(alert: AlertInfo): void { - alertService.push(alert); -} - -export const kAlertHostId = "alert-host"; - -export function getAlertHost(): HTMLElement | null { - return document.getElementById(kAlertHostId); -} diff --git a/Timeline/ClientApp/src/app/common/alert.sass b/Timeline/ClientApp/src/app/common/alert.sass deleted file mode 100644 index 5b6e65c2..00000000 --- a/Timeline/ClientApp/src/app/common/alert.sass +++ /dev/null @@ -1,15 +0,0 @@ -.alert-container - position: fixed - z-index: $zindex-popover - -@include media-breakpoint-up(sm) - .alert-container - bottom: 0 - right: 0 - -@include media-breakpoint-down(sm) - .alert-container - bottom: 0 - right: 0 - left: 0 - text-align: center diff --git a/Timeline/ClientApp/src/app/common/common.sass b/Timeline/ClientApp/src/app/common/common.sass deleted file mode 100644 index 15d34d7c..00000000 --- a/Timeline/ClientApp/src/app/common/common.sass +++ /dev/null @@ -1,33 +0,0 @@ -.image-cropper-container - position: relative - box-sizing: border-box - user-select: none - -.image-cropper-container img - position: absolute - left: 0 - top: 0 - width: 100% - height: 100% - -.image-cropper-mask-container - position: absolute - left: 0 - top: 0 - right: 0 - bottom: 0 - overflow: hidden - -.image-cropper-mask - position: absolute - box-shadow: 0 0 0 10000px rgba(255, 255, 255, 80%) - touch-action: none - -.image-cropper-handler - position: absolute - width: 26px - height: 26px - border: black solid 2px - border-radius: 50% - background: white - touch-action: none diff --git a/Timeline/ClientApp/src/app/data/DataHub.ts b/Timeline/ClientApp/src/app/data/DataHub.ts deleted file mode 100644 index 93a9b41f..00000000 --- a/Timeline/ClientApp/src/app/data/DataHub.ts +++ /dev/null @@ -1,225 +0,0 @@ -import { pull } from "lodash"; -import { Observable, BehaviorSubject, combineLatest } from "rxjs"; -import { map } from "rxjs/operators"; - -export type Subscriber = (data: TData) => void; - -export type WithSyncStatus = T & { syncing: boolean }; - -export class DataLine { - private _current: TData | undefined = undefined; - - private _syncPromise: Promise | null = null; - private _syncingSubject = new BehaviorSubject(false); - - private _observers: Subscriber[] = []; - - constructor( - private config: { - sync: () => Promise; - destroyable?: (value: TData | undefined) => boolean; - disableInitSync?: boolean; - } - ) { - if (config.disableInitSync !== true) { - setImmediate(() => void this.sync()); - } - } - - private subscribe(subscriber: Subscriber): void { - this._observers.push(subscriber); - if (this._current !== undefined) { - subscriber(this._current); - } - } - - private unsubscribe(subscriber: Subscriber): void { - if (!this._observers.includes(subscriber)) return; - pull(this._observers, subscriber); - } - - getObservable(): Observable { - return new Observable((observer) => { - const f = (data: TData): void => { - observer.next(data); - }; - this.subscribe(f); - - return () => { - this.unsubscribe(f); - }; - }); - } - - getSyncStatusObservable(): Observable { - return this._syncingSubject.asObservable(); - } - - getDataWithSyncStatusObservable(): Observable> { - return combineLatest([ - this.getObservable(), - this.getSyncStatusObservable(), - ]).pipe( - map(([data, syncing]) => ({ - ...data, - syncing, - })) - ); - } - - get value(): TData | undefined { - return this._current; - } - - next(value: TData): void { - this._current = value; - this._observers.forEach((observer) => observer(value)); - } - - get isSyncing(): boolean { - return this._syncPromise != null; - } - - sync(): Promise { - if (this._syncPromise == null) { - this._syncingSubject.next(true); - this._syncPromise = this.config.sync().then(() => { - this._syncingSubject.next(false); - this._syncPromise = null; - }); - } - - return this._syncPromise; - } - - syncWithAction( - syncAction: (line: DataLine) => Promise - ): Promise { - if (this._syncPromise == null) { - this._syncingSubject.next(true); - this._syncPromise = syncAction(this).then(() => { - this._syncingSubject.next(false); - this._syncPromise = null; - }); - } - - return this._syncPromise; - } - - get destroyable(): boolean { - const customDestroyable = this.config?.destroyable; - - return ( - this._observers.length === 0 && - !this.isSyncing && - (customDestroyable != null ? customDestroyable(this._current) : true) - ); - } -} - -export class DataHub { - private sync: (key: TKey, line: DataLine) => Promise; - private keyToString: (key: TKey) => string; - private destroyable?: (key: TKey, value: TData | undefined) => boolean; - - private readonly subscriptionLineMap = new Map>(); - - private cleanTimerId = 0; - - // setup is called after creating line and if it returns a function as destroyer, then when the line is destroyed the destroyer will be called. - constructor(config: { - sync: (key: TKey, line: DataLine) => Promise; - keyToString?: (key: TKey) => string; - destroyable?: (key: TKey, value: TData | undefined) => boolean; - }) { - this.sync = config.sync; - this.keyToString = - config.keyToString ?? - ((value): string => { - if (typeof value === "string") return value; - else - throw new Error( - "Default keyToString function only pass string value." - ); - }); - - this.destroyable = config.destroyable; - } - - private cleanLines(): void { - const toDelete: string[] = []; - for (const [key, line] of this.subscriptionLineMap.entries()) { - if (line.destroyable) { - toDelete.push(key); - } - } - - if (toDelete.length === 0) return; - - for (const key of toDelete) { - this.subscriptionLineMap.delete(key); - } - - if (this.subscriptionLineMap.size === 0) { - window.clearInterval(this.cleanTimerId); - this.cleanTimerId = 0; - } - } - - private createLine(key: TKey, disableInitSync = false): DataLine { - const keyString = this.keyToString(key); - const { destroyable } = this; - const newLine: DataLine = new DataLine({ - sync: () => this.sync(key, newLine), - destroyable: - destroyable != null ? (value) => destroyable(key, value) : undefined, - disableInitSync: disableInitSync, - }); - this.subscriptionLineMap.set(keyString, newLine); - if (this.subscriptionLineMap.size === 1) { - this.cleanTimerId = window.setInterval(this.cleanLines.bind(this), 20000); - } - return newLine; - } - - getObservable(key: TKey): Observable { - return this.getLineOrCreate(key).getObservable(); - } - - getSyncStatusObservable(key: TKey): Observable { - return this.getLineOrCreate(key).getSyncStatusObservable(); - } - - getDataWithSyncStatusObservable( - key: TKey - ): Observable> { - return this.getLineOrCreate(key).getDataWithSyncStatusObservable(); - } - - getLine(key: TKey): DataLine | null { - const keyString = this.keyToString(key); - return this.subscriptionLineMap.get(keyString) ?? null; - } - - getLineOrCreate(key: TKey): DataLine { - const keyString = this.keyToString(key); - return this.subscriptionLineMap.get(keyString) ?? this.createLine(key); - } - - getLineOrCreateWithoutInitSync(key: TKey): DataLine { - const keyString = this.keyToString(key); - return ( - this.subscriptionLineMap.get(keyString) ?? this.createLine(key, true) - ); - } - - optionalInitLineWithSyncAction( - key: TKey, - syncAction: (line: DataLine) => Promise - ): Promise { - const optionalLine = this.getLine(key); - if (optionalLine != null) return Promise.resolve(); - const line = this.createLine(key, true); - return line.syncWithAction(syncAction); - } -} diff --git a/Timeline/ClientApp/src/app/data/common.ts b/Timeline/ClientApp/src/app/data/common.ts deleted file mode 100644 index 8d52abe5..00000000 --- a/Timeline/ClientApp/src/app/data/common.ts +++ /dev/null @@ -1,23 +0,0 @@ -import localforage from "localforage"; - -import { HttpNetworkError } from "../http/common"; - -export const dataStorage = localforage.createInstance({ - name: "data", - description: "Database for offline data.", - driver: localforage.INDEXEDDB, -}); - -export class ForbiddenError extends Error { - constructor(message?: string) { - super(message); - } -} - -export function throwIfNotNetworkError(e: unknown): void { - if (!(e instanceof HttpNetworkError)) { - throw e; - } -} - -export type BlobOrStatus = Blob | "loading" | "error"; diff --git a/Timeline/ClientApp/src/app/data/timeline.ts b/Timeline/ClientApp/src/app/data/timeline.ts deleted file mode 100644 index 3eda35f9..00000000 --- a/Timeline/ClientApp/src/app/data/timeline.ts +++ /dev/null @@ -1,702 +0,0 @@ -import React from "react"; -import XRegExp from "xregexp"; -import { Observable, from, combineLatest, of } from "rxjs"; -import { map, switchMap, startWith } from "rxjs/operators"; -import { uniqBy } from "lodash"; - -import { convertError } from "../utilities/rxjs"; -import { - TimelineVisibility, - HttpTimelineInfo, - HttpTimelinePatchRequest, - HttpTimelinePostPostRequest, - HttpTimelinePostPostRequestContent, - HttpTimelinePostPostRequestTextContent, - HttpTimelinePostPostRequestImageContent, - HttpTimelinePostInfo, - HttpTimelinePostTextContent, - getHttpTimelineClient, - HttpTimelineNotExistError, - HttpTimelineNameConflictError, -} from "../http/timeline"; -import { BlobWithEtag, NotModified, HttpForbiddenError } from "../http/common"; -import { HttpUser } from "../http/user"; - -import { dataStorage, throwIfNotNetworkError, BlobOrStatus } from "./common"; -import { DataHub, WithSyncStatus } from "./DataHub"; -import { UserAuthInfo, checkLogin, userService, userInfoService } from "./user"; - -export { kTimelineVisibilities } from "../http/timeline"; - -export type { TimelineVisibility } from "../http/timeline"; - -export type TimelineInfo = HttpTimelineInfo; -export type TimelineChangePropertyRequest = HttpTimelinePatchRequest; -export type TimelineCreatePostRequest = HttpTimelinePostPostRequest; -export type TimelineCreatePostContent = HttpTimelinePostPostRequestContent; -export type TimelineCreatePostTextContent = HttpTimelinePostPostRequestTextContent; -export type TimelineCreatePostImageContent = HttpTimelinePostPostRequestImageContent; - -export type TimelinePostTextContent = HttpTimelinePostTextContent; - -export interface TimelinePostImageContent { - type: "image"; - data: BlobOrStatus; -} - -export type TimelinePostContent = - | TimelinePostTextContent - | TimelinePostImageContent; - -export interface TimelinePostInfo { - id: number; - content: TimelinePostContent; - time: Date; - lastUpdated: Date; - author: HttpUser; -} - -export const timelineVisibilityTooltipTranslationMap: Record< - TimelineVisibility, - string -> = { - Public: "timeline.visibilityTooltip.public", - Register: "timeline.visibilityTooltip.register", - Private: "timeline.visibilityTooltip.private", -}; - -export class TimelineNotExistError extends Error {} -export class TimelineNameConflictError extends Error {} - -export type TimelineWithSyncStatus = WithSyncStatus< - | { - type: "cache"; - timeline: TimelineInfo; - } - | { - type: "offline" | "synced"; - timeline: TimelineInfo | null; - } ->; - -export type TimelinePostsWithSyncState = WithSyncStatus<{ - type: - | "cache" - | "offline" // Sync failed and use cache. - | "synced" // Sync succeeded. - | "forbid" // The list is forbidden to see. - | "notexist"; // The timeline does not exist. - posts: TimelinePostInfo[]; -}>; - -type TimelineData = Omit & { - owner: string; - members: string[]; -}; - -type TimelinePostData = Omit & { - author: string; -}; - -export class TimelineService { - private getCachedTimeline( - timelineName: string - ): Promise { - return dataStorage.getItem(`timeline.${timelineName}`); - } - - private saveTimeline( - timelineName: string, - data: TimelineData - ): Promise { - return dataStorage - .setItem(`timeline.${timelineName}`, data) - .then(); - } - - private async clearTimelineData(timelineName: string): Promise { - const keys = (await dataStorage.keys()).filter((k) => - k.startsWith(`timeline.${timelineName}`) - ); - await Promise.all(keys.map((k) => dataStorage.removeItem(k))); - } - - private convertHttpTimelineToData(timeline: HttpTimelineInfo): TimelineData { - return { - ...timeline, - owner: timeline.owner.username, - members: timeline.members.map((m) => m.username), - }; - } - - private _timelineHub = new DataHub< - string, - | { - type: "cache"; - timeline: TimelineData; - } - | { - type: "offline" | "synced"; - timeline: TimelineData | null; - } - >({ - sync: async (key, line) => { - const cache = await this.getCachedTimeline(key); - - if (line.value == undefined) { - if (cache != null) { - line.next({ type: "cache", timeline: cache }); - } - } - - try { - const httpTimeline = await getHttpTimelineClient().getTimeline(key); - - userInfoService.saveUsers([ - httpTimeline.owner, - ...httpTimeline.members, - ]); - - const timeline = this.convertHttpTimelineToData(httpTimeline); - - if (cache != null && timeline.uniqueId !== cache.uniqueId) { - console.log( - `Timeline with name ${key} has changed to a new one. Clear old data.` - ); - await this.clearTimelineData(key); // If timeline has changed, clear all old data. - } - - await this.saveTimeline(key, timeline); - - line.next({ type: "synced", timeline }); - } catch (e) { - if (e instanceof HttpTimelineNotExistError) { - line.next({ type: "synced", timeline: null }); - } else { - if (cache == null) { - line.next({ type: "offline", timeline: null }); - } else { - line.next({ type: "offline", timeline: cache }); - } - throwIfNotNetworkError(e); - } - } - }, - }); - - syncTimeline(timelineName: string): Promise { - return this._timelineHub.getLineOrCreate(timelineName).sync(); - } - - getTimeline$(timelineName: string): Observable { - return this._timelineHub.getDataWithSyncStatusObservable(timelineName).pipe( - switchMap((state) => { - const { timeline } = state; - if (timeline != null) { - return combineLatest( - [timeline.owner, ...timeline.members].map((u) => - userInfoService.getUser$(u) - ) - ).pipe( - map((users) => { - return { - ...state, - timeline: { - ...timeline, - owner: users[0], - members: users.slice(1), - }, - }; - }) - ); - } else { - return of(state as TimelineWithSyncStatus); - } - }) - ); - } - - createTimeline(timelineName: string): Observable { - const user = checkLogin(); - return from( - getHttpTimelineClient().postTimeline( - { - name: timelineName, - }, - user.token - ) - ).pipe( - convertError(HttpTimelineNameConflictError, TimelineNameConflictError) - ); - } - - changeTimelineProperty( - timelineName: string, - req: TimelineChangePropertyRequest - ): Observable { - const user = checkLogin(); - return from( - getHttpTimelineClient() - .patchTimeline(timelineName, req, user.token) - .then((timeline) => { - void this.syncTimeline(timelineName); - return timeline; - }) - ); - } - - deleteTimeline(timelineName: string): Observable { - const user = checkLogin(); - return from( - getHttpTimelineClient().deleteTimeline(timelineName, user.token) - ); - } - - addMember(timelineName: string, username: string): Observable { - const user = checkLogin(); - return from( - getHttpTimelineClient() - .memberPut(timelineName, username, user.token) - .then(() => { - void this.syncTimeline(timelineName); - }) - ); - } - - removeMember(timelineName: string, username: string): Observable { - const user = checkLogin(); - return from( - getHttpTimelineClient() - .memberDelete(timelineName, username, user.token) - .then(() => { - void this.syncTimeline(timelineName); - }) - ); - } - - private convertHttpPostToData(post: HttpTimelinePostInfo): TimelinePostData { - return { - ...post, - author: post.author.username, - }; - } - - private convertHttpPostToDataList( - posts: HttpTimelinePostInfo[] - ): TimelinePostData[] { - return posts.map((post) => this.convertHttpPostToData(post)); - } - - private getCachedPosts( - timelineName: string - ): Promise { - return dataStorage.getItem( - `timeline.${timelineName}.posts` - ); - } - - private savePosts( - timelineName: string, - data: TimelinePostData[] - ): Promise { - return dataStorage - .setItem(`timeline.${timelineName}.posts`, data) - .then(); - } - - private syncPosts(timelineName: string): Promise { - return this._postsHub.getLineOrCreate(timelineName).sync(); - } - - private _postsHub = new DataHub< - string, - { - type: "cache" | "offline" | "synced" | "forbid" | "notexist"; - posts: TimelinePostData[]; - } - >({ - sync: async (key, line) => { - // Wait for timeline synced. In case the timeline has changed to another and old data has been cleaned. - await this.syncTimeline(key); - - if (line.value == null) { - const cache = await this.getCachedPosts(key); - if (cache != null) { - line.next({ type: "cache", posts: cache }); - } - } - - const now = new Date(); - - const lastUpdatedTime = await dataStorage.getItem( - `timeline.${key}.lastUpdated` - ); - - try { - if (lastUpdatedTime == null) { - const httpPosts = await getHttpTimelineClient().listPost( - key, - userService.currentUser?.token - ); - - userInfoService.saveUsers( - uniqBy( - httpPosts.map((post) => post.author), - "username" - ) - ); - - const posts = this.convertHttpPostToDataList(httpPosts); - await this.savePosts(key, posts); - await dataStorage.setItem(`timeline.${key}.lastUpdated`, now); - - line.next({ type: "synced", posts }); - } else { - const httpPosts = await getHttpTimelineClient().listPost( - key, - userService.currentUser?.token, - { - modifiedSince: lastUpdatedTime, - includeDeleted: true, - } - ); - - const deletedIds = httpPosts - .filter((p) => p.deleted) - .map((p) => p.id); - const changed = httpPosts.filter( - (p): p is HttpTimelinePostInfo => !p.deleted - ); - - userInfoService.saveUsers( - uniqBy( - httpPosts - .map((post) => post.author) - .filter((u): u is HttpUser => u != null), - "username" - ) - ); - - const cache = (await this.getCachedPosts(key)) ?? []; - - const posts = cache.filter((p) => !deletedIds.includes(p.id)); - - for (const changedPost of changed) { - const savedChangedPostIndex = posts.findIndex( - (p) => p.id === changedPost.id - ); - if (savedChangedPostIndex === -1) { - posts.push(this.convertHttpPostToData(changedPost)); - } else { - posts[savedChangedPostIndex] = this.convertHttpPostToData( - changedPost - ); - } - } - - await this.savePosts(key, posts); - await dataStorage.setItem(`timeline.${key}.lastUpdated`, now); - line.next({ type: "synced", posts }); - } - } catch (e) { - if (e instanceof HttpTimelineNotExistError) { - line.next({ type: "notexist", posts: [] }); - } else if (e instanceof HttpForbiddenError) { - line.next({ type: "forbid", posts: [] }); - } else { - const cache = await this.getCachedPosts(key); - if (cache == null) { - line.next({ type: "offline", posts: [] }); - } else { - line.next({ type: "offline", posts: cache }); - } - throwIfNotNetworkError(e); - } - } - }, - }); - - getPosts$(timelineName: string): Observable { - return this._postsHub.getDataWithSyncStatusObservable(timelineName).pipe( - switchMap((state) => { - if (state.posts.length === 0) { - return of({ - ...state, - posts: [], - }); - } - - return combineLatest([ - combineLatest( - state.posts.map((post) => userInfoService.getUser$(post.author)) - ), - combineLatest( - state.posts.map((post) => { - if (post.content.type === "image") { - return this.getPostData$(timelineName, post.id); - } else { - return of(null); - } - }) - ), - ]).pipe( - map(([authors, datas]) => { - return { - ...state, - posts: state.posts.map((post, i) => { - const { content } = post; - - return { - ...post, - author: authors[i], - content: (() => { - if (content.type === "text") return content; - else - return { - type: "image", - data: datas[i], - } as TimelinePostImageContent; - })(), - }; - }), - }; - }) - ); - }) - ); - } - - private getCachedPostData(key: { - timelineName: string; - postId: number; - }): Promise { - return dataStorage.getItem( - `timeline.${key.timelineName}.post.${key.postId}.data` - ); - } - - private savePostData( - key: { - timelineName: string; - postId: number; - }, - data: BlobWithEtag - ): Promise { - return dataStorage - .setItem( - `timeline.${key.timelineName}.post.${key.postId}.data`, - data - ) - .then(); - } - - private syncPostData(key: { - timelineName: string; - postId: number; - }): Promise { - return this._postDataHub.getLineOrCreate(key).sync(); - } - - private _postDataHub = new DataHub< - { timelineName: string; postId: number }, - | { data: Blob; type: "cache" | "synced" | "offline" } - | { data?: undefined; type: "notexist" | "offline" } - >({ - keyToString: (key) => `${key.timelineName}.${key.postId}`, - sync: async (key, line) => { - const cache = await this.getCachedPostData(key); - if (line.value == null) { - if (cache != null) { - line.next({ type: "cache", data: cache.data }); - } - } - - if (cache == null) { - try { - const res = await getHttpTimelineClient().getPostData( - key.timelineName, - key.postId - ); - await this.savePostData(key, res); - line.next({ data: res.data, type: "synced" }); - } catch (e) { - line.next({ type: "offline" }); - throwIfNotNetworkError(e); - } - } else { - try { - const res = await getHttpTimelineClient().getPostData( - key.timelineName, - key.postId, - cache.etag - ); - if (res instanceof NotModified) { - line.next({ data: cache.data, type: "synced" }); - } else { - await this.savePostData(key, res); - line.next({ data: res.data, type: "synced" }); - } - } catch (e) { - line.next({ data: cache.data, type: "offline" }); - throwIfNotNetworkError(e); - } - } - }, - }); - - getPostData$(timelineName: string, postId: number): Observable { - return this._postDataHub.getObservable({ timelineName, postId }).pipe( - map((state): BlobOrStatus => state.data ?? "error"), - startWith("loading") - ); - } - - createPost( - timelineName: string, - request: TimelineCreatePostRequest - ): Observable { - const user = checkLogin(); - return from( - getHttpTimelineClient() - .postPost(timelineName, request, user.token) - .then(() => { - void this.syncPosts(timelineName); - }) - ); - } - - deletePost(timelineName: string, postId: number): Observable { - const user = checkLogin(); - return from( - getHttpTimelineClient() - .deletePost(timelineName, postId, user.token) - .then(() => { - void this.syncPosts(timelineName); - }) - ); - } - - isMemberOf(username: string, timeline: TimelineInfo): boolean { - return timeline.members.findIndex((m) => m.username == username) >= 0; - } - - hasReadPermission( - user: UserAuthInfo | null | undefined, - timeline: TimelineInfo - ): boolean { - if (user != null && user.administrator) return true; - - const { visibility } = timeline; - if (visibility === "Public") { - return true; - } else if (visibility === "Register") { - if (user != null) return true; - } else if (visibility === "Private") { - if ( - user != null && - (user.username === timeline.owner.username || - this.isMemberOf(user.username, timeline)) - ) { - return true; - } - } - return false; - } - - hasPostPermission( - user: UserAuthInfo | null | undefined, - timeline: TimelineInfo - ): boolean { - if (user != null && user.administrator) return true; - - return ( - user != null && - (timeline.owner.username === user.username || - this.isMemberOf(user.username, timeline)) - ); - } - - hasManagePermission( - user: UserAuthInfo | null | undefined, - timeline: TimelineInfo - ): boolean { - if (user != null && user.administrator) return true; - - return user != null && user.username == timeline.owner.username; - } - - hasModifyPostPermission( - user: UserAuthInfo | null | undefined, - timeline: TimelineInfo, - post: TimelinePostInfo - ): boolean { - if (user != null && user.administrator) return true; - - return ( - user != null && - (user.username === timeline.owner.username || - user.username === post.author.username) - ); - } -} - -export const timelineService = new TimelineService(); - -const timelineNameReg = XRegExp("^[-_\\p{L}]*$", "u"); - -export function validateTimelineName(name: string): boolean { - return timelineNameReg.test(name); -} - -export function useTimelineInfo( - timelineName: string -): TimelineWithSyncStatus | undefined { - const [state, setState] = React.useState( - undefined - ); - React.useEffect(() => { - const subscription = timelineService - .getTimeline$(timelineName) - .subscribe((data) => { - setState(data); - }); - return () => { - subscription.unsubscribe(); - }; - }, [timelineName]); - return state; -} - -export function usePostList( - timelineName: string | null | undefined -): TimelinePostsWithSyncState | undefined { - const [state, setState] = React.useState< - TimelinePostsWithSyncState | undefined - >(undefined); - React.useEffect(() => { - if (timelineName == null) { - setState(undefined); - return; - } - - const subscription = timelineService - .getPosts$(timelineName) - .subscribe((data) => { - setState(data); - }); - return () => { - subscription.unsubscribe(); - }; - }, [timelineName]); - return state; -} - -export async function getAllCachedTimelineNames(): Promise { - const keys = await dataStorage.keys(); - return keys - .filter( - (key) => - key.startsWith("timeline.") && (key.match(/\./g) ?? []).length === 1 - ) - .map((key) => key.substr("timeline.".length)); -} diff --git a/Timeline/ClientApp/src/app/data/user.ts b/Timeline/ClientApp/src/app/data/user.ts deleted file mode 100644 index b8f163eb..00000000 --- a/Timeline/ClientApp/src/app/data/user.ts +++ /dev/null @@ -1,392 +0,0 @@ -import React, { useState, useEffect } from "react"; -import { BehaviorSubject, Observable, from } from "rxjs"; -import { map, filter } from "rxjs/operators"; - -import { UiLogicError } from "../common"; -import { convertError } from "../utilities/rxjs"; -import { pushAlert } from "../common/alert-service"; -import { HttpNetworkError, BlobWithEtag, NotModified } from "../http/common"; -import { - getHttpTokenClient, - HttpCreateTokenBadCredentialError, -} from "../http/token"; -import { - getHttpUserClient, - HttpUserNotExistError, - HttpUser, -} from "../http/user"; - -import { DataHub } from "./DataHub"; -import { dataStorage, throwIfNotNetworkError } from "./common"; - -export type User = HttpUser; - -export interface UserAuthInfo { - username: string; - administrator: boolean; -} - -export interface UserWithToken extends User { - token: string; -} - -export interface LoginCredentials { - username: string; - password: string; -} - -export class BadCredentialError { - message = "login.badCredential"; -} - -const USER_STORAGE_KEY = "currentuser"; - -export class UserService { - private userSubject = new BehaviorSubject( - undefined - ); - - get user$(): Observable { - return this.userSubject; - } - - get currentUser(): UserWithToken | null | undefined { - return this.userSubject.value; - } - - async checkLoginState(): Promise { - if (this.currentUser !== undefined) { - console.warn("Already checked user. Can't check twice."); - } - - const savedUser = await dataStorage.getItem( - USER_STORAGE_KEY - ); - - if (savedUser == null) { - this.userSubject.next(null); - return null; - } - - this.userSubject.next(savedUser); - - const savedToken = savedUser.token; - try { - const res = await getHttpTokenClient().verify({ token: savedToken }); - const user: UserWithToken = { ...res.user, token: savedToken }; - await dataStorage.setItem(USER_STORAGE_KEY, user); - this.userSubject.next(user); - pushAlert({ - type: "success", - message: { - type: "i18n", - key: "user.welcomeBack", - }, - }); - return user; - } catch (error) { - if (error instanceof HttpNetworkError) { - pushAlert({ - type: "danger", - message: { type: "i18n", key: "user.verifyTokenFailedNetwork" }, - }); - return savedUser; - } else { - await dataStorage.removeItem(USER_STORAGE_KEY); - this.userSubject.next(null); - pushAlert({ - type: "danger", - message: { type: "i18n", key: "user.verifyTokenFailed" }, - }); - return null; - } - } - } - - async login( - credentials: LoginCredentials, - rememberMe: boolean - ): Promise { - if (this.currentUser) { - throw new UiLogicError("Already login."); - } - try { - const res = await getHttpTokenClient().create({ - ...credentials, - expire: 30, - }); - const user: UserWithToken = { - ...res.user, - token: res.token, - }; - if (rememberMe) { - await dataStorage.setItem(USER_STORAGE_KEY, user); - } - this.userSubject.next(user); - } catch (e) { - if (e instanceof HttpCreateTokenBadCredentialError) { - throw new BadCredentialError(); - } else { - throw e; - } - } - } - - async logout(): Promise { - if (this.currentUser === undefined) { - throw new UiLogicError("Please check user first."); - } - if (this.currentUser === null) { - throw new UiLogicError("No login."); - } - await dataStorage.removeItem(USER_STORAGE_KEY); - this.userSubject.next(null); - } - - changePassword( - oldPassword: string, - newPassword: string - ): Observable { - if (this.currentUser == undefined) { - throw new UiLogicError("Not login or checked now, can't log out."); - } - const $ = from( - getHttpUserClient().changePassword( - { - oldPassword, - newPassword, - }, - this.currentUser.token - ) - ); - $.subscribe(() => { - void this.logout(); - }); - return $; - } -} - -export const userService = new UserService(); - -export function useRawUser(): UserWithToken | null | undefined { - const [user, setUser] = useState( - userService.currentUser - ); - useEffect(() => { - const subscription = userService.user$.subscribe((u) => setUser(u)); - return () => { - subscription.unsubscribe(); - }; - }); - return user; -} - -export function useUser(): UserWithToken | null { - const [user, setUser] = useState(() => { - const initUser = userService.currentUser; - if (initUser === undefined) { - throw new UiLogicError( - "This is a logic error in user module. Current user can't be undefined in useUser." - ); - } - return initUser; - }); - useEffect(() => { - const sub = userService.user$.subscribe((u) => { - if (u === undefined) { - throw new UiLogicError( - "This is a logic error in user module. User emitted can't be undefined later." - ); - } - setUser(u); - }); - return () => { - sub.unsubscribe(); - }; - }); - return user; -} - -export function useUserLoggedIn(): UserWithToken { - const user = useUser(); - if (user == null) { - throw new UiLogicError("You assert user has logged in but actually not."); - } - return user; -} - -export function checkLogin(): UserWithToken { - const user = userService.currentUser; - if (user == null) { - throw new UiLogicError("You must login to perform the operation."); - } - return user; -} - -export class UserNotExistError extends Error {} - -export class UserInfoService { - saveUser(user: HttpUser): void { - const key = user.username; - void this._userHub.optionalInitLineWithSyncAction(key, async (line) => { - await this.doSaveUser(user); - line.next({ user, type: "synced" }); - }); - } - - saveUsers(users: HttpUser[]): void { - return users.forEach((user) => this.saveUser(user)); - } - - private getCachedUser(username: string): Promise { - return dataStorage.getItem(`user.${username}`); - } - - private doSaveUser(user: HttpUser): Promise { - return dataStorage.setItem(`user.${user.username}`, user).then(); - } - - syncUser(username: string): Promise { - return this._userHub.getLineOrCreate(username).sync(); - } - - private _userHub = new DataHub< - string, - | { user: User; type: "cache" | "synced" | "offline" } - | { user?: undefined; type: "notexist" | "offline" } - >({ - sync: async (key, line) => { - if (line.value == undefined) { - const cache = await this.getCachedUser(key); - if (cache != null) { - line.next({ user: cache, type: "cache" }); - } - } - - try { - const res = await getHttpUserClient().get(key); - await this.doSaveUser(res); - line.next({ user: res, type: "synced" }); - } catch (e) { - if (e instanceof HttpUserNotExistError) { - line.next({ type: "notexist" }); - } else { - const cache = await this.getCachedUser(key); - line.next({ user: cache ?? undefined, type: "offline" }); - throwIfNotNetworkError(e); - } - } - }, - }); - - getUser$(username: string): Observable { - return this._userHub.getObservable(username).pipe( - map((state) => state?.user), - filter((user): user is User => user != null) - ); - } - - private getCachedAvatar(username: string): Promise { - return dataStorage.getItem(`user.${username}.avatar`); - } - - private saveAvatar(username: string, data: BlobWithEtag): Promise { - return dataStorage - .setItem(`user.${username}.avatar`, data) - .then(); - } - - syncAvatar(username: string): Promise { - return this._avatarHub.getLineOrCreate(username).sync(); - } - - private _avatarHub = new DataHub< - string, - | { data: Blob; type: "cache" | "synced" | "offline" } - | { data?: undefined; type: "notexist" | "offline" } - >({ - sync: async (key, line) => { - const cache = await this.getCachedAvatar(key); - if (line.value == null) { - if (cache != null) { - line.next({ data: cache.data, type: "cache" }); - } - } - - if (cache == null) { - try { - const avatar = await getHttpUserClient().getAvatar(key); - await this.saveAvatar(key, avatar); - line.next({ data: avatar.data, type: "synced" }); - } catch (e) { - line.next({ type: "offline" }); - throwIfNotNetworkError(e); - } - } else { - try { - const res = await getHttpUserClient().getAvatar(key, cache.etag); - if (res instanceof NotModified) { - line.next({ data: cache.data, type: "synced" }); - } else { - const avatar = res; - await this.saveAvatar(key, avatar); - line.next({ data: avatar.data, type: "synced" }); - } - } catch (e) { - line.next({ data: cache.data, type: "offline" }); - throwIfNotNetworkError(e); - } - } - }, - }); - - getAvatar$(username: string): Observable { - return this._avatarHub.getObservable(username).pipe( - map((state) => state.data), - filter((blob): blob is Blob => blob != null) - ); - } - - getUserInfo(username: string): Observable { - return from(getHttpUserClient().get(username)).pipe( - convertError(HttpUserNotExistError, UserNotExistError) - ); - } - - async setAvatar(username: string, blob: Blob): Promise { - const user = checkLogin(); - await getHttpUserClient().putAvatar(username, blob, user.token); - this._avatarHub.getLine(username)?.next({ data: blob, type: "synced" }); - } - - async setNickname(username: string, nickname: string): Promise { - const user = checkLogin(); - return getHttpUserClient() - .patch(username, { nickname }, user.token) - .then((user) => { - this.saveUser(user); - }); - } -} - -export const userInfoService = new UserInfoService(); - -export function useAvatar(username?: string): Blob | undefined { - const [state, setState] = React.useState(undefined); - React.useEffect(() => { - if (username == null) { - setState(undefined); - return; - } - - const subscription = userInfoService - .getAvatar$(username) - .subscribe((blob) => { - setState(blob); - }); - return () => { - subscription.unsubscribe(); - }; - }, [username]); - return state; -} diff --git a/Timeline/ClientApp/src/app/home/BoardWithUser.tsx b/Timeline/ClientApp/src/app/home/BoardWithUser.tsx deleted file mode 100644 index 22a4667c..00000000 --- a/Timeline/ClientApp/src/app/home/BoardWithUser.tsx +++ /dev/null @@ -1,101 +0,0 @@ -import React from "react"; -import { Row, Col } from "reactstrap"; -import { useTranslation } from "react-i18next"; - -import { UserWithToken } from "../data/user"; -import { TimelineInfo } from "../data/timeline"; -import { getHttpTimelineClient } from "../http/timeline"; - -import TimelineBoard from "./TimelineBoard"; -import OfflineBoard from "./OfflineBoard"; - -const BoardWithUser: React.FC<{ user: UserWithToken }> = ({ user }) => { - const { t } = useTranslation(); - - const [ownTimelines, setOwnTimelines] = React.useState< - TimelineInfo[] | "offline" | "loading" - >("loading"); - const [joinTimelines, setJoinTimelines] = React.useState< - TimelineInfo[] | "offline" | "loading" - >("loading"); - - React.useEffect(() => { - let subscribe = true; - if (ownTimelines === "loading") { - void getHttpTimelineClient() - .listTimeline({ relate: user.username, relateType: "own" }) - .then( - (timelines) => { - if (subscribe) { - setOwnTimelines(timelines); - } - }, - () => { - setOwnTimelines("offline"); - } - ); - } - return () => { - subscribe = false; - }; - }, [user, ownTimelines]); - - React.useEffect(() => { - let subscribe = true; - if (joinTimelines === "loading") { - void getHttpTimelineClient() - .listTimeline({ relate: user.username, relateType: "join" }) - .then( - (timelines) => { - if (subscribe) { - setJoinTimelines(timelines); - } - }, - () => { - setJoinTimelines("offline"); - } - ); - } - return () => { - subscribe = false; - }; - }, [user, joinTimelines]); - - return ( - - {ownTimelines === "offline" && joinTimelines === "offline" ? ( - - { - setOwnTimelines("loading"); - setJoinTimelines("loading"); - }} - /> - - ) : ( - <> - - { - setOwnTimelines("loading"); - }} - /> - - - { - setJoinTimelines("loading"); - }} - /> - - - )} - - ); -}; - -export default BoardWithUser; diff --git a/Timeline/ClientApp/src/app/home/BoardWithoutUser.tsx b/Timeline/ClientApp/src/app/home/BoardWithoutUser.tsx deleted file mode 100644 index 972c1b25..00000000 --- a/Timeline/ClientApp/src/app/home/BoardWithoutUser.tsx +++ /dev/null @@ -1,60 +0,0 @@ -import React from "react"; -import { Row, Col } from "reactstrap"; - -import { TimelineInfo } from "../data/timeline"; -import { getHttpTimelineClient } from "../http/timeline"; - -import TimelineBoard from "./TimelineBoard"; -import OfflineBoard from "./OfflineBoard"; - -const BoardWithoutUser: React.FC = () => { - const [publicTimelines, setPublicTimelines] = React.useState< - TimelineInfo[] | "offline" | "loading" - >("loading"); - - React.useEffect(() => { - let subscribe = true; - if (publicTimelines === "loading") { - void getHttpTimelineClient() - .listTimeline({ visibility: "Public" }) - .then( - (timelines) => { - if (subscribe) { - setPublicTimelines(timelines); - } - }, - () => { - setPublicTimelines("offline"); - } - ); - } - return () => { - subscribe = false; - }; - }, [publicTimelines]); - - return ( - - {publicTimelines === "offline" ? ( - - { - setPublicTimelines("loading"); - }} - /> - - ) : ( - - { - setPublicTimelines("loading"); - }} - /> - - )} - - ); -}; - -export default BoardWithoutUser; diff --git a/Timeline/ClientApp/src/app/home/Home.tsx b/Timeline/ClientApp/src/app/home/Home.tsx deleted file mode 100644 index 910c9a01..00000000 --- a/Timeline/ClientApp/src/app/home/Home.tsx +++ /dev/null @@ -1,102 +0,0 @@ -import React from "react"; -import { useHistory } from "react-router"; -import { Row, Container, Button, Col } from "reactstrap"; -import { useTranslation } from "react-i18next"; - -import { useUser } from "../data/user"; -import AppBar from "../common/AppBar"; -import SearchInput from "../common/SearchInput"; - -import BoardWithoutUser from "./BoardWithoutUser"; -import BoardWithUser from "./BoardWithUser"; -import TimelineCreateDialog from "./TimelineCreateDialog"; - -const Home: React.FC = () => { - const history = useHistory(); - - const { t } = useTranslation(); - - const user = useUser(); - - const [navText, setNavText] = React.useState(""); - - const [dialog, setDialog] = React.useState<"create" | null>(null); - - const goto = React.useCallback((): void => { - if (navText === "") { - history.push("users/crupest"); - } else if (navText.startsWith("@")) { - history.push(`users/${navText.slice(1)}`); - } else { - history.push(`timelines/${navText}`); - } - }, [navText, history]); - - return ( - <> - - - - - { - setDialog("create"); - }} - > - {t("home.createButton")} - - ) - } - /> - - - {(() => { - if (user == null) { - return ; - } else { - return ; - } - })()} - - - {dialog === "create" && ( - { - setDialog(null); - }} - /> - )} - - ); -}; - -export default Home; diff --git a/Timeline/ClientApp/src/app/home/OfflineBoard.tsx b/Timeline/ClientApp/src/app/home/OfflineBoard.tsx deleted file mode 100644 index fbd37efd..00000000 --- a/Timeline/ClientApp/src/app/home/OfflineBoard.tsx +++ /dev/null @@ -1,61 +0,0 @@ -import React from "react"; -import { Link } from "react-router-dom"; -import { Trans } from "react-i18next"; - -import { getAllCachedTimelineNames } from "../data/timeline"; -import UserTimelineLogo from "../common/UserTimelineLogo"; -import TimelineLogo from "../common/TimelineLogo"; - -export interface OfflineBoardProps { - onReload: () => void; -} - -const OfflineBoard: React.FC = ({ onReload }) => { - const [timelines, setTimelines] = React.useState([]); - - React.useEffect(() => { - let subscribe = true; - void getAllCachedTimelineNames().then((t) => { - if (subscribe) setTimelines(t); - }); - return () => { - subscribe = false; - }; - }); - - return ( - <> - - 0 - { - onReload(); - e.preventDefault(); - }} - > - 1 - - 2 - - {timelines.map((timeline) => { - const isPersonal = timeline.startsWith("@"); - const url = isPersonal - ? `/users/${timeline.slice(1)}` - : `/timelines/${timeline}`; - return ( -
- {isPersonal ? ( - - ) : ( - - )} - {timeline} -
- ); - })} - - ); -}; - -export default OfflineBoard; diff --git a/Timeline/ClientApp/src/app/home/TimelineBoard.tsx b/Timeline/ClientApp/src/app/home/TimelineBoard.tsx deleted file mode 100644 index 21dcac3f..00000000 --- a/Timeline/ClientApp/src/app/home/TimelineBoard.tsx +++ /dev/null @@ -1,73 +0,0 @@ -import React from "react"; -import clsx from "clsx"; -import { Link } from "react-router-dom"; -import { Spinner } from "reactstrap"; -import { Trans } from "react-i18next"; - -import { TimelineInfo } from "../data/timeline"; -import TimelineLogo from "../common/TimelineLogo"; -import UserTimelineLogo from "../common/UserTimelineLogo"; - -export interface TimelineBoardProps { - title?: string; - timelines: TimelineInfo[] | "offline" | "loading"; - onReload: () => void; - className?: string; -} - -const TimelineBoard: React.FC = (props) => { - const { title, timelines, className } = props; - - return ( -
- {title != null &&

{title}

} - {(() => { - if (timelines === "loading") { - return ( -
- -
- ); - } else if (timelines === "offline") { - return ( - - ); - } else { - return timelines.map((timeline) => { - const { name } = timeline; - const isPersonal = name.startsWith("@"); - const url = isPersonal - ? `/users/${timeline.owner.username}` - : `/timelines/${name}`; - return ( -
- {isPersonal ? ( - - ) : ( - - )} - {name} -
- ); - }); - } - })()} -
- ); -}; - -export default TimelineBoard; diff --git a/Timeline/ClientApp/src/app/home/TimelineCreateDialog.tsx b/Timeline/ClientApp/src/app/home/TimelineCreateDialog.tsx deleted file mode 100644 index 911dd60c..00000000 --- a/Timeline/ClientApp/src/app/home/TimelineCreateDialog.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import React from "react"; -import { useHistory } from "react-router"; - -import { validateTimelineName, timelineService } from "../data/timeline"; -import OperationDialog from "../common/OperationDialog"; - -interface TimelineCreateDialogProps { - open: boolean; - close: () => void; -} - -const TimelineCreateDialog: React.FC = (props) => { - const history = useHistory(); - - let nameSaved: string; - - return ( - { - if (name.length === 0) { - return "home.createDialog.noEmpty"; - } else if (name.length > 26) { - return "home.createDialog.tooLong"; - } else if (!validateTimelineName(name)) { - return "home.createDialog.badFormat"; - } else { - return null; - } - }, - }, - ]} - onProcess={([name]) => { - nameSaved = name as string; - return timelineService.createTimeline(nameSaved).toPromise(); - }} - onSuccessAndClose={() => { - history.push(`timelines/${nameSaved}`); - }} - failurePrompt={(e) => `${e as string}`} - /> - ); -}; - -export default TimelineCreateDialog; diff --git a/Timeline/ClientApp/src/app/home/home.sass b/Timeline/ClientApp/src/app/home/home.sass deleted file mode 100644 index f5d6ffc3..00000000 --- a/Timeline/ClientApp/src/app/home/home.sass +++ /dev/null @@ -1,13 +0,0 @@ -.timeline-board-item - font-size: 1.1em - @extend .my-2 - .icon - height: 1.3em - @extend .mr-2 - -.timeline-board - @extend .cru-card - @extend .d-flex - @extend .flex-column - @extend .p-3 - min-height: 200px diff --git a/Timeline/ClientApp/src/app/index.sass b/Timeline/ClientApp/src/app/index.sass index ef0b03ba..efac4df5 100644 --- a/Timeline/ClientApp/src/app/index.sass +++ b/Timeline/ClientApp/src/app/index.sass @@ -1,12 +1,12 @@ @import '~bootstrap/scss/bootstrap' -@import './common/common' -@import './common/alert' -@import './home/home' -@import './about/about' -@import './timeline/timeline' -@import './timeline/timeline-ui' -@import './user/user-page' +@import './views/common/common' +@import './views/common/alert/alert' +@import './views/home/home' +@import './views/about/about' +@import './views/timeline-common/timeline-common' +@import './views/timeline/timeline' +@import './views/user/user' body margin: 0 diff --git a/Timeline/ClientApp/src/app/service-worker.tsx b/Timeline/ClientApp/src/app/service-worker.tsx index f71b23b3..e629995a 100644 --- a/Timeline/ClientApp/src/app/service-worker.tsx +++ b/Timeline/ClientApp/src/app/service-worker.tsx @@ -2,7 +2,7 @@ import React from "react"; import { Button } from "reactstrap"; import { useTranslation } from "react-i18next"; -import { pushAlert } from "./common/alert-service"; +import { pushAlert } from "./services/alert"; if ("serviceWorker" in navigator) { let isThisTriggerUpgrade = false; diff --git a/Timeline/ClientApp/src/app/services/DataHub.ts b/Timeline/ClientApp/src/app/services/DataHub.ts new file mode 100644 index 00000000..93a9b41f --- /dev/null +++ b/Timeline/ClientApp/src/app/services/DataHub.ts @@ -0,0 +1,225 @@ +import { pull } from "lodash"; +import { Observable, BehaviorSubject, combineLatest } from "rxjs"; +import { map } from "rxjs/operators"; + +export type Subscriber = (data: TData) => void; + +export type WithSyncStatus = T & { syncing: boolean }; + +export class DataLine { + private _current: TData | undefined = undefined; + + private _syncPromise: Promise | null = null; + private _syncingSubject = new BehaviorSubject(false); + + private _observers: Subscriber[] = []; + + constructor( + private config: { + sync: () => Promise; + destroyable?: (value: TData | undefined) => boolean; + disableInitSync?: boolean; + } + ) { + if (config.disableInitSync !== true) { + setImmediate(() => void this.sync()); + } + } + + private subscribe(subscriber: Subscriber): void { + this._observers.push(subscriber); + if (this._current !== undefined) { + subscriber(this._current); + } + } + + private unsubscribe(subscriber: Subscriber): void { + if (!this._observers.includes(subscriber)) return; + pull(this._observers, subscriber); + } + + getObservable(): Observable { + return new Observable((observer) => { + const f = (data: TData): void => { + observer.next(data); + }; + this.subscribe(f); + + return () => { + this.unsubscribe(f); + }; + }); + } + + getSyncStatusObservable(): Observable { + return this._syncingSubject.asObservable(); + } + + getDataWithSyncStatusObservable(): Observable> { + return combineLatest([ + this.getObservable(), + this.getSyncStatusObservable(), + ]).pipe( + map(([data, syncing]) => ({ + ...data, + syncing, + })) + ); + } + + get value(): TData | undefined { + return this._current; + } + + next(value: TData): void { + this._current = value; + this._observers.forEach((observer) => observer(value)); + } + + get isSyncing(): boolean { + return this._syncPromise != null; + } + + sync(): Promise { + if (this._syncPromise == null) { + this._syncingSubject.next(true); + this._syncPromise = this.config.sync().then(() => { + this._syncingSubject.next(false); + this._syncPromise = null; + }); + } + + return this._syncPromise; + } + + syncWithAction( + syncAction: (line: DataLine) => Promise + ): Promise { + if (this._syncPromise == null) { + this._syncingSubject.next(true); + this._syncPromise = syncAction(this).then(() => { + this._syncingSubject.next(false); + this._syncPromise = null; + }); + } + + return this._syncPromise; + } + + get destroyable(): boolean { + const customDestroyable = this.config?.destroyable; + + return ( + this._observers.length === 0 && + !this.isSyncing && + (customDestroyable != null ? customDestroyable(this._current) : true) + ); + } +} + +export class DataHub { + private sync: (key: TKey, line: DataLine) => Promise; + private keyToString: (key: TKey) => string; + private destroyable?: (key: TKey, value: TData | undefined) => boolean; + + private readonly subscriptionLineMap = new Map>(); + + private cleanTimerId = 0; + + // setup is called after creating line and if it returns a function as destroyer, then when the line is destroyed the destroyer will be called. + constructor(config: { + sync: (key: TKey, line: DataLine) => Promise; + keyToString?: (key: TKey) => string; + destroyable?: (key: TKey, value: TData | undefined) => boolean; + }) { + this.sync = config.sync; + this.keyToString = + config.keyToString ?? + ((value): string => { + if (typeof value === "string") return value; + else + throw new Error( + "Default keyToString function only pass string value." + ); + }); + + this.destroyable = config.destroyable; + } + + private cleanLines(): void { + const toDelete: string[] = []; + for (const [key, line] of this.subscriptionLineMap.entries()) { + if (line.destroyable) { + toDelete.push(key); + } + } + + if (toDelete.length === 0) return; + + for (const key of toDelete) { + this.subscriptionLineMap.delete(key); + } + + if (this.subscriptionLineMap.size === 0) { + window.clearInterval(this.cleanTimerId); + this.cleanTimerId = 0; + } + } + + private createLine(key: TKey, disableInitSync = false): DataLine { + const keyString = this.keyToString(key); + const { destroyable } = this; + const newLine: DataLine = new DataLine({ + sync: () => this.sync(key, newLine), + destroyable: + destroyable != null ? (value) => destroyable(key, value) : undefined, + disableInitSync: disableInitSync, + }); + this.subscriptionLineMap.set(keyString, newLine); + if (this.subscriptionLineMap.size === 1) { + this.cleanTimerId = window.setInterval(this.cleanLines.bind(this), 20000); + } + return newLine; + } + + getObservable(key: TKey): Observable { + return this.getLineOrCreate(key).getObservable(); + } + + getSyncStatusObservable(key: TKey): Observable { + return this.getLineOrCreate(key).getSyncStatusObservable(); + } + + getDataWithSyncStatusObservable( + key: TKey + ): Observable> { + return this.getLineOrCreate(key).getDataWithSyncStatusObservable(); + } + + getLine(key: TKey): DataLine | null { + const keyString = this.keyToString(key); + return this.subscriptionLineMap.get(keyString) ?? null; + } + + getLineOrCreate(key: TKey): DataLine { + const keyString = this.keyToString(key); + return this.subscriptionLineMap.get(keyString) ?? this.createLine(key); + } + + getLineOrCreateWithoutInitSync(key: TKey): DataLine { + const keyString = this.keyToString(key); + return ( + this.subscriptionLineMap.get(keyString) ?? this.createLine(key, true) + ); + } + + optionalInitLineWithSyncAction( + key: TKey, + syncAction: (line: DataLine) => Promise + ): Promise { + const optionalLine = this.getLine(key); + if (optionalLine != null) return Promise.resolve(); + const line = this.createLine(key, true); + return line.syncWithAction(syncAction); + } +} diff --git a/Timeline/ClientApp/src/app/services/alert.ts b/Timeline/ClientApp/src/app/services/alert.ts new file mode 100644 index 00000000..e4c0e653 --- /dev/null +++ b/Timeline/ClientApp/src/app/services/alert.ts @@ -0,0 +1,61 @@ +import React from "react"; +import pull from "lodash/pull"; + +export interface AlertInfo { + type?: "primary" | "secondary" | "success" | "danger" | "warning" | "info"; + message: string | React.FC | { type: "i18n"; key: string }; + dismissTime?: number | "never"; +} + +export interface AlertInfoEx extends AlertInfo { + id: number; +} + +export type AlertConsumer = (alerts: AlertInfoEx) => void; + +export class AlertService { + private consumers: AlertConsumer[] = []; + private savedAlerts: AlertInfoEx[] = []; + private currentId = 1; + + private produce(alert: AlertInfoEx): void { + for (const consumer of this.consumers) { + consumer(alert); + } + } + + registerConsumer(consumer: AlertConsumer): void { + this.consumers.push(consumer); + if (this.savedAlerts.length !== 0) { + for (const alert of this.savedAlerts) { + this.produce(alert); + } + this.savedAlerts = []; + } + } + + unregisterConsumer(consumer: AlertConsumer): void { + pull(this.consumers, consumer); + } + + push(alert: AlertInfo): void { + const newAlert: AlertInfoEx = { ...alert, id: this.currentId++ }; + if (this.consumers.length === 0) { + this.savedAlerts.push(newAlert); + } else { + this.produce(newAlert); + } + } +} + +export const alertService = new AlertService(); + +export function pushAlert(alert: AlertInfo): void { + alertService.push(alert); +} + +export const kAlertHostId = "alert-host"; + +export function getAlertHost(): HTMLElement | null { + return document.getElementById(kAlertHostId); +} diff --git a/Timeline/ClientApp/src/app/services/common.ts b/Timeline/ClientApp/src/app/services/common.ts new file mode 100644 index 00000000..3bb6b9d7 --- /dev/null +++ b/Timeline/ClientApp/src/app/services/common.ts @@ -0,0 +1,23 @@ +import localforage from "localforage"; + +import { HttpNetworkError } from "@/http/common"; + +export const dataStorage = localforage.createInstance({ + name: "data", + description: "Database for offline data.", + driver: localforage.INDEXEDDB, +}); + +export class ForbiddenError extends Error { + constructor(message?: string) { + super(message); + } +} + +export function throwIfNotNetworkError(e: unknown): void { + if (!(e instanceof HttpNetworkError)) { + throw e; + } +} + +export type BlobOrStatus = Blob | "loading" | "error"; diff --git a/Timeline/ClientApp/src/app/services/timeline.ts b/Timeline/ClientApp/src/app/services/timeline.ts new file mode 100644 index 00000000..9db76281 --- /dev/null +++ b/Timeline/ClientApp/src/app/services/timeline.ts @@ -0,0 +1,702 @@ +import React from "react"; +import XRegExp from "xregexp"; +import { Observable, from, combineLatest, of } from "rxjs"; +import { map, switchMap, startWith } from "rxjs/operators"; +import { uniqBy } from "lodash"; + +import { convertError } from "@/utilities/rxjs"; +import { + TimelineVisibility, + HttpTimelineInfo, + HttpTimelinePatchRequest, + HttpTimelinePostPostRequest, + HttpTimelinePostPostRequestContent, + HttpTimelinePostPostRequestTextContent, + HttpTimelinePostPostRequestImageContent, + HttpTimelinePostInfo, + HttpTimelinePostTextContent, + getHttpTimelineClient, + HttpTimelineNotExistError, + HttpTimelineNameConflictError, +} from "@/http/timeline"; +import { BlobWithEtag, NotModified, HttpForbiddenError } from "@/http/common"; +import { HttpUser } from "@/http/user"; + +export { kTimelineVisibilities } from "@/http/timeline"; + +export type { TimelineVisibility } from "@/http/timeline"; + +import { dataStorage, throwIfNotNetworkError, BlobOrStatus } from "./common"; +import { DataHub, WithSyncStatus } from "./DataHub"; +import { UserAuthInfo, checkLogin, userService, userInfoService } from "./user"; + +export type TimelineInfo = HttpTimelineInfo; +export type TimelineChangePropertyRequest = HttpTimelinePatchRequest; +export type TimelineCreatePostRequest = HttpTimelinePostPostRequest; +export type TimelineCreatePostContent = HttpTimelinePostPostRequestContent; +export type TimelineCreatePostTextContent = HttpTimelinePostPostRequestTextContent; +export type TimelineCreatePostImageContent = HttpTimelinePostPostRequestImageContent; + +export type TimelinePostTextContent = HttpTimelinePostTextContent; + +export interface TimelinePostImageContent { + type: "image"; + data: BlobOrStatus; +} + +export type TimelinePostContent = + | TimelinePostTextContent + | TimelinePostImageContent; + +export interface TimelinePostInfo { + id: number; + content: TimelinePostContent; + time: Date; + lastUpdated: Date; + author: HttpUser; +} + +export const timelineVisibilityTooltipTranslationMap: Record< + TimelineVisibility, + string +> = { + Public: "timeline.visibilityTooltip.public", + Register: "timeline.visibilityTooltip.register", + Private: "timeline.visibilityTooltip.private", +}; + +export class TimelineNotExistError extends Error {} +export class TimelineNameConflictError extends Error {} + +export type TimelineWithSyncStatus = WithSyncStatus< + | { + type: "cache"; + timeline: TimelineInfo; + } + | { + type: "offline" | "synced"; + timeline: TimelineInfo | null; + } +>; + +export type TimelinePostsWithSyncState = WithSyncStatus<{ + type: + | "cache" + | "offline" // Sync failed and use cache. + | "synced" // Sync succeeded. + | "forbid" // The list is forbidden to see. + | "notexist"; // The timeline does not exist. + posts: TimelinePostInfo[]; +}>; + +type TimelineData = Omit & { + owner: string; + members: string[]; +}; + +type TimelinePostData = Omit & { + author: string; +}; + +export class TimelineService { + private getCachedTimeline( + timelineName: string + ): Promise { + return dataStorage.getItem(`timeline.${timelineName}`); + } + + private saveTimeline( + timelineName: string, + data: TimelineData + ): Promise { + return dataStorage + .setItem(`timeline.${timelineName}`, data) + .then(); + } + + private async clearTimelineData(timelineName: string): Promise { + const keys = (await dataStorage.keys()).filter((k) => + k.startsWith(`timeline.${timelineName}`) + ); + await Promise.all(keys.map((k) => dataStorage.removeItem(k))); + } + + private convertHttpTimelineToData(timeline: HttpTimelineInfo): TimelineData { + return { + ...timeline, + owner: timeline.owner.username, + members: timeline.members.map((m) => m.username), + }; + } + + private _timelineHub = new DataHub< + string, + | { + type: "cache"; + timeline: TimelineData; + } + | { + type: "offline" | "synced"; + timeline: TimelineData | null; + } + >({ + sync: async (key, line) => { + const cache = await this.getCachedTimeline(key); + + if (line.value == undefined) { + if (cache != null) { + line.next({ type: "cache", timeline: cache }); + } + } + + try { + const httpTimeline = await getHttpTimelineClient().getTimeline(key); + + userInfoService.saveUsers([ + httpTimeline.owner, + ...httpTimeline.members, + ]); + + const timeline = this.convertHttpTimelineToData(httpTimeline); + + if (cache != null && timeline.uniqueId !== cache.uniqueId) { + console.log( + `Timeline with name ${key} has changed to a new one. Clear old data.` + ); + await this.clearTimelineData(key); // If timeline has changed, clear all old data. + } + + await this.saveTimeline(key, timeline); + + line.next({ type: "synced", timeline }); + } catch (e) { + if (e instanceof HttpTimelineNotExistError) { + line.next({ type: "synced", timeline: null }); + } else { + if (cache == null) { + line.next({ type: "offline", timeline: null }); + } else { + line.next({ type: "offline", timeline: cache }); + } + throwIfNotNetworkError(e); + } + } + }, + }); + + syncTimeline(timelineName: string): Promise { + return this._timelineHub.getLineOrCreate(timelineName).sync(); + } + + getTimeline$(timelineName: string): Observable { + return this._timelineHub.getDataWithSyncStatusObservable(timelineName).pipe( + switchMap((state) => { + const { timeline } = state; + if (timeline != null) { + return combineLatest( + [timeline.owner, ...timeline.members].map((u) => + userInfoService.getUser$(u) + ) + ).pipe( + map((users) => { + return { + ...state, + timeline: { + ...timeline, + owner: users[0], + members: users.slice(1), + }, + }; + }) + ); + } else { + return of(state as TimelineWithSyncStatus); + } + }) + ); + } + + createTimeline(timelineName: string): Observable { + const user = checkLogin(); + return from( + getHttpTimelineClient().postTimeline( + { + name: timelineName, + }, + user.token + ) + ).pipe( + convertError(HttpTimelineNameConflictError, TimelineNameConflictError) + ); + } + + changeTimelineProperty( + timelineName: string, + req: TimelineChangePropertyRequest + ): Observable { + const user = checkLogin(); + return from( + getHttpTimelineClient() + .patchTimeline(timelineName, req, user.token) + .then((timeline) => { + void this.syncTimeline(timelineName); + return timeline; + }) + ); + } + + deleteTimeline(timelineName: string): Observable { + const user = checkLogin(); + return from( + getHttpTimelineClient().deleteTimeline(timelineName, user.token) + ); + } + + addMember(timelineName: string, username: string): Observable { + const user = checkLogin(); + return from( + getHttpTimelineClient() + .memberPut(timelineName, username, user.token) + .then(() => { + void this.syncTimeline(timelineName); + }) + ); + } + + removeMember(timelineName: string, username: string): Observable { + const user = checkLogin(); + return from( + getHttpTimelineClient() + .memberDelete(timelineName, username, user.token) + .then(() => { + void this.syncTimeline(timelineName); + }) + ); + } + + private convertHttpPostToData(post: HttpTimelinePostInfo): TimelinePostData { + return { + ...post, + author: post.author.username, + }; + } + + private convertHttpPostToDataList( + posts: HttpTimelinePostInfo[] + ): TimelinePostData[] { + return posts.map((post) => this.convertHttpPostToData(post)); + } + + private getCachedPosts( + timelineName: string + ): Promise { + return dataStorage.getItem( + `timeline.${timelineName}.posts` + ); + } + + private savePosts( + timelineName: string, + data: TimelinePostData[] + ): Promise { + return dataStorage + .setItem(`timeline.${timelineName}.posts`, data) + .then(); + } + + private syncPosts(timelineName: string): Promise { + return this._postsHub.getLineOrCreate(timelineName).sync(); + } + + private _postsHub = new DataHub< + string, + { + type: "cache" | "offline" | "synced" | "forbid" | "notexist"; + posts: TimelinePostData[]; + } + >({ + sync: async (key, line) => { + // Wait for timeline synced. In case the timeline has changed to another and old data has been cleaned. + await this.syncTimeline(key); + + if (line.value == null) { + const cache = await this.getCachedPosts(key); + if (cache != null) { + line.next({ type: "cache", posts: cache }); + } + } + + const now = new Date(); + + const lastUpdatedTime = await dataStorage.getItem( + `timeline.${key}.lastUpdated` + ); + + try { + if (lastUpdatedTime == null) { + const httpPosts = await getHttpTimelineClient().listPost( + key, + userService.currentUser?.token + ); + + userInfoService.saveUsers( + uniqBy( + httpPosts.map((post) => post.author), + "username" + ) + ); + + const posts = this.convertHttpPostToDataList(httpPosts); + await this.savePosts(key, posts); + await dataStorage.setItem(`timeline.${key}.lastUpdated`, now); + + line.next({ type: "synced", posts }); + } else { + const httpPosts = await getHttpTimelineClient().listPost( + key, + userService.currentUser?.token, + { + modifiedSince: lastUpdatedTime, + includeDeleted: true, + } + ); + + const deletedIds = httpPosts + .filter((p) => p.deleted) + .map((p) => p.id); + const changed = httpPosts.filter( + (p): p is HttpTimelinePostInfo => !p.deleted + ); + + userInfoService.saveUsers( + uniqBy( + httpPosts + .map((post) => post.author) + .filter((u): u is HttpUser => u != null), + "username" + ) + ); + + const cache = (await this.getCachedPosts(key)) ?? []; + + const posts = cache.filter((p) => !deletedIds.includes(p.id)); + + for (const changedPost of changed) { + const savedChangedPostIndex = posts.findIndex( + (p) => p.id === changedPost.id + ); + if (savedChangedPostIndex === -1) { + posts.push(this.convertHttpPostToData(changedPost)); + } else { + posts[savedChangedPostIndex] = this.convertHttpPostToData( + changedPost + ); + } + } + + await this.savePosts(key, posts); + await dataStorage.setItem(`timeline.${key}.lastUpdated`, now); + line.next({ type: "synced", posts }); + } + } catch (e) { + if (e instanceof HttpTimelineNotExistError) { + line.next({ type: "notexist", posts: [] }); + } else if (e instanceof HttpForbiddenError) { + line.next({ type: "forbid", posts: [] }); + } else { + const cache = await this.getCachedPosts(key); + if (cache == null) { + line.next({ type: "offline", posts: [] }); + } else { + line.next({ type: "offline", posts: cache }); + } + throwIfNotNetworkError(e); + } + } + }, + }); + + getPosts$(timelineName: string): Observable { + return this._postsHub.getDataWithSyncStatusObservable(timelineName).pipe( + switchMap((state) => { + if (state.posts.length === 0) { + return of({ + ...state, + posts: [], + }); + } + + return combineLatest([ + combineLatest( + state.posts.map((post) => userInfoService.getUser$(post.author)) + ), + combineLatest( + state.posts.map((post) => { + if (post.content.type === "image") { + return this.getPostData$(timelineName, post.id); + } else { + return of(null); + } + }) + ), + ]).pipe( + map(([authors, datas]) => { + return { + ...state, + posts: state.posts.map((post, i) => { + const { content } = post; + + return { + ...post, + author: authors[i], + content: (() => { + if (content.type === "text") return content; + else + return { + type: "image", + data: datas[i], + } as TimelinePostImageContent; + })(), + }; + }), + }; + }) + ); + }) + ); + } + + private getCachedPostData(key: { + timelineName: string; + postId: number; + }): Promise { + return dataStorage.getItem( + `timeline.${key.timelineName}.post.${key.postId}.data` + ); + } + + private savePostData( + key: { + timelineName: string; + postId: number; + }, + data: BlobWithEtag + ): Promise { + return dataStorage + .setItem( + `timeline.${key.timelineName}.post.${key.postId}.data`, + data + ) + .then(); + } + + private syncPostData(key: { + timelineName: string; + postId: number; + }): Promise { + return this._postDataHub.getLineOrCreate(key).sync(); + } + + private _postDataHub = new DataHub< + { timelineName: string; postId: number }, + | { data: Blob; type: "cache" | "synced" | "offline" } + | { data?: undefined; type: "notexist" | "offline" } + >({ + keyToString: (key) => `${key.timelineName}.${key.postId}`, + sync: async (key, line) => { + const cache = await this.getCachedPostData(key); + if (line.value == null) { + if (cache != null) { + line.next({ type: "cache", data: cache.data }); + } + } + + if (cache == null) { + try { + const res = await getHttpTimelineClient().getPostData( + key.timelineName, + key.postId + ); + await this.savePostData(key, res); + line.next({ data: res.data, type: "synced" }); + } catch (e) { + line.next({ type: "offline" }); + throwIfNotNetworkError(e); + } + } else { + try { + const res = await getHttpTimelineClient().getPostData( + key.timelineName, + key.postId, + cache.etag + ); + if (res instanceof NotModified) { + line.next({ data: cache.data, type: "synced" }); + } else { + await this.savePostData(key, res); + line.next({ data: res.data, type: "synced" }); + } + } catch (e) { + line.next({ data: cache.data, type: "offline" }); + throwIfNotNetworkError(e); + } + } + }, + }); + + getPostData$(timelineName: string, postId: number): Observable { + return this._postDataHub.getObservable({ timelineName, postId }).pipe( + map((state): BlobOrStatus => state.data ?? "error"), + startWith("loading") + ); + } + + createPost( + timelineName: string, + request: TimelineCreatePostRequest + ): Observable { + const user = checkLogin(); + return from( + getHttpTimelineClient() + .postPost(timelineName, request, user.token) + .then(() => { + void this.syncPosts(timelineName); + }) + ); + } + + deletePost(timelineName: string, postId: number): Observable { + const user = checkLogin(); + return from( + getHttpTimelineClient() + .deletePost(timelineName, postId, user.token) + .then(() => { + void this.syncPosts(timelineName); + }) + ); + } + + isMemberOf(username: string, timeline: TimelineInfo): boolean { + return timeline.members.findIndex((m) => m.username == username) >= 0; + } + + hasReadPermission( + user: UserAuthInfo | null | undefined, + timeline: TimelineInfo + ): boolean { + if (user != null && user.administrator) return true; + + const { visibility } = timeline; + if (visibility === "Public") { + return true; + } else if (visibility === "Register") { + if (user != null) return true; + } else if (visibility === "Private") { + if ( + user != null && + (user.username === timeline.owner.username || + this.isMemberOf(user.username, timeline)) + ) { + return true; + } + } + return false; + } + + hasPostPermission( + user: UserAuthInfo | null | undefined, + timeline: TimelineInfo + ): boolean { + if (user != null && user.administrator) return true; + + return ( + user != null && + (timeline.owner.username === user.username || + this.isMemberOf(user.username, timeline)) + ); + } + + hasManagePermission( + user: UserAuthInfo | null | undefined, + timeline: TimelineInfo + ): boolean { + if (user != null && user.administrator) return true; + + return user != null && user.username == timeline.owner.username; + } + + hasModifyPostPermission( + user: UserAuthInfo | null | undefined, + timeline: TimelineInfo, + post: TimelinePostInfo + ): boolean { + if (user != null && user.administrator) return true; + + return ( + user != null && + (user.username === timeline.owner.username || + user.username === post.author.username) + ); + } +} + +export const timelineService = new TimelineService(); + +const timelineNameReg = XRegExp("^[-_\\p{L}]*$", "u"); + +export function validateTimelineName(name: string): boolean { + return timelineNameReg.test(name); +} + +export function useTimelineInfo( + timelineName: string +): TimelineWithSyncStatus | undefined { + const [state, setState] = React.useState( + undefined + ); + React.useEffect(() => { + const subscription = timelineService + .getTimeline$(timelineName) + .subscribe((data) => { + setState(data); + }); + return () => { + subscription.unsubscribe(); + }; + }, [timelineName]); + return state; +} + +export function usePostList( + timelineName: string | null | undefined +): TimelinePostsWithSyncState | undefined { + const [state, setState] = React.useState< + TimelinePostsWithSyncState | undefined + >(undefined); + React.useEffect(() => { + if (timelineName == null) { + setState(undefined); + return; + } + + const subscription = timelineService + .getPosts$(timelineName) + .subscribe((data) => { + setState(data); + }); + return () => { + subscription.unsubscribe(); + }; + }, [timelineName]); + return state; +} + +export async function getAllCachedTimelineNames(): Promise { + const keys = await dataStorage.keys(); + return keys + .filter( + (key) => + key.startsWith("timeline.") && (key.match(/\./g) ?? []).length === 1 + ) + .map((key) => key.substr("timeline.".length)); +} diff --git a/Timeline/ClientApp/src/app/services/user.ts b/Timeline/ClientApp/src/app/services/user.ts new file mode 100644 index 00000000..f253fc19 --- /dev/null +++ b/Timeline/ClientApp/src/app/services/user.ts @@ -0,0 +1,393 @@ +import React, { useState, useEffect } from "react"; +import { BehaviorSubject, Observable, from } from "rxjs"; +import { map, filter } from "rxjs/operators"; + +import { UiLogicError } from "@/common"; +import { convertError } from "@/utilities/rxjs"; + +import { HttpNetworkError, BlobWithEtag, NotModified } from "@/http/common"; +import { + getHttpTokenClient, + HttpCreateTokenBadCredentialError, +} from "@/http/token"; +import { + getHttpUserClient, + HttpUserNotExistError, + HttpUser, +} from "@/http/user"; + +import { dataStorage, throwIfNotNetworkError } from "./common"; +import { DataHub } from "./DataHub"; +import { pushAlert } from "./alert"; + +export type User = HttpUser; + +export interface UserAuthInfo { + username: string; + administrator: boolean; +} + +export interface UserWithToken extends User { + token: string; +} + +export interface LoginCredentials { + username: string; + password: string; +} + +export class BadCredentialError { + message = "login.badCredential"; +} + +const USER_STORAGE_KEY = "currentuser"; + +export class UserService { + private userSubject = new BehaviorSubject( + undefined + ); + + get user$(): Observable { + return this.userSubject; + } + + get currentUser(): UserWithToken | null | undefined { + return this.userSubject.value; + } + + async checkLoginState(): Promise { + if (this.currentUser !== undefined) { + console.warn("Already checked user. Can't check twice."); + } + + const savedUser = await dataStorage.getItem( + USER_STORAGE_KEY + ); + + if (savedUser == null) { + this.userSubject.next(null); + return null; + } + + this.userSubject.next(savedUser); + + const savedToken = savedUser.token; + try { + const res = await getHttpTokenClient().verify({ token: savedToken }); + const user: UserWithToken = { ...res.user, token: savedToken }; + await dataStorage.setItem(USER_STORAGE_KEY, user); + this.userSubject.next(user); + pushAlert({ + type: "success", + message: { + type: "i18n", + key: "user.welcomeBack", + }, + }); + return user; + } catch (error) { + if (error instanceof HttpNetworkError) { + pushAlert({ + type: "danger", + message: { type: "i18n", key: "user.verifyTokenFailedNetwork" }, + }); + return savedUser; + } else { + await dataStorage.removeItem(USER_STORAGE_KEY); + this.userSubject.next(null); + pushAlert({ + type: "danger", + message: { type: "i18n", key: "user.verifyTokenFailed" }, + }); + return null; + } + } + } + + async login( + credentials: LoginCredentials, + rememberMe: boolean + ): Promise { + if (this.currentUser) { + throw new UiLogicError("Already login."); + } + try { + const res = await getHttpTokenClient().create({ + ...credentials, + expire: 30, + }); + const user: UserWithToken = { + ...res.user, + token: res.token, + }; + if (rememberMe) { + await dataStorage.setItem(USER_STORAGE_KEY, user); + } + this.userSubject.next(user); + } catch (e) { + if (e instanceof HttpCreateTokenBadCredentialError) { + throw new BadCredentialError(); + } else { + throw e; + } + } + } + + async logout(): Promise { + if (this.currentUser === undefined) { + throw new UiLogicError("Please check user first."); + } + if (this.currentUser === null) { + throw new UiLogicError("No login."); + } + await dataStorage.removeItem(USER_STORAGE_KEY); + this.userSubject.next(null); + } + + changePassword( + oldPassword: string, + newPassword: string + ): Observable { + if (this.currentUser == undefined) { + throw new UiLogicError("Not login or checked now, can't log out."); + } + const $ = from( + getHttpUserClient().changePassword( + { + oldPassword, + newPassword, + }, + this.currentUser.token + ) + ); + $.subscribe(() => { + void this.logout(); + }); + return $; + } +} + +export const userService = new UserService(); + +export function useRawUser(): UserWithToken | null | undefined { + const [user, setUser] = useState( + userService.currentUser + ); + useEffect(() => { + const subscription = userService.user$.subscribe((u) => setUser(u)); + return () => { + subscription.unsubscribe(); + }; + }); + return user; +} + +export function useUser(): UserWithToken | null { + const [user, setUser] = useState(() => { + const initUser = userService.currentUser; + if (initUser === undefined) { + throw new UiLogicError( + "This is a logic error in user module. Current user can't be undefined in useUser." + ); + } + return initUser; + }); + useEffect(() => { + const sub = userService.user$.subscribe((u) => { + if (u === undefined) { + throw new UiLogicError( + "This is a logic error in user module. User emitted can't be undefined later." + ); + } + setUser(u); + }); + return () => { + sub.unsubscribe(); + }; + }); + return user; +} + +export function useUserLoggedIn(): UserWithToken { + const user = useUser(); + if (user == null) { + throw new UiLogicError("You assert user has logged in but actually not."); + } + return user; +} + +export function checkLogin(): UserWithToken { + const user = userService.currentUser; + if (user == null) { + throw new UiLogicError("You must login to perform the operation."); + } + return user; +} + +export class UserNotExistError extends Error {} + +export class UserInfoService { + saveUser(user: HttpUser): void { + const key = user.username; + void this._userHub.optionalInitLineWithSyncAction(key, async (line) => { + await this.doSaveUser(user); + line.next({ user, type: "synced" }); + }); + } + + saveUsers(users: HttpUser[]): void { + return users.forEach((user) => this.saveUser(user)); + } + + private getCachedUser(username: string): Promise { + return dataStorage.getItem(`user.${username}`); + } + + private doSaveUser(user: HttpUser): Promise { + return dataStorage.setItem(`user.${user.username}`, user).then(); + } + + syncUser(username: string): Promise { + return this._userHub.getLineOrCreate(username).sync(); + } + + private _userHub = new DataHub< + string, + | { user: User; type: "cache" | "synced" | "offline" } + | { user?: undefined; type: "notexist" | "offline" } + >({ + sync: async (key, line) => { + if (line.value == undefined) { + const cache = await this.getCachedUser(key); + if (cache != null) { + line.next({ user: cache, type: "cache" }); + } + } + + try { + const res = await getHttpUserClient().get(key); + await this.doSaveUser(res); + line.next({ user: res, type: "synced" }); + } catch (e) { + if (e instanceof HttpUserNotExistError) { + line.next({ type: "notexist" }); + } else { + const cache = await this.getCachedUser(key); + line.next({ user: cache ?? undefined, type: "offline" }); + throwIfNotNetworkError(e); + } + } + }, + }); + + getUser$(username: string): Observable { + return this._userHub.getObservable(username).pipe( + map((state) => state?.user), + filter((user): user is User => user != null) + ); + } + + private getCachedAvatar(username: string): Promise { + return dataStorage.getItem(`user.${username}.avatar`); + } + + private saveAvatar(username: string, data: BlobWithEtag): Promise { + return dataStorage + .setItem(`user.${username}.avatar`, data) + .then(); + } + + syncAvatar(username: string): Promise { + return this._avatarHub.getLineOrCreate(username).sync(); + } + + private _avatarHub = new DataHub< + string, + | { data: Blob; type: "cache" | "synced" | "offline" } + | { data?: undefined; type: "notexist" | "offline" } + >({ + sync: async (key, line) => { + const cache = await this.getCachedAvatar(key); + if (line.value == null) { + if (cache != null) { + line.next({ data: cache.data, type: "cache" }); + } + } + + if (cache == null) { + try { + const avatar = await getHttpUserClient().getAvatar(key); + await this.saveAvatar(key, avatar); + line.next({ data: avatar.data, type: "synced" }); + } catch (e) { + line.next({ type: "offline" }); + throwIfNotNetworkError(e); + } + } else { + try { + const res = await getHttpUserClient().getAvatar(key, cache.etag); + if (res instanceof NotModified) { + line.next({ data: cache.data, type: "synced" }); + } else { + const avatar = res; + await this.saveAvatar(key, avatar); + line.next({ data: avatar.data, type: "synced" }); + } + } catch (e) { + line.next({ data: cache.data, type: "offline" }); + throwIfNotNetworkError(e); + } + } + }, + }); + + getAvatar$(username: string): Observable { + return this._avatarHub.getObservable(username).pipe( + map((state) => state.data), + filter((blob): blob is Blob => blob != null) + ); + } + + getUserInfo(username: string): Observable { + return from(getHttpUserClient().get(username)).pipe( + convertError(HttpUserNotExistError, UserNotExistError) + ); + } + + async setAvatar(username: string, blob: Blob): Promise { + const user = checkLogin(); + await getHttpUserClient().putAvatar(username, blob, user.token); + this._avatarHub.getLine(username)?.next({ data: blob, type: "synced" }); + } + + async setNickname(username: string, nickname: string): Promise { + const user = checkLogin(); + return getHttpUserClient() + .patch(username, { nickname }, user.token) + .then((user) => { + this.saveUser(user); + }); + } +} + +export const userInfoService = new UserInfoService(); + +export function useAvatar(username?: string): Blob | undefined { + const [state, setState] = React.useState(undefined); + React.useEffect(() => { + if (username == null) { + setState(undefined); + return; + } + + const subscription = userInfoService + .getAvatar$(username) + .subscribe((blob) => { + setState(blob); + }); + return () => { + subscription.unsubscribe(); + }; + }, [username]); + return state; +} diff --git a/Timeline/ClientApp/src/app/settings/Settings.tsx b/Timeline/ClientApp/src/app/settings/Settings.tsx deleted file mode 100644 index 64851ce2..00000000 --- a/Timeline/ClientApp/src/app/settings/Settings.tsx +++ /dev/null @@ -1,221 +0,0 @@ -import React, { useState } from "react"; -import { useHistory } from "react-router"; -import { useTranslation } from "react-i18next"; -import { - Container, - Row, - Col, - Input, - Modal, - ModalHeader, - ModalBody, - ModalFooter, - Button, -} from "reactstrap"; - -import { useUser, userService } from "../data/user"; -import AppBar from "../common/AppBar"; -import OperationDialog, { - OperationInputErrorInfo, -} from "../common/OperationDialog"; - -interface ChangePasswordDialogProps { - open: boolean; - close: () => void; -} - -const ChangePasswordDialog: React.FC = (props) => { - const history = useHistory(); - const { t } = useTranslation(); - - const [redirect, setRedirect] = useState(false); - - return ( - - v === "" - ? "settings.dialogChangePassword.errorEmptyOldPassword" - : null, - }, - { - type: "text", - label: t("settings.dialogChangePassword.inputNewPassword"), - password: true, - validator: (v, values) => { - const error: OperationInputErrorInfo = {}; - error[1] = - v === "" - ? "settings.dialogChangePassword.errorEmptyNewPassword" - : null; - if (v === values[2]) { - error[2] = null; - } else { - if (values[2] !== "") { - error[2] = "settings.dialogChangePassword.errorRetypeNotMatch"; - } - } - return error; - }, - }, - { - type: "text", - label: t("settings.dialogChangePassword.inputRetypeNewPassword"), - password: true, - validator: (v, values) => - v !== values[1] - ? "settings.dialogChangePassword.errorRetypeNotMatch" - : null, - }, - ]} - onProcess={async ([oldPassword, newPassword]) => { - await userService - .changePassword(oldPassword as string, newPassword as string) - .toPromise(); - await userService.logout(); - setRedirect(true); - }} - close={() => { - props.close(); - if (redirect) { - history.push("/login"); - } - }} - /> - ); -}; - -const ConfirmLogoutDialog: React.FC<{ - toggle: () => void; - onConfirm: () => void; -}> = ({ toggle, onConfirm }) => { - const { t } = useTranslation(); - - return ( - - - {t("settings.dialogConfirmLogout.title")} - - {t("settings.dialogConfirmLogout.prompt")} - - - - - - ); -}; - -const Settings: React.FC = (_) => { - const { i18n, t } = useTranslation(); - const user = useUser(); - const history = useHistory(); - - const [dialog, setDialog] = useState( - null - ); - - const language = i18n.language.slice(0, 2); - - return ( - <> - - - {user ? ( - <> - - -
{ - history.push(`/users/${user.username}`); - }} - > - {t("settings.gotoSelf")} -
- -
- - -
setDialog("changepassword")} - > - {t("settings.changePassword")} -
- -
- - -
{ - setDialog("logout"); - }} - > - {t("settings.logout")} -
- -
- - ) : null} - - -
{t("settings.languagePrimary")}
-

{t("settings.languageSecondary")}

- - - { - void i18n.changeLanguage(e.target.value); - }} - > - - - - -
- {(() => { - switch (dialog) { - case "changepassword": - return ( - { - setDialog(null); - }} - /> - ); - case "logout": - return ( - setDialog(null)} - onConfirm={() => { - void userService.logout().then(() => { - history.push("/"); - }); - }} - /> - ); - default: - return null; - } - })()} -
- - ); -}; - -export default Settings; diff --git a/Timeline/ClientApp/src/app/timeline/Timeline.tsx b/Timeline/ClientApp/src/app/timeline/Timeline.tsx deleted file mode 100644 index 780588d1..00000000 --- a/Timeline/ClientApp/src/app/timeline/Timeline.tsx +++ /dev/null @@ -1,88 +0,0 @@ -import React from "react"; -import clsx from "clsx"; - -import { TimelinePostInfo } from "../data/timeline"; - -import TimelineItem from "./TimelineItem"; - -export interface TimelinePostInfoEx extends TimelinePostInfo { - deletable: boolean; -} - -export type TimelineDeleteCallback = (index: number, id: number) => void; - -export interface TimelineProps { - className?: string; - posts: TimelinePostInfoEx[]; - onDelete: TimelineDeleteCallback; - onResize?: () => void; - containerRef?: React.Ref; -} - -const Timeline: React.FC = (props) => { - const { posts, onDelete, onResize } = props; - - const [indexShowDeleteButton, setIndexShowDeleteButton] = React.useState< - number - >(-1); - - const onItemClick = React.useCallback(() => { - setIndexShowDeleteButton(-1); - }, []); - - const onToggleDelete = React.useMemo(() => { - return posts.map((post, i) => { - return post.deletable - ? () => { - setIndexShowDeleteButton((oldIndexShowDeleteButton) => { - return oldIndexShowDeleteButton !== i ? i : -1; - }); - } - : undefined; - }); - }, [posts]); - - const onItemDelete = React.useMemo(() => { - return posts.map((post, i) => { - return () => { - onDelete(i, post.id); - }; - }); - }, [posts, onDelete]); - - return ( -
-
- {(() => { - const length = posts.length; - return posts.map((post, i) => { - const toggleMore = onToggleDelete[i]; - - return ( - - ); - }); - })()} -
- ); -}; - -export default Timeline; diff --git a/Timeline/ClientApp/src/app/timeline/TimelineDeleteDialog.tsx b/Timeline/ClientApp/src/app/timeline/TimelineDeleteDialog.tsx deleted file mode 100644 index 5ebbf9df..00000000 --- a/Timeline/ClientApp/src/app/timeline/TimelineDeleteDialog.tsx +++ /dev/null @@ -1,54 +0,0 @@ -import React from "react"; -import { useHistory } from "react-router"; -import { Trans } from "react-i18next"; - -import OperationDialog from "../common/OperationDialog"; -import { timelineService } from "../data/timeline"; - -interface TimelineDeleteDialog { - open: boolean; - name: string; - close: () => void; -} - -const TimelineDeleteDialog: React.FC = (props) => { - const history = useHistory(); - - const { name } = props; - - return ( - { - return ( - - 0{{ name }}2 - - ); - }} - inputScheme={[ - { - type: "text", - validator: (value) => { - if (value !== name) { - return "timeline.deleteDialog.notMatch"; - } else { - return null; - } - }, - }, - ]} - onProcess={() => { - return timelineService.deleteTimeline(name).toPromise(); - }} - onSuccessAndClose={() => { - history.replace("/"); - }} - /> - ); -}; - -export default TimelineDeleteDialog; diff --git a/Timeline/ClientApp/src/app/timeline/TimelineInfoCard.tsx b/Timeline/ClientApp/src/app/timeline/TimelineInfoCard.tsx deleted file mode 100644 index c11c3376..00000000 --- a/Timeline/ClientApp/src/app/timeline/TimelineInfoCard.tsx +++ /dev/null @@ -1,110 +0,0 @@ -import React from "react"; -import clsx from "clsx"; -import { - Dropdown, - DropdownToggle, - DropdownMenu, - DropdownItem, - Button, -} from "reactstrap"; -import { useTranslation } from "react-i18next"; -import { fromEvent } from "rxjs"; - -import { useAvatar } from "../data/user"; -import { timelineVisibilityTooltipTranslationMap } from "../data/timeline"; -import BlobImage from "../common/BlobImage"; - -import { TimelineCardComponentProps } from "./TimelinePageTemplateUI"; - -export type OrdinaryTimelineManageItem = "delete"; - -export type TimelineInfoCardProps = TimelineCardComponentProps< - OrdinaryTimelineManageItem ->; - -const TimelineInfoCard: React.FC = (props) => { - const { onHeight, onManage } = props; - - const { t } = useTranslation(); - - const avatar = useAvatar(props.timeline.owner.username); - - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const containerRef = React.useRef(null!); - - const notifyHeight = React.useCallback((): void => { - if (onHeight) { - onHeight(containerRef.current.getBoundingClientRect().height); - } - }, [onHeight]); - - React.useEffect(() => { - const subscription = fromEvent(window, "resize").subscribe(notifyHeight); - return () => subscription.unsubscribe(); - }); - - const [manageDropdownOpen, setManageDropdownOpen] = React.useState( - false - ); - const toggleManageDropdown = React.useCallback( - (): void => setManageDropdownOpen((old) => !old), - [] - ); - - return ( -
-

- {props.timeline.name} -

-
- - {props.timeline.owner.nickname} - - @{props.timeline.owner.username} - -
-

{props.timeline.description}

- - {t(timelineVisibilityTooltipTranslationMap[props.timeline.visibility])} - -
- {onManage != null ? ( - - - {t("timeline.manage")} - - - onManage("property")}> - {t("timeline.manageItem.property")} - - - {t("timeline.manageItem.member")} - - - onManage("delete")} - > - {t("timeline.manageItem.delete")} - - - - ) : ( - - )} -
-
- ); -}; - -export default TimelineInfoCard; diff --git a/Timeline/ClientApp/src/app/timeline/TimelineItem.tsx b/Timeline/ClientApp/src/app/timeline/TimelineItem.tsx deleted file mode 100644 index 33f0741e..00000000 --- a/Timeline/ClientApp/src/app/timeline/TimelineItem.tsx +++ /dev/null @@ -1,181 +0,0 @@ -import React from "react"; -import clsx from "clsx"; -import { - Row, - Col, - Modal, - ModalHeader, - ModalBody, - ModalFooter, - Button, -} from "reactstrap"; -import { Link } from "react-router-dom"; -import { useTranslation } from "react-i18next"; -import Svg from "react-inlinesvg"; -import chevronDownIcon from "bootstrap-icons/icons/chevron-down.svg"; -import trashIcon from "bootstrap-icons/icons/trash.svg"; - -import BlobImage from "../common/BlobImage"; -import { useAvatar } from "../data/user"; -import { TimelinePostInfo } from "../data/timeline"; - -const TimelinePostDeleteConfirmDialog: React.FC<{ - toggle: () => void; - onConfirm: () => void; -}> = ({ toggle, onConfirm }) => { - const { t } = useTranslation(); - - return ( - - - {t("timeline.post.deleteDialog.title")} - - {t("timeline.post.deleteDialog.prompt")} - - - - - - ); -}; - -export interface TimelineItemProps { - post: TimelinePostInfo; - current?: boolean; - more?: { - isOpen: boolean; - toggle: () => void; - onDelete: () => void; - }; - onClick?: () => void; - onResize?: () => void; - className?: string; - style?: React.CSSProperties; -} - -const TimelineItem: React.FC = (props) => { - const { i18n } = useTranslation(); - - const current = props.current === true; - - const { more, onResize } = props; - - const avatar = useAvatar(props.post.author.username); - - const [deleteDialog, setDeleteDialog] = React.useState(false); - const toggleDeleteDialog = React.useCallback( - () => setDeleteDialog((old) => !old), - [] - ); - - return ( - - -
-
-
-
-
- {current &&
} - - - -
- - - {props.post.time.toLocaleString(i18n.languages)} - - - {props.post.author.nickname} - - -
- {more != null ? ( -
- { - more.toggle(); - e.stopPropagation(); - }} - /> -
- ) : null} -
-
- - - - {(() => { - const { content } = props.post; - if (content.type === "text") { - return content.text; - } else { - return ( - - ); - } - })()} -
- - {more != null && more.isOpen ? ( - <> -
- { - toggleDeleteDialog(); - e.stopPropagation(); - }} - /> -
- {deleteDialog ? ( - { - toggleDeleteDialog(); - more.toggle(); - }} - onConfirm={more.onDelete} - /> - ) : null} - - ) : null} - - ); -}; - -export default TimelineItem; diff --git a/Timeline/ClientApp/src/app/timeline/TimelineMember.tsx b/Timeline/ClientApp/src/app/timeline/TimelineMember.tsx deleted file mode 100644 index f334c6e9..00000000 --- a/Timeline/ClientApp/src/app/timeline/TimelineMember.tsx +++ /dev/null @@ -1,218 +0,0 @@ -import React, { useState } from "react"; -import { useTranslation } from "react-i18next"; -import { - Container, - ListGroup, - ListGroupItem, - Modal, - Row, - Col, - Button, -} from "reactstrap"; - -import { User, useAvatar } from "../data/user"; -import SearchInput from "../common/SearchInput"; -import BlobImage from "../common/BlobImage"; - -const TimelineMemberItem: React.FC<{ - user: User; - owner: boolean; - onRemove?: (username: string) => void; -}> = ({ user, owner, onRemove }) => { - const { t } = useTranslation(); - - const avatar = useAvatar(user.username); - - return ( - - - - - - - {user.nickname} - - {"@" + user.username} - - - {(() => { - if (owner) { - return null; - } - if (onRemove == null) { - return null; - } - return ( - - ); - })()} - - - ); -}; - -export interface TimelineMemberCallbacks { - onCheckUser: (username: string) => Promise; - onAddUser: (user: User) => Promise; - onRemoveUser: (username: string) => void; -} - -export interface TimelineMemberProps { - members: User[]; - edit: TimelineMemberCallbacks | null | undefined; -} - -const TimelineMember: React.FC = (props) => { - const { t } = useTranslation(); - - const [userSearchText, setUserSearchText] = useState(""); - const [userSearchState, setUserSearchState] = useState< - | { - type: "user"; - data: User; - } - | { type: "error"; data: string } - | { type: "loading" } - | { type: "init" } - >({ type: "init" }); - - const userSearchAvatar = useAvatar( - userSearchState.type === "user" ? userSearchState.data.username : undefined - ); - - const members = props.members; - - return ( - - - {members.map((member, index) => ( - - ))} - - {(() => { - const edit = props.edit; - if (edit != null) { - return ( - <> - { - setUserSearchText(v); - }} - loading={userSearchState.type === "loading"} - onButtonClick={() => { - if (userSearchText === "") { - setUserSearchState({ - type: "error", - data: "login.emptyUsername", - }); - return; - } - - setUserSearchState({ type: "loading" }); - edit.onCheckUser(userSearchText).then( - (u) => { - if (u == null) { - setUserSearchState({ - type: "error", - data: "timeline.userNotExist", - }); - } else { - setUserSearchState({ type: "user", data: u }); - } - }, - (e) => { - setUserSearchState({ - type: "error", - data: `${e as string}`, - }); - } - ); - }} - /> - {(() => { - if (userSearchState.type === "user") { - const u = userSearchState.data; - const addable = - members.findIndex((m) => m.username === u.username) === -1; - return ( - <> - {!addable ? ( -

{t("timeline.member.alreadyMember")}

- ) : null} - - - - - - - {u.nickname} - - {"@" + u.username} - - - - - - - ); - } else if (userSearchState.type === "error") { - return ( -

{t(userSearchState.data)}

- ); - } - })()} - - ); - } else { - return null; - } - })()} -
- ); -}; - -export default TimelineMember; - -export interface TimelineMemberDialogProps extends TimelineMemberProps { - open: boolean; - onClose: () => void; -} - -export const TimelineMemberDialog: React.FC = ( - props -) => { - return ( - - - - ); -}; diff --git a/Timeline/ClientApp/src/app/timeline/TimelinePage.tsx b/Timeline/ClientApp/src/app/timeline/TimelinePage.tsx deleted file mode 100644 index 21d52db1..00000000 --- a/Timeline/ClientApp/src/app/timeline/TimelinePage.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import React from "react"; -import { useParams } from "react-router"; - -import TimelinePageTemplate from "../timeline/TimelinePageTemplate"; - -import TimelinePageUI from "./TimelinePageUI"; -import { OrdinaryTimelineManageItem } from "./TimelineInfoCard"; -import TimelineDeleteDialog from "./TimelineDeleteDialog"; - -const TimelinePage: React.FC = (_) => { - const { name } = useParams<{ name: string }>(); - - const [dialog, setDialog] = React.useState( - null - ); - - let dialogElement: React.ReactElement | undefined; - if (dialog === "delete") { - dialogElement = ( - setDialog(null)} name={name} /> - ); - } - - return ( - <> - setDialog(item)} - notFoundI18nKey="timeline.timelineNotExist" - /> - {dialogElement} - - ); -}; - -export default TimelinePage; diff --git a/Timeline/ClientApp/src/app/timeline/TimelinePageTemplate.tsx b/Timeline/ClientApp/src/app/timeline/TimelinePageTemplate.tsx deleted file mode 100644 index 62470e63..00000000 --- a/Timeline/ClientApp/src/app/timeline/TimelinePageTemplate.tsx +++ /dev/null @@ -1,190 +0,0 @@ -import React from "react"; -import { useTranslation } from "react-i18next"; -import { of } from "rxjs"; -import { catchError } from "rxjs/operators"; - -import { ExcludeKey } from "../utilities/type"; -import { pushAlert } from "../common/alert-service"; -import { useUser, userInfoService, UserNotExistError } from "../data/user"; -import { - timelineService, - usePostList, - useTimelineInfo, -} from "../data/timeline"; -import { UiLogicError } from "../common"; - -import { TimelineDeleteCallback } from "./Timeline"; -import { TimelineMemberDialog } from "./TimelineMember"; -import TimelinePropertyChangeDialog from "./TimelinePropertyChangeDialog"; -import { TimelinePageTemplateUIProps } from "./TimelinePageTemplateUI"; -import { TimelinePostSendCallback } from "./TimelinePostEdit"; - -export interface TimelinePageTemplateProps { - name: string; - onManage: (item: TManageItem) => void; - UiComponent: React.ComponentType< - ExcludeKey, "CardComponent"> - >; - notFoundI18nKey: string; -} - -export default function TimelinePageTemplate( - props: TimelinePageTemplateProps -): React.ReactElement | null { - const { t } = useTranslation(); - - const { name } = props; - - const service = timelineService; - - const user = useUser(); - - const [dialog, setDialog] = React.useState( - null - ); - - const timelineState = useTimelineInfo(name); - - const timeline = timelineState?.timeline; - - const postListState = usePostList(name); - - const error: string | undefined = (() => { - if (timelineState != null) { - const { type, timeline } = timelineState; - if (type === "offline" && timeline == null) return "Network Error"; - if (type === "synced" && timeline == null) - return t(props.notFoundI18nKey); - } - return undefined; - })(); - - const closeDialog = React.useCallback((): void => { - setDialog(null); - }, []); - - let dialogElement: React.ReactElement | undefined; - - if (dialog === "property") { - if (timeline == null) { - throw new UiLogicError( - "Timeline is null but attempt to open change property dialog." - ); - } - - dialogElement = ( - { - return service.changeTimelineProperty(name, req).toPromise().then(); - }} - /> - ); - } else if (dialog === "member") { - if (timeline == null) { - throw new UiLogicError( - "Timeline is null but attempt to open change property dialog." - ); - } - - dialogElement = ( - { - return userInfoService - .getUserInfo(u) - .pipe( - catchError((e) => { - if (e instanceof UserNotExistError) { - return of(null); - } else { - throw e; - } - }) - ) - .toPromise(); - }, - onAddUser: (u) => { - return service.addMember(name, u.username).toPromise().then(); - }, - onRemoveUser: (u) => { - service.removeMember(name, u); - }, - } - : null - } - /> - ); - } - - const { UiComponent } = props; - - const onDelete: TimelineDeleteCallback = React.useCallback( - (index, id) => { - service.deletePost(name, id).subscribe(null, () => { - pushAlert({ - type: "danger", - message: t("timeline.deletePostFailed"), - }); - }); - }, - [service, name, t] - ); - - const onPost: TimelinePostSendCallback = React.useCallback( - (req) => { - return service.createPost(name, req).toPromise().then(); - }, - [service, name] - ); - - const onManageProp = props.onManage; - - const onManage = React.useCallback( - (item: "property" | TManageItem) => { - if (item === "property") { - setDialog(item); - } else { - onManageProp(item); - } - }, - [onManageProp] - ); - - const onMember = React.useCallback(() => { - setDialog("member"); - }, []); - - return ( - <> - - {dialogElement} - - ); -} diff --git a/Timeline/ClientApp/src/app/timeline/TimelinePageTemplateUI.tsx b/Timeline/ClientApp/src/app/timeline/TimelinePageTemplateUI.tsx deleted file mode 100644 index e6514478..00000000 --- a/Timeline/ClientApp/src/app/timeline/TimelinePageTemplateUI.tsx +++ /dev/null @@ -1,324 +0,0 @@ -import React, { CSSProperties } from "react"; -import { Spinner } from "reactstrap"; -import { useTranslation } from "react-i18next"; -import { fromEvent } from "rxjs"; -import Svg from "react-inlinesvg"; -import clsx from "clsx"; -import arrowsAngleContractIcon from "bootstrap-icons/icons/arrows-angle-contract.svg"; -import arrowsAngleExpandIcon from "bootstrap-icons/icons/arrows-angle-expand.svg"; - -import { getAlertHost } from "../common/alert-service"; -import { useEventEmiiter, UiLogicError } from "../common"; -import { - TimelineInfo, - TimelinePostsWithSyncState, - timelineService, -} from "../data/timeline"; -import { userService } from "../data/user"; -import AppBar from "../common/AppBar"; - -import Timeline, { - TimelinePostInfoEx, - TimelineDeleteCallback, -} from "./Timeline"; -import TimelinePostEdit, { TimelinePostSendCallback } from "./TimelinePostEdit"; - -type TimelinePostSyncState = "syncing" | "synced" | "offline"; - -const TimelinePostSyncStateBadge: React.FC<{ - state: TimelinePostSyncState; - style?: CSSProperties; - className?: string; -}> = ({ state, style, className }) => { - const { t } = useTranslation(); - - return ( -
- {(() => { - switch (state) { - case "syncing": { - return ( - <> - - - {t("timeline.postSyncState.syncing")} - - - ); - } - case "synced": { - return ( - <> - - - {t("timeline.postSyncState.synced")} - - - ); - } - case "offline": { - return ( - <> - - - {t("timeline.postSyncState.offline")} - - - ); - } - default: - throw new UiLogicError("Unknown sync state."); - } - })()} -
- ); -}; - -export interface TimelineCardComponentProps { - timeline: TimelineInfo; - onManage?: (item: TManageItems | "property") => void; - onMember: () => void; - className?: string; - onHeight?: (height: number) => void; -} - -export interface TimelinePageTemplateUIProps { - avatarKey?: string | number; - timeline?: TimelineInfo; - postListState?: TimelinePostsWithSyncState; - CardComponent: React.ComponentType>; - onMember: () => void; - onManage?: (item: TManageItems | "property") => void; - onPost?: TimelinePostSendCallback; - onDelete: TimelineDeleteCallback; - error?: string; -} - -export default function TimelinePageTemplateUI( - props: TimelinePageTemplateUIProps -): React.ReactElement | null { - const { timeline, postListState } = props; - - const { t } = useTranslation(); - - const bottomSpaceRef = React.useRef(null); - - const onPostEditHeightChange = React.useCallback((height: number): void => { - const { current: bottomSpaceDiv } = bottomSpaceRef; - if (bottomSpaceDiv != null) { - bottomSpaceDiv.style.height = `${height}px`; - } - if (height === 0) { - const alertHost = getAlertHost(); - if (alertHost != null) { - alertHost.style.removeProperty("margin-bottom"); - } - } else { - const alertHost = getAlertHost(); - if (alertHost != null) { - alertHost.style.marginBottom = `${height}px`; - } - } - }, []); - - const timelineRef = React.useRef(null); - - const [getResizeEvent, triggerResizeEvent] = useEventEmiiter(); - - React.useEffect(() => { - const { current: timelineElement } = timelineRef; - if (timelineElement != null) { - let loadingScrollToBottom = true; - let pinBottom = false; - - const isAtBottom = (): boolean => - window.innerHeight + window.scrollY + 10 >= document.body.scrollHeight; - - const disableLoadingScrollToBottom = (): void => { - loadingScrollToBottom = false; - if (isAtBottom()) pinBottom = true; - }; - - const checkAndScrollToBottom = (): void => { - if (loadingScrollToBottom || pinBottom) { - window.scrollTo(0, document.body.scrollHeight); - } - }; - - const subscriptions = [ - fromEvent(timelineElement, "wheel").subscribe( - disableLoadingScrollToBottom - ), - fromEvent(timelineElement, "pointerdown").subscribe( - disableLoadingScrollToBottom - ), - fromEvent(timelineElement, "keydown").subscribe( - disableLoadingScrollToBottom - ), - fromEvent(window, "scroll").subscribe(() => { - if (loadingScrollToBottom) return; - - if (isAtBottom()) { - pinBottom = true; - } else { - pinBottom = false; - } - }), - fromEvent(window, "resize").subscribe(checkAndScrollToBottom), - getResizeEvent().subscribe(checkAndScrollToBottom), - ]; - - return () => { - subscriptions.forEach((s) => s.unsubscribe()); - }; - } - }, [getResizeEvent, triggerResizeEvent, timeline, postListState]); - - const [cardHeight, setCardHeight] = React.useState(0); - - const genCardCollapseLocalStorageKey = (uniqueId: string): string => - `timeline.${uniqueId}.cardCollapse`; - - const cardCollapseLocalStorageKey = - timeline != null ? genCardCollapseLocalStorageKey(timeline.uniqueId) : null; - - const [infoCardCollapse, setInfoCardCollapse] = React.useState(true); - React.useEffect(() => { - if (cardCollapseLocalStorageKey != null) { - const savedCollapse = - window.localStorage.getItem(cardCollapseLocalStorageKey) === "true"; - setInfoCardCollapse(savedCollapse); - } - }, [cardCollapseLocalStorageKey]); - - let body: React.ReactElement; - - if (props.error != null) { - body =

{t(props.error)}

; - } else { - if (timeline != null) { - let timelineBody: React.ReactElement; - if (postListState != null) { - if (postListState.type === "notexist") { - throw new UiLogicError( - "Timeline is not null but post list state is notexist." - ); - } - if (postListState.type === "forbid") { - timelineBody = ( -

{t("timeline.messageCantSee")}

- ); - } else { - const posts: TimelinePostInfoEx[] = postListState.posts.map( - (post) => ({ - ...post, - deletable: timelineService.hasModifyPostPermission( - userService.currentUser, - timeline, - post - ), - }) - ); - - const topHeight: string = infoCardCollapse - ? "calc(68px + 1.5em)" - : `${cardHeight + 60}px`; - - const syncState: TimelinePostSyncState = postListState.syncing - ? "syncing" - : postListState.type === "synced" - ? "synced" - : "offline"; - - timelineBody = ( -
- - -
- ); - if (props.onPost != null) { - timelineBody = ( - <> - {timelineBody} -
- - - ); - } - } - } else { - timelineBody = ( -
- -
- ); - } - const { CardComponent } = props; - - body = ( - <> -
- { - const newState = !infoCardCollapse; - setInfoCardCollapse(newState); - window.localStorage.setItem( - genCardCollapseLocalStorageKey(timeline.uniqueId), - newState.toString() - ); - }} - className="float-right m-1 info-card-collapse-button text-primary icon-button" - /> - -
- {timelineBody} - - ); - } else { - body = ( -
- -
- ); - } - } - - return ( - <> - -
-
- {body} -
- - ); -} diff --git a/Timeline/ClientApp/src/app/timeline/TimelinePageUI.tsx b/Timeline/ClientApp/src/app/timeline/TimelinePageUI.tsx deleted file mode 100644 index 63751eeb..00000000 --- a/Timeline/ClientApp/src/app/timeline/TimelinePageUI.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import React from "react"; - -import { ExcludeKey } from "../utilities/type"; - -import TimelinePageTemplateUI, { - TimelinePageTemplateUIProps, -} from "./TimelinePageTemplateUI"; -import TimelineInfoCard, { - OrdinaryTimelineManageItem, -} from "./TimelineInfoCard"; - -export type TimelinePageUIProps = ExcludeKey< - TimelinePageTemplateUIProps, - "CardComponent" ->; - -const TimelinePageUI: React.FC = (props) => { - return ; -}; - -export default TimelinePageUI; diff --git a/Timeline/ClientApp/src/app/timeline/TimelinePostEdit.tsx b/Timeline/ClientApp/src/app/timeline/TimelinePostEdit.tsx deleted file mode 100644 index b30dc8d3..00000000 --- a/Timeline/ClientApp/src/app/timeline/TimelinePostEdit.tsx +++ /dev/null @@ -1,232 +0,0 @@ -import React from "react"; -import { Button, Spinner, Row, Col } from "reactstrap"; -import { useTranslation } from "react-i18next"; -import Svg from "react-inlinesvg"; -import textIcon from "bootstrap-icons/icons/card-text.svg"; -import imageIcon from "bootstrap-icons/icons/image.svg"; - -import { pushAlert } from "../common/alert-service"; -import { TimelineCreatePostRequest } from "../data/timeline"; -import FileInput from "../common/FileInput"; -import { UiLogicError } from "../common"; - -interface TimelinePostEditImageProps { - onSelect: (blob: Blob | null) => void; -} - -const TimelinePostEditImage: React.FC = (props) => { - const { onSelect } = props; - const { t } = useTranslation(); - - const [file, setFile] = React.useState(null); - const [fileUrl, setFileUrl] = React.useState(null); - const [error, setError] = React.useState(null); - - React.useEffect(() => { - if (file != null) { - const url = URL.createObjectURL(file); - setFileUrl(url); - return () => { - URL.revokeObjectURL(url); - }; - } - }, [file]); - - const onInputChange: React.ChangeEventHandler = React.useCallback( - (e) => { - const files = e.target.files; - if (files == null || files.length === 0) { - setFile(null); - setFileUrl(null); - } else { - setFile(files[0]); - } - onSelect(null); - setError(null); - }, - [onSelect] - ); - - const onImgLoad = React.useCallback(() => { - onSelect(file); - }, [onSelect, file]); - - const onImgError = React.useCallback(() => { - setError("loadImageError"); - }, []); - - return ( - <> - - {fileUrl && error == null && ( - - )} - {error != null &&
{t(error)}
} - - ); -}; - -export type TimelinePostSendCallback = ( - content: TimelineCreatePostRequest -) => Promise; - -export interface TimelinePostEditProps { - className?: string; - onPost: TimelinePostSendCallback; - onHeightChange?: (height: number) => void; - timelineUniqueId: string; -} - -const TimelinePostEdit: React.FC = (props) => { - const { onPost } = props; - - const { t } = useTranslation(); - - const [state, setState] = React.useState<"input" | "process">("input"); - const [kind, setKind] = React.useState<"text" | "image">("text"); - const [text, setText] = React.useState(""); - const [imageBlob, setImageBlob] = React.useState(null); - - const draftLocalStorageKey = `timeline.${props.timelineUniqueId}.postDraft`; - - React.useEffect(() => { - setText(window.localStorage.getItem(draftLocalStorageKey) ?? ""); - }, [draftLocalStorageKey]); - - const canSend = kind === "text" || (kind === "image" && imageBlob != null); - - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const containerRef = React.useRef(null!); - - const notifyHeightChange = (): void => { - if (props.onHeightChange) { - props.onHeightChange(containerRef.current.clientHeight); - } - }; - - React.useEffect(() => { - if (props.onHeightChange) { - props.onHeightChange(containerRef.current.clientHeight); - } - return () => { - if (props.onHeightChange) { - props.onHeightChange(0); - } - }; - }); - - const toggleKind = React.useCallback(() => { - setKind((oldKind) => (oldKind === "text" ? "image" : "text")); - setImageBlob(null); - }, []); - - const onSend = React.useCallback(() => { - setState("process"); - - const req: TimelineCreatePostRequest = (() => { - switch (kind) { - case "text": - return { - content: { - type: "text", - text: text, - }, - } as TimelineCreatePostRequest; - case "image": - if (imageBlob == null) { - throw new UiLogicError( - "Content type is image but image blob is null." - ); - } - return { - content: { - type: "image", - data: imageBlob, - }, - } as TimelineCreatePostRequest; - default: - throw new UiLogicError("Unknown content type."); - } - })(); - - onPost(req).then( - (_) => { - if (kind === "text") { - setText(""); - window.localStorage.removeItem(draftLocalStorageKey); - } - setState("input"); - setKind("text"); - }, - (_) => { - pushAlert({ - type: "danger", - message: t("timeline.sendPostFailed"), - }); - setState("input"); - } - ); - }, [onPost, kind, text, imageBlob, t, draftLocalStorageKey]); - - const onImageSelect = React.useCallback((blob: Blob | null) => { - setImageBlob(blob); - }, []); - - return ( -
- - - {kind === "text" ? ( -