From 83d1bcc8488e4cd34b42828259a059fd429def47 Mon Sep 17 00:00:00 2001 From: Dreamacro <305009791@qq.com> Date: Fri, 13 Dec 2019 10:17:17 +0800 Subject: [PATCH] Feature: support provider --- package-lock.json | 379 +++++++++++------- package.json | 21 +- src/components/Card/index.tsx | 2 +- src/components/Col/index.tsx | 2 +- src/components/Header/index.tsx | 2 +- src/components/Icon/index.tsx | 2 +- src/components/Loading/Spinner/index.tsx | 31 ++ src/components/Loading/Spinner/style.scss | 61 +++ src/components/Loading/index.tsx | 35 ++ src/components/Loading/style.scss | 13 + src/components/Row/index.tsx | 2 +- src/components/Select/index.tsx | 2 +- src/components/Tag/index.tsx | 18 + src/components/Tag/style.scss | 14 + src/components/index.ts | 2 + .../Proxies/components/Group/index.tsx | 4 +- .../Proxies/components/Group/style.scss | 11 - .../Proxies/components/Provider/index.tsx | 60 +++ .../Proxies/components/Provider/style.scss | 62 +++ src/containers/Proxies/components/index.ts | 1 + src/containers/Proxies/index.tsx | 89 ++-- src/containers/Proxies/style.scss | 9 +- src/i18n/en_US.ts | 2 + src/i18n/zh_CN.ts | 2 + src/lib/hook.ts | 18 +- src/lib/request.ts | 41 ++ src/models/Config.ts | 2 + src/stores/HookStore.ts | 11 +- src/styles/common.scss | 4 +- src/styles/iconfont.scss | 6 +- 30 files changed, 683 insertions(+), 225 deletions(-) create mode 100644 src/components/Loading/Spinner/index.tsx create mode 100644 src/components/Loading/Spinner/style.scss create mode 100644 src/components/Loading/index.tsx create mode 100644 src/components/Loading/style.scss create mode 100644 src/components/Tag/index.tsx create mode 100644 src/components/Tag/style.scss create mode 100644 src/containers/Proxies/components/Provider/index.tsx create mode 100644 src/containers/Proxies/components/Provider/style.scss diff --git a/package-lock.json b/package-lock.json index e2dd12b..4f74d44 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,9 +5,9 @@ "requires": true, "dependencies": { "@babel/cli": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.7.4.tgz", - "integrity": "sha512-O7mmzaWdm+VabWQmxuM8hqNrWGGihN83KfhPUzp2lAW4kzIMwBxujXkZbD4fMwKMYY9FXTbDvXsJqU+5XHXi4A==", + "version": "7.7.5", + "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.7.5.tgz", + "integrity": "sha512-y2YrMGXM3NUyu1Myg0pxg+Lx6g8XhEyvLHYNRwTBV6fDek3H7Io6b7N/LXscLs4HWn4HxMdy7f2rM1rTMp2mFg==", "dev": true, "requires": { "chokidar": "^2.1.8", @@ -39,15 +39,15 @@ } }, "@babel/core": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.7.4.tgz", - "integrity": "sha512-+bYbx56j4nYBmpsWtnPUsKW3NdnYxbqyfrP2w9wILBuHzdfIKz9prieZK0DFPyIzkjYVUe4QkusGL07r5pXznQ==", + "version": "7.7.5", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.7.5.tgz", + "integrity": "sha512-M42+ScN4+1S9iB6f+TL7QBpoQETxbclx+KNoKJABghnKYE+fMzSGqst0BZJc8CpI625bwPwYgUyRvxZ+0mZzpw==", "dev": true, "requires": { "@babel/code-frame": "^7.5.5", "@babel/generator": "^7.7.4", "@babel/helpers": "^7.7.4", - "@babel/parser": "^7.7.4", + "@babel/parser": "^7.7.5", "@babel/template": "^7.7.4", "@babel/traverse": "^7.7.4", "@babel/types": "^7.7.4", @@ -214,9 +214,9 @@ } }, "@babel/helper-module-transforms": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.7.4.tgz", - "integrity": "sha512-ehGBu4mXrhs0FxAqN8tWkzF8GSIGAiEumu4ONZ/hD9M88uHcD+Yu2ttKfOCgwzoesJOJrtQh7trI5YPbRtMmnA==", + "version": "7.7.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.7.5.tgz", + "integrity": "sha512-A7pSxyJf1gN5qXVcidwLWydjftUN878VkalhXX5iQDuGyiGK3sOrrKKHF4/A4fwHtnsotv/NipwAeLzY4KQPvw==", "dev": true, "requires": { "@babel/helper-module-imports": "^7.7.4", @@ -330,9 +330,9 @@ } }, "@babel/parser": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.7.4.tgz", - "integrity": "sha512-jIwvLO0zCL+O/LmEJQjWA75MQTWwx3c3u2JOTDK5D3/9egrWRRA0/0hk9XXywYnXZVVpzrBYeIQTmhwUaePI9g==", + "version": "7.7.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.7.5.tgz", + "integrity": "sha512-KNlOe9+/nk4i29g0VXgl8PEXIRms5xKLJeuZ6UptN0fHv+jDiriG+y94X6qAgWTR0h3KaoM1wK5G5h7MHFRSig==", "dev": true }, "@babel/plugin-proposal-async-generator-functions": { @@ -599,23 +599,23 @@ } }, "@babel/plugin-transform-modules-amd": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.7.4.tgz", - "integrity": "sha512-/542/5LNA18YDtg1F+QHvvUSlxdvjZoD/aldQwkq+E3WCkbEjNSN9zdrOXaSlfg3IfGi22ijzecklF/A7kVZFQ==", + "version": "7.7.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.7.5.tgz", + "integrity": "sha512-CT57FG4A2ZUNU1v+HdvDSDrjNWBrtCmSH6YbbgN3Lrf0Di/q/lWRxZrE72p3+HCCz9UjfZOEBdphgC0nzOS6DQ==", "dev": true, "requires": { - "@babel/helper-module-transforms": "^7.7.4", + "@babel/helper-module-transforms": "^7.7.5", "@babel/helper-plugin-utils": "^7.0.0", "babel-plugin-dynamic-import-node": "^2.3.0" } }, "@babel/plugin-transform-modules-commonjs": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.7.4.tgz", - "integrity": "sha512-k8iVS7Jhc367IcNF53KCwIXtKAH7czev866ThsTgy8CwlXjnKZna2VHwChglzLleYrcHz1eQEIJlGRQxB53nqA==", + "version": "7.7.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.7.5.tgz", + "integrity": "sha512-9Cq4zTFExwFhQI6MT1aFxgqhIsMWQWDVwOgLzl7PTWJHsNaqFvklAU+Oz6AQLAS0dJKTwZSOCo20INwktxpi3Q==", "dev": true, "requires": { - "@babel/helper-module-transforms": "^7.7.4", + "@babel/helper-module-transforms": "^7.7.5", "@babel/helper-plugin-utils": "^7.0.0", "@babel/helper-simple-access": "^7.7.4", "babel-plugin-dynamic-import-node": "^2.3.0" @@ -731,9 +731,9 @@ } }, "@babel/plugin-transform-regenerator": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.7.4.tgz", - "integrity": "sha512-e7MWl5UJvmPEwFJTwkBlPmqixCtr9yAASBqff4ggXTNicZiwbF8Eefzm6NVgfiBp7JdAGItecnctKTgH44q2Jw==", + "version": "7.7.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.7.5.tgz", + "integrity": "sha512-/8I8tPvX2FkuEyWbjRCt4qTAgZK0DVy8QRguhA524UH48RfGJy94On2ri+dCuwOpcerPRl9O4ebQkRcVzIaGBw==", "dev": true, "requires": { "regenerator-transform": "^0.14.0" @@ -806,9 +806,9 @@ } }, "@babel/preset-env": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.7.4.tgz", - "integrity": "sha512-Dg+ciGJjwvC1NIe/DGblMbcGq1HOtKbw8RLl4nIjlfcILKEOkWT/vRqPpumswABEBVudii6dnVwrBtzD7ibm4g==", + "version": "7.7.6", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.7.6.tgz", + "integrity": "sha512-k5hO17iF/Q7tR9Jv8PdNBZWYW6RofxhnxKjBMc0nG4JTaWvOTiPoO/RLFwAKcA4FpmuBFm6jkoqaRJLGi0zdaQ==", "dev": true, "requires": { "@babel/helper-module-imports": "^7.7.4", @@ -839,8 +839,8 @@ "@babel/plugin-transform-function-name": "^7.7.4", "@babel/plugin-transform-literals": "^7.7.4", "@babel/plugin-transform-member-expression-literals": "^7.7.4", - "@babel/plugin-transform-modules-amd": "^7.7.4", - "@babel/plugin-transform-modules-commonjs": "^7.7.4", + "@babel/plugin-transform-modules-amd": "^7.7.5", + "@babel/plugin-transform-modules-commonjs": "^7.7.5", "@babel/plugin-transform-modules-systemjs": "^7.7.4", "@babel/plugin-transform-modules-umd": "^7.7.4", "@babel/plugin-transform-named-capturing-groups-regex": "^7.7.4", @@ -848,7 +848,7 @@ "@babel/plugin-transform-object-super": "^7.7.4", "@babel/plugin-transform-parameters": "^7.7.4", "@babel/plugin-transform-property-literals": "^7.7.4", - "@babel/plugin-transform-regenerator": "^7.7.4", + "@babel/plugin-transform-regenerator": "^7.7.5", "@babel/plugin-transform-reserved-words": "^7.7.4", "@babel/plugin-transform-shorthand-properties": "^7.7.4", "@babel/plugin-transform-spread": "^7.7.4", @@ -858,7 +858,7 @@ "@babel/plugin-transform-unicode-regex": "^7.7.4", "@babel/types": "^7.7.4", "browserslist": "^4.6.0", - "core-js-compat": "^3.1.1", + "core-js-compat": "^3.4.7", "invariant": "^2.2.2", "js-levenshtein": "^1.1.3", "semver": "^5.5.0" @@ -1383,9 +1383,9 @@ "dev": true }, "@types/node": { - "version": "12.12.14", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.14.tgz", - "integrity": "sha512-u/SJDyXwuihpwjXy7hOOghagLEV1KdAST6syfnOk6QZAMzZuWZqXy5aYYZbh8Jdpd4escVFP0MvftHNDb9pruA==", + "version": "12.12.17", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.17.tgz", + "integrity": "sha512-Is+l3mcHvs47sKy+afn2O1rV4ldZFU7W8101cNlOd+MRbjM4Onida8jSZnJdTe/0Pcf25g9BNIUsuugmE6puHA==", "dev": true }, "@types/parse-json": { @@ -1408,9 +1408,9 @@ "optional": true }, "@types/react": { - "version": "16.9.13", - "resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.13.tgz", - "integrity": "sha512-LikzRslbiufJYHyzbHSW0GrAiff8QYLMBFeZmSxzCYGXKxi8m/1PHX+rsVOwhr7mJNq+VIu2Dhf7U6mjFERK6w==", + "version": "16.9.16", + "resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.16.tgz", + "integrity": "sha512-dQ3wlehuBbYlfvRXfF5G+5TbZF3xqgkikK7DWAsQXe2KnzV+kjD4W2ea+ThCrKASZn9h98bjjPzoTYzfRqyBkw==", "dev": true, "requires": { "@types/prop-types": "*", @@ -2662,12 +2662,6 @@ "safe-buffer": "^5.1.1" } }, - "bluebird": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.1.tgz", - "integrity": "sha512-DdmyoGCleJnkbp3nkbxTLJ18rjDsE4yCggEwKNXkeV123sPNfOCYeDoeuOY+F2FrSjO1YXcTU+dsy96KMy+gcg==", - "dev": true - }, "bmp-js": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/bmp-js/-/bmp-js-0.1.0.tgz", @@ -3571,13 +3565,47 @@ "dev": true }, "core-js-compat": { - "version": "3.4.5", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.4.5.tgz", - "integrity": "sha512-rYVvzvKJDKoefdAC+q6VP63vp5hMmeVONCi9pVUbU1qRrtVrmAk/nPhnRg+i+XFd775m1hpG2Yd5RY3X45ccuw==", + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.5.0.tgz", + "integrity": "sha512-E7iJB72svRjJTnm9HDvujzNVMCm3ZcDYEedkJ/sDTNsy/0yooCd9Cg7GSzE7b4e0LfIkjijdB1tqg0pGwxWeWg==", "dev": true, "requires": { - "browserslist": "^4.7.3", + "browserslist": "^4.8.2", "semver": "^6.3.0" + }, + "dependencies": { + "browserslist": { + "version": "4.8.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.8.2.tgz", + "integrity": "sha512-+M4oeaTplPm/f1pXDw84YohEv7B1i/2Aisei8s4s6k3QsoSHa7i5sz8u/cGQkkatCPxMASKxPualR4wwYgVboA==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30001015", + "electron-to-chromium": "^1.3.322", + "node-releases": "^1.1.42" + } + }, + "caniuse-lite": { + "version": "1.0.30001015", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001015.tgz", + "integrity": "sha512-/xL2AbW/XWHNu1gnIrO8UitBGoFthcsDgU9VLK1/dpsoxbaD5LscHozKze05R6WLsBvLhqv78dAPozMFQBYLbQ==", + "dev": true + }, + "electron-to-chromium": { + "version": "1.3.322", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.322.tgz", + "integrity": "sha512-Tc8JQEfGQ1MzfSzI/bTlSr7btJv/FFO7Yh6tanqVmIWOuNCu6/D1MilIEgLtmWqIrsv+o4IjpLAhgMBr/ncNAA==", + "dev": true + }, + "node-releases": { + "version": "1.1.42", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.42.tgz", + "integrity": "sha512-OQ/ESmUqGawI2PRX+XIRao44qWYBBfN54ImQYdWVTQqUckuejOg76ysSqDBK8NG3zwySRVnX36JwDQ6x+9GxzA==", + "dev": true, + "requires": { + "semver": "^6.3.0" + } + } } }, "core-util-is": { @@ -3685,9 +3713,9 @@ "dev": true }, "css-loader": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-3.2.0.tgz", - "integrity": "sha512-QTF3Ud5H7DaZotgdcJjGMvyDj5F3Pn1j/sC6VBEOVp94cbwqyIBdcs/quzj4MC1BKQSrTpQznegH/5giYbhnCQ==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-3.3.2.tgz", + "integrity": "sha512-4XSiURS+YEK2fQhmSaM1onnUm0VKWNf6WWBYjkp9YbSDGCBTVZ5XOM6Gkxo8tLgQlzkZOBJvk9trHlDk4gjEYg==", "dev": true, "requires": { "camelcase": "^5.3.1", @@ -3695,29 +3723,67 @@ "icss-utils": "^4.1.1", "loader-utils": "^1.2.3", "normalize-path": "^3.0.0", - "postcss": "^7.0.17", + "postcss": "^7.0.23", "postcss-modules-extract-imports": "^2.0.0", "postcss-modules-local-by-default": "^3.0.2", - "postcss-modules-scope": "^2.1.0", + "postcss-modules-scope": "^2.1.1", "postcss-modules-values": "^3.0.0", - "postcss-value-parser": "^4.0.0", - "schema-utils": "^2.0.0" + "postcss-value-parser": "^4.0.2", + "schema-utils": "^2.6.0" }, "dependencies": { - "postcss-value-parser": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.0.2.tgz", - "integrity": "sha512-LmeoohTpp/K4UiyQCwuGWlONxXamGzCMtFxLq4W1nZVGIQLYvMCJx3yAF9qyyuFpflABI9yVdtJAqbihOsCsJQ==", - "dev": true - }, - "schema-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.1.0.tgz", - "integrity": "sha512-g6SViEZAfGNrToD82ZPUjq52KUPDYc+fN5+g6Euo5mLokl/9Yx14z0Cu4RR1m55HtBXejO0sBt+qw79axN+Fiw==", + "ajv": { + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz", + "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==", "dev": true, "requires": { - "ajv": "^6.1.0", - "ajv-keywords": "^3.1.0" + "fast-deep-equal": "^2.0.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.4.1.tgz", + "integrity": "sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ==", + "dev": true + }, + "postcss": { + "version": "7.0.24", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.24.tgz", + "integrity": "sha512-Xl0XvdNWg+CblAXzNvbSOUvgJXwSjmbAKORqyw9V2AlHrm1js2gFw9y3jibBAhpKZi8b5JzJCVh/FyzPsTtgTA==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "schema-utils": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.6.1.tgz", + "integrity": "sha512-0WXHDs1VDJyo+Zqs9TKLKyD/h7yDpHUhEFsM2CzkICFdoX1av+GBq/J2xRTFfsQO5kBfhZzANf2VcIm84jqDbg==", + "dev": true, + "requires": { + "ajv": "^6.10.2", + "ajv-keywords": "^3.4.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" } } } @@ -4988,8 +5054,7 @@ "fast-deep-equal": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", - "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", - "dev": true + "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=" }, "fast-glob": { "version": "2.2.7", @@ -6887,9 +6952,9 @@ } }, "immer": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/immer/-/immer-5.0.0.tgz", - "integrity": "sha512-G7gRqKbi9NE025XVyqyTV98dxUOtdKvu/P1QRaVZfA55aEcXgjbxPdm+TlWdcSMNPKijlaHNz61DGPyelouRlA==" + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/immer/-/immer-5.0.1.tgz", + "integrity": "sha512-KFHV1ivrBmPCVRhjy9oBooypnPfJ876NTrWXMNoUhXFAaWWAViVqZ4l6HxPST52qcN82qqsR38/pCGYRWP5W7w==" }, "import-cwd": { "version": "2.1.0", @@ -8263,24 +8328,6 @@ "minipass": "^3.0.0" } }, - "mississippi": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", - "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==", - "dev": true, - "requires": { - "concat-stream": "^1.5.0", - "duplexify": "^3.4.2", - "end-of-stream": "^1.1.0", - "flush-write-stream": "^1.0.0", - "from2": "^2.1.0", - "parallel-transform": "^1.1.0", - "pump": "^3.0.0", - "pumpify": "^1.3.3", - "stream-each": "^1.1.0", - "through2": "^2.0.0" - } - }, "mixin-deep": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", @@ -9491,20 +9538,12 @@ "postcss": "^7.0.16", "postcss-selector-parser": "^6.0.2", "postcss-value-parser": "^4.0.0" - }, - "dependencies": { - "postcss-value-parser": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.0.2.tgz", - "integrity": "sha512-LmeoohTpp/K4UiyQCwuGWlONxXamGzCMtFxLq4W1nZVGIQLYvMCJx3yAF9qyyuFpflABI9yVdtJAqbihOsCsJQ==", - "dev": true - } } }, "postcss-modules-scope": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-2.1.0.tgz", - "integrity": "sha512-91Rjps0JnmtUB0cujlc8KIKCsJXWjzuxGeT/+Q2i2HXKZ7nBUeF9YQTZZTNvHVoNYj1AthsjnGLtqDUE0Op79A==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-2.1.1.tgz", + "integrity": "sha512-OXRUPecnHCg8b9xWvldG/jUpRIGPNRka0r4D4j0ESUU2/5IOnpsjfPPmDprM3Ih8CgZ8FXjWqaniK5v4rWt3oQ==", "dev": true, "requires": { "postcss": "^7.0.6", @@ -10646,9 +10685,9 @@ } }, "serialize-javascript": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-2.1.0.tgz", - "integrity": "sha512-a/mxFfU00QT88umAJQsNWOnUKckhNCqOl028N48e7wFmo2/EHpTo9Wso+iJJCMrQnmFvcjto5RJdAHEvVhcyUQ==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-2.1.2.tgz", + "integrity": "sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ==", "dev": true }, "serve-index": { @@ -12151,9 +12190,9 @@ } }, "stylelint-webpack-plugin": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/stylelint-webpack-plugin/-/stylelint-webpack-plugin-1.1.1.tgz", - "integrity": "sha512-1yBV1Bhfsl5Sdl1xJUUYveA21bUAqP4nEwtCmtv9x4AsR5SxMaBnYWS31vVr4OqmYn0qKB8hn9lmQqeXv5FOJA==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/stylelint-webpack-plugin/-/stylelint-webpack-plugin-1.1.2.tgz", + "integrity": "sha512-PvxFM8z614xNZW+opA733X8NkROCkH1ZkZZ7EBWwm7J+7Rwk/bIHiAqUqlM4VueXECPsCjrzxqDZnCE+EOYZxQ==", "dev": true, "requires": { "arrify": "^2.0.1", @@ -12312,6 +12351,14 @@ } } }, + "swr": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/swr/-/swr-0.1.13.tgz", + "integrity": "sha512-5MjXCs1hWwkyPBCQ84Hcq0vlijj19I1R64Ma0q/xeF1MN0AZB+Kj5HUrIyVSDDUubvtq0KuaewVlhcytEHiqGw==", + "requires": { + "fast-deep-equal": "2.0.1" + } + }, "table": { "version": "5.4.6", "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", @@ -12417,9 +12464,9 @@ } }, "terser": { - "version": "4.3.8", - "resolved": "https://registry.npmjs.org/terser/-/terser-4.3.8.tgz", - "integrity": "sha512-otmIRlRVmLChAWsnSFNO0Bfk6YySuBp6G9qrHiJwlLDd4mxe2ta4sjI7TzIR+W1nBMjilzrMcPOz9pSusgx3hQ==", + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/terser/-/terser-4.4.2.tgz", + "integrity": "sha512-Uufrsvhj9O1ikwgITGsZ5EZS6qPokUOkCegS7fYOdGTv+OA90vndUbU6PEjr5ePqHfNUbGyMO7xyIZv2MhsALQ==", "dev": true, "requires": { "commander": "^2.20.0", @@ -12436,18 +12483,18 @@ } }, "terser-webpack-plugin": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-2.2.1.tgz", - "integrity": "sha512-jwdauV5Al7zopR6OAYvIIRcxXCSvLjZjr7uZE8l2tIWb/ryrGN48sJftqGf5k9z09tWhajx53ldp0XPI080YnA==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-2.3.0.tgz", + "integrity": "sha512-yez0HdpDf/iQVYGf+e/o8ZYWLb1g9d1nRRi5FIOZ4KfXbfSPT259UoqxPiSLhCnr0mlDoh+bucpYQSFbU0cEsQ==", "dev": true, "requires": { "cacache": "^13.0.1", - "find-cache-dir": "^3.0.0", + "find-cache-dir": "^3.1.0", "jest-worker": "^24.9.0", - "schema-utils": "^2.5.0", - "serialize-javascript": "^2.1.0", + "schema-utils": "^2.6.1", + "serialize-javascript": "^2.1.2", "source-map": "^0.6.1", - "terser": "^4.3.9", + "terser": "^4.4.2", "webpack-sources": "^1.4.3" }, "dependencies": { @@ -12470,9 +12517,9 @@ "dev": true }, "find-cache-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.0.0.tgz", - "integrity": "sha512-t7ulV1fmbxh5G9l/492O1p5+EBbr3uwpt6odhFTMc+nWyhmbloe+ja9BZ8pIBtqFWhOmCWVjx+pTW4zDkFoclw==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.2.0.tgz", + "integrity": "sha512-1JKclkYYsf1q9WIJKLZa9S9muC+08RIjzAlLrK4QcYLJMS6mk9yombQ9qf+zJ7H9LS800k0s44L4sDq9VYzqyg==", "dev": true, "requires": { "commondir": "^1.0.1", @@ -12533,9 +12580,9 @@ } }, "schema-utils": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.5.0.tgz", - "integrity": "sha512-32ISrwW2scPXHUSusP8qMg5dLUawKkyV+/qIEV9JdXKx+rsM6mi8vZY8khg2M69Qom16rtroWXD3Ybtiws38gQ==", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.6.1.tgz", + "integrity": "sha512-0WXHDs1VDJyo+Zqs9TKLKyD/h7yDpHUhEFsM2CzkICFdoX1av+GBq/J2xRTFfsQO5kBfhZzANf2VcIm84jqDbg==", "dev": true, "requires": { "ajv": "^6.10.2", @@ -12547,17 +12594,6 @@ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true - }, - "terser": { - "version": "4.3.9", - "resolved": "https://registry.npmjs.org/terser/-/terser-4.3.9.tgz", - "integrity": "sha512-NFGMpHjlzmyOtPL+fDw3G7+6Ueh/sz4mkaUYa4lJCxOPTNzd0Uj0aZJOmsDYoSQyfuVoWDMSWTPU3huyOm2zdA==", - "dev": true, - "requires": { - "commander": "^2.20.0", - "source-map": "~0.6.1", - "source-map-support": "~0.5.12" - } } } }, @@ -12891,9 +12927,9 @@ } }, "typescript": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.7.2.tgz", - "integrity": "sha512-ml7V7JfiN2Xwvcer+XAf2csGO1bPBdRbFCkYBczNZggrBZ9c7G3riSUeJmqEU5uOtXNPMhE3n+R4FA/3YOAWOQ==", + "version": "3.7.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.7.3.tgz", + "integrity": "sha512-Mcr/Qk7hXqFBXMN7p7Lusj1ktCBydylfQM/FZCk5glCNQJrCUKPkMHdo9R0MTFWsC/4kPFvDS0fDPvukfCkFsw==", "dev": true }, "uglify-js": { @@ -13447,6 +13483,32 @@ "ssri": "^6.0.1", "unique-filename": "^1.1.1", "y18n": "^4.0.0" + }, + "dependencies": { + "bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "dev": true + }, + "mississippi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", + "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==", + "dev": true, + "requires": { + "concat-stream": "^1.5.0", + "duplexify": "^3.4.2", + "end-of-stream": "^1.1.0", + "flush-write-stream": "^1.0.0", + "from2": "^2.1.0", + "parallel-transform": "^1.1.0", + "pump": "^3.0.0", + "pumpify": "^1.3.3", + "stream-each": "^1.1.0", + "through2": "^2.0.0" + } + } } }, "lru-cache": { @@ -13458,12 +13520,6 @@ "yallist": "^3.0.2" } }, - "serialize-javascript": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.9.1.tgz", - "integrity": "sha512-0Vb/54WJ6k5v8sSWN09S0ora+Hnr+cX40r9F170nT+mSkaxltoE/7R3OrIdBSUv1OoiobH1QoWQbCnAO+e8J1A==", - "dev": true - }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -13480,20 +13536,42 @@ } }, "terser-webpack-plugin": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.1.tgz", - "integrity": "sha512-ZXmmfiwtCLfz8WKZyYUuuHf3dMYEjg8NrjHMb0JqHVHVOSkzp3cW2/XG1fP3tRhqEqSzMwzzRQGtAPbs4Cncxg==", + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.3.tgz", + "integrity": "sha512-QMxecFz/gHQwteWwSo5nTc6UaICqN1bMedC5sMtUc7y3Ha3Q8y6ZO0iCR8pq4RJC8Hjf0FEPEHZqcMB/+DFCrA==", "dev": true, "requires": { "cacache": "^12.0.2", "find-cache-dir": "^2.1.0", "is-wsl": "^1.1.0", "schema-utils": "^1.0.0", - "serialize-javascript": "^1.7.0", + "serialize-javascript": "^2.1.2", "source-map": "^0.6.1", "terser": "^4.1.2", "webpack-sources": "^1.4.0", "worker-farm": "^1.7.0" + }, + "dependencies": { + "terser": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/terser/-/terser-4.4.2.tgz", + "integrity": "sha512-Uufrsvhj9O1ikwgITGsZ5EZS6qPokUOkCegS7fYOdGTv+OA90vndUbU6PEjr5ePqHfNUbGyMO7xyIZv2MhsALQ==", + "dev": true, + "requires": { + "commander": "^2.20.0", + "source-map": "~0.6.1", + "source-map-support": "~0.5.12" + } + }, + "worker-farm": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz", + "integrity": "sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==", + "dev": true, + "requires": { + "errno": "~0.1.7" + } + } } }, "yallist": { @@ -13880,15 +13958,6 @@ "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", "dev": true }, - "worker-farm": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz", - "integrity": "sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==", - "dev": true, - "requires": { - "errno": "~0.1.7" - } - }, "wrap-ansi": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", diff --git a/package.json b/package.json index 49a2d3e..3173336 100644 --- a/package.json +++ b/package.json @@ -28,15 +28,15 @@ "contributors:generate": "all-contributors generate" }, "devDependencies": { - "@babel/cli": "^7.7.4", - "@babel/core": "^7.7.4", - "@babel/preset-env": "^7.7.4", + "@babel/cli": "^7.7.5", + "@babel/core": "^7.7.5", + "@babel/preset-env": "^7.7.6", "@babel/preset-react": "^7.7.4", "@hot-loader/react-dom": "^16.11.0", "@types/classnames": "^2.2.8", "@types/lodash-es": "^4.17.3", - "@types/node": "^12.12.14", - "@types/react": "^16.9.13", + "@types/node": "^12.12.17", + "@types/react": "^16.9.16", "@types/react-dom": "^16.9.4", "@types/react-router-dom": "^5.1.3", "@types/react-virtualized-auto-sizer": "^1.0.0", @@ -46,7 +46,7 @@ "awesome-typescript-loader": "^5.2.1", "babel-loader": "^8.0.6", "babel-preset-minify": "^0.5.1", - "css-loader": "^3.2.0", + "css-loader": "^3.3.2", "file-loader": "^5.0.2", "html-webpack-plugin": "^3.2.0", "image-webpack-loader": "^6.0.0", @@ -59,12 +59,12 @@ "style-loader": "^1.0.1", "stylelint": "^12.0.0", "stylelint-config-standard": "^19.0.0", - "stylelint-webpack-plugin": "^1.1.1", - "terser-webpack-plugin": "^2.2.1", + "stylelint-webpack-plugin": "^1.1.2", + "terser-webpack-plugin": "^2.3.0", "tslint": "^5.20.1", "tslint-config-standard": "^9.0.0", "tslint-loader": "^3.6.0", - "typescript": "^3.7.2", + "typescript": "^3.7.3", "webpack": "^4.41.2", "webpack-cli": "^3.3.10", "webpack-dev-middleware": "^3.7.2", @@ -77,7 +77,7 @@ "classnames": "^2.2.6", "dayjs": "^1.8.17", "eventemitter3": "^4.0.0", - "immer": "^5.0.0", + "immer": "^5.0.1", "lodash-es": "^4.17.15", "react": "^16.12.0", "react-dom": "^16.12.0", @@ -86,6 +86,7 @@ "react-virtualized-auto-sizer": "^1.0.2", "react-window": "^1.8.5", "semver": "^6.3.0", + "swr": "^0.1.13", "timeago.js": "^4.0.1", "unstated-next": "^1.1.0", "use-immer": "^0.3.5" diff --git a/src/components/Card/index.tsx b/src/components/Card/index.tsx index ab0ee74..04ba297 100644 --- a/src/components/Card/index.tsx +++ b/src/components/Card/index.tsx @@ -5,7 +5,7 @@ import './style.scss' interface CardProps extends BaseComponentProps {} -export const Card: React.SFC = props => { +export function Card (props: CardProps) { const { className, style, children } = props return (
diff --git a/src/components/Col/index.tsx b/src/components/Col/index.tsx index f4032c9..cc80531 100644 --- a/src/components/Col/index.tsx +++ b/src/components/Col/index.tsx @@ -12,7 +12,7 @@ interface ColProps extends BaseComponentProps { span?: number } -export const Col: React.SFC = props => { +export function Col (props: ColProps) { const { offset = 0, order = 0, diff --git a/src/components/Header/index.tsx b/src/components/Header/index.tsx index 7779959..1e96e0d 100644 --- a/src/components/Header/index.tsx +++ b/src/components/Header/index.tsx @@ -8,7 +8,7 @@ interface HeaderProps extends BaseComponentProps { title: string } -export const Header: React.SFC = props => { +export function Header (props: HeaderProps) { const { title, children, className, style } = props return
diff --git a/src/components/Icon/index.tsx b/src/components/Icon/index.tsx index 1005c8b..e531663 100644 --- a/src/components/Icon/index.tsx +++ b/src/components/Icon/index.tsx @@ -12,7 +12,7 @@ interface IconProps extends BaseComponentProps { onClick?: React.FormEventHandler } -export const Icon: React.SFC = props => { +export function Icon (props: IconProps) { const { type, size = 14, className: cn, style: s } = props const className = classnames('clash-iconfont', `icon-${type}`, cn) const style: React.CSSProperties = { fontSize: size, ...s } diff --git a/src/components/Loading/Spinner/index.tsx b/src/components/Loading/Spinner/index.tsx new file mode 100644 index 0000000..5884262 --- /dev/null +++ b/src/components/Loading/Spinner/index.tsx @@ -0,0 +1,31 @@ +import * as React from 'react' +import classnames from 'classnames' +import { BaseComponentProps } from '@models/BaseProps' + +import './style.scss' + +interface SpinnerProps extends BaseComponentProps {} + +export function Spinner (props: SpinnerProps) { + const classname = classnames('spinner', props.className) + + return ( +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ) +} diff --git a/src/components/Loading/Spinner/style.scss b/src/components/Loading/Spinner/style.scss new file mode 100644 index 0000000..411264c --- /dev/null +++ b/src/components/Loading/Spinner/style.scss @@ -0,0 +1,61 @@ +@import '~@styles/variables'; + +.spinner { + position: relative; + width: 80px; + height: 80px; + border-radius: 100%; + animation: spinner 5s infinite linear; +} + +.spinner-circle { + position: absolute; + width: 100%; + height: 100%; + transform-origin: 48% 48%; +} + +.spinner-inner { + width: 100%; + height: 100%; + border-radius: 100%; + border: 5px solid transparentize($color-primary-dark, 0.3); + border-right: none; + border-top: none; + background-clip: padding-box; + box-shadow: inset 0 0 10px transparentize($color-primary-dark, 0.85); +} + +@keyframes spinner { + from { + transform: rotate(0deg); + } + + to { + transform: rotate(360deg); + } +} + +.spinner-circle:nth-of-type(0) { + transform: rotate(0deg); +} + +.spinner-circle:nth-of-type(0) .spinner-inner { + animation: spinner 2s infinite linear; +} + +.spinner-circle:nth-of-type(1) { + transform: rotate(70deg); +} + +.spinner-circle:nth-of-type(1) .spinner-inner { + animation: spinner 2s infinite linear; +} + +.spinner-circle:nth-of-type(2) { + transform: rotate(140deg); +} + +.spinner-circle:nth-of-type(2) .spinner-inner { + animation: spinner 2s infinite linear; +} diff --git a/src/components/Loading/index.tsx b/src/components/Loading/index.tsx new file mode 100644 index 0000000..29c5615 --- /dev/null +++ b/src/components/Loading/index.tsx @@ -0,0 +1,35 @@ +import React, { useState } from 'react' +import classnames from 'classnames' +import { BaseComponentProps } from '@models/BaseProps' +import { Spinner } from './Spinner' + +import './style.scss' + +interface LoadingProps extends BaseComponentProps { + visible: boolean +} + +export function Loading (props: LoadingProps) { + const classname = classnames('loading', 'visible') + return props.visible + ? ( +
+ +
+ ) + : null +} + +export function useLoading (initial: boolean) { + const [visible, setVisible] = useState(initial) + + function hide () { + setVisible(false) + } + + function show () { + setVisible(true) + } + + return { visible, hide, show } +} diff --git a/src/components/Loading/style.scss b/src/components/Loading/style.scss new file mode 100644 index 0000000..64aba94 --- /dev/null +++ b/src/components/Loading/style.scss @@ -0,0 +1,13 @@ +.loading { + position: absolute; + display: flex; + align-items: center; + justify-content: center; + top: 0; + left: 0; + bottom: 0; + right: 0; + background-color: rgba($color: #fff, $alpha: 0.9); + box-shadow: inset 0 0 80px rgba(0, 0, 0, 0.1); + z-index: 1000; +} diff --git a/src/components/Row/index.tsx b/src/components/Row/index.tsx index 21bf802..389b3f2 100644 --- a/src/components/Row/index.tsx +++ b/src/components/Row/index.tsx @@ -14,7 +14,7 @@ interface RowProps extends BaseComponentProps { justify?: 'start' | 'end' | 'center' | 'space-around' | 'space-between' } -export const Row: React.SFC = props => { +export function Row (props: RowProps) { const { gutter = 24, align = 'top', diff --git a/src/components/Select/index.tsx b/src/components/Select/index.tsx index f3a8f6f..84df54f 100644 --- a/src/components/Select/index.tsx +++ b/src/components/Select/index.tsx @@ -139,7 +139,7 @@ interface OptionProps extends BaseComponentProps { onClick?: (e: React.MouseEvent) => void } -export const Option: React.SFC = props => { +export function Option (props: OptionProps) { const { className: cn, style, key, disabled = false, children, onClick = () => {} } = props const className = classnames('option', { disabled }, cn) diff --git a/src/components/Tag/index.tsx b/src/components/Tag/index.tsx new file mode 100644 index 0000000..752181c --- /dev/null +++ b/src/components/Tag/index.tsx @@ -0,0 +1,18 @@ +import * as React from 'react' +import classnames from 'classnames' +import { BaseComponentProps } from '@models/BaseProps' + +import './style.scss' + +interface TagProps extends BaseComponentProps { + color?: string +} + +export function Tag (props: TagProps) { + const { color, className: cn, style: s } = props + const className = classnames('tag', cn) + const style: React.CSSProperties = { color, ...s } + const spanProps = { ...props, className, style } + + return { props.children } +} diff --git a/src/components/Tag/style.scss b/src/components/Tag/style.scss new file mode 100644 index 0000000..b2f32d7 --- /dev/null +++ b/src/components/Tag/style.scss @@ -0,0 +1,14 @@ +@import '~@styles/variables'; + +.tag { + display: flex; + align-items: center; + height: 24px; + font-size: 12px; + padding: 0 12px; + text-align: center; + background-color: #fff; + border: 2px solid $color-primary-dark; + color: $color-primary-dark; + border-radius: 12px; +} diff --git a/src/components/index.ts b/src/components/index.ts index 14867af..ef74c4c 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -13,3 +13,5 @@ export * from './Alert' export * from './Button' export * from './Message' export * from './Checkbox' +export * from './Tag' +export * from './Loading' diff --git a/src/containers/Proxies/components/Group/index.tsx b/src/containers/Proxies/components/Group/index.tsx index f2e01c0..2fa7fa0 100644 --- a/src/containers/Proxies/components/Group/index.tsx +++ b/src/containers/Proxies/components/Group/index.tsx @@ -1,7 +1,7 @@ import * as React from 'react' import { containers } from '@stores' import { changeProxySelected, Group as IGroup } from '@lib/request' -import { Tags } from '@components' +import { Tags, Tag } from '@components' import './style.scss' interface GroupProps { @@ -22,7 +22,7 @@ export function Group (props: GroupProps) {
{ config.name } - { config.type } + { config.type }
fetch()).finally(() => hide()) + } + + function handleUpdate () { + show() + updateProvider(provider.name).then(() => fetch()).finally(() => hide()) + } + + return ( + + +
+
+ { provider.name } + { provider.vehicleType } +
+
+ { + provider.updatedAt && + { `${t('providerUpdateTime')}: ${format(new Date(provider.updatedAt), lang)}`} + } + + +
+
+
    + { + provider.proxies.map((p: IProxy) => ( +
  • + +
  • + )) + } +
+
+ ) +} diff --git a/src/containers/Proxies/components/Provider/style.scss b/src/containers/Proxies/components/Provider/style.scss new file mode 100644 index 0000000..370fc0b --- /dev/null +++ b/src/containers/Proxies/components/Provider/style.scss @@ -0,0 +1,62 @@ +@import '~@styles/variables'; + +.proxy-provider { + position: relative; + display: flex; + flex-direction: column; + font-size: 16px; + padding: 20px; + color: $color-black-light; +} + +.proxy-provider-header { + display: flex; + align-items: center; + justify-content: space-between; +} + +.proxy-provider-header-part { + display: flex; + align-items: center; +} + +.proxy-provider-name { + margin-right: 24px; +} + +.proxy-provider-proxies { + list-style: none; +} + +.proxy-provider-item { + box-shadow: 0 0 24px 0 rgba(44, 138, 248, 0.2); + + &:hover { + box-shadow: 0 0 24px 0 rgba(84, 117, 154, 0.4); + } +} + +.proxy-provider-update { + line-height: 14px; + font-size: 14px; +} + +.proxy-provider-icon { + margin-left: 20px; + cursor: pointer; + + &.healthcheck { + color: $color-red; + } +} + +@media (max-width: 768px) { + .proxy-provider-header { + flex-direction: column; + align-items: flex-start; + } + + .proxy-provider-header-part { + margin: 6px 0; + } +} diff --git a/src/containers/Proxies/components/index.ts b/src/containers/Proxies/components/index.ts index 80408ae..2ac2796 100644 --- a/src/containers/Proxies/components/index.ts +++ b/src/containers/Proxies/components/index.ts @@ -1,2 +1,3 @@ export * from './Proxy' export * from './Group' +export * from './Provider' diff --git a/src/containers/Proxies/index.tsx b/src/containers/Proxies/index.tsx index 1072e14..d836c34 100644 --- a/src/containers/Proxies/index.tsx +++ b/src/containers/Proxies/index.tsx @@ -1,10 +1,12 @@ -import React, { useLayoutEffect, useState, useMemo } from 'react' +import React, { useMemo } from 'react' +import useSWR from 'swr' import EE from '@lib/event' +import { useRound } from '@lib/hook' import { Card, Header, Icon } from '@components' import { containers } from '@stores' import * as API from '@lib/request' -import { Proxy, Group } from './components' +import { Proxy, Group, Provider } from './components' import './style.scss' enum sortType { @@ -29,15 +31,15 @@ export default function Proxies () { const { data, fetch } = containers.useData() const { useTranslation } = containers.useI18n() const { t } = useTranslation('Proxies') + useSWR('data', fetch) - useLayoutEffect(() => { - fetch() - }, []) function handleNotitySpeedTest () { EE.notifySpeedTest() } - const [sort, setSort] = useState(sortType.None) + const { current: sort, next } = useRound( + [sortType.None, sortType.Asc, sortType.Desc] + ) const proxies = useMemo(() => { switch (sort) { case sortType.Desc: @@ -48,42 +50,61 @@ export default function Proxies () { return data.proxy.slice() } }, [sort, data]) - function handleSort () { - setSort((sort + 1) % 3) - } + const handleSort = next return (
-
-
- -
    + { + data.proxyGroup.length !== 0 && +
    +
    + +
      + { + data.proxyGroup.map(p => ( +
    • + +
    • + )) + } +
    +
    +
    + } + { + data.proxyProviders.length !== 0 && +
    +
    +
      { - data.proxyGroup.map(p => ( -
    • - + data.proxyProviders.map(p => ( +
    • +
    • )) }
    - -
    -
    -
    - - - {t('speedTestText')} -
    -
      - { - proxies.map(p => ( -
    • - -
    • - )) - } -
    -
    +
+ } + { + proxies.length !== 0 && +
+
+ + + {t('speedTestText')} +
+
    + { + proxies.map(p => ( +
  • + +
  • + )) + } +
+
+ }
) } diff --git a/src/containers/Proxies/style.scss b/src/containers/Proxies/style.scss index 97ccd7b..8257bc2 100644 --- a/src/containers/Proxies/style.scss +++ b/src/containers/Proxies/style.scss @@ -7,7 +7,6 @@ display: flex; margin-right: calc(-1 * var(--gap)); margin-top: 20px; - padding-bottom: 100px; flex-wrap: wrap; align-content: flex-start; list-style: none; @@ -69,6 +68,14 @@ cursor: pointer; } +.proxies-providers-item { + margin: 20px 0; +} + +.proxies-providers-list { + list-style: none; +} + @media (max-width: 768px) { .proxies-group-card { margin: 12px 0; diff --git a/src/i18n/en_US.ts b/src/i18n/en_US.ts index c63bde3..de0de67 100644 --- a/src/i18n/en_US.ts +++ b/src/i18n/en_US.ts @@ -84,6 +84,8 @@ export default { tls: 'TLS' }, groupTitle: 'Policy Group', + providerTitle: 'Providers', + providerUpdateTime: 'Last updated at', expandText: 'Expand', collapseText: 'Collapse', speedTestText: 'Speed Test' diff --git a/src/i18n/zh_CN.ts b/src/i18n/zh_CN.ts index 4fe9a21..ccbaa51 100644 --- a/src/i18n/zh_CN.ts +++ b/src/i18n/zh_CN.ts @@ -84,6 +84,8 @@ export default { tls: 'TLS' }, groupTitle: '策略组', + providerTitle: '代理集', + providerUpdateTime: '最后更新于', expandText: '展开', collapseText: '收起', speedTestText: '测速' diff --git a/src/lib/hook.ts b/src/lib/hook.ts index c13f207..070c95f 100644 --- a/src/lib/hook.ts +++ b/src/lib/hook.ts @@ -1,7 +1,7 @@ import { Draft } from 'immer' import { useImmer } from 'use-immer' import { createContainer } from 'unstated-next' -import { useRef, useEffect } from 'react' +import { useRef, useEffect, useState, useMemo } from 'react' import { noop } from '@lib/helper' @@ -71,3 +71,19 @@ export function composeContainer, U extends { [key: }, {} as { [K in keyof U]: U[K] }) } } + +export function useRound (list: T[], defidx = 0) { + if (list.length < 2) { + throw new Error('List requires at least two elements') + } + + const [state, setState] = useState(defidx) + + function next () { + setState((state + 1) % list.length) + } + + const current = useMemo(() => list[state], [state]) + + return { current, next } +} diff --git a/src/lib/request.ts b/src/lib/request.ts index 5582389..bceb20b 100644 --- a/src/lib/request.ts +++ b/src/lib/request.ts @@ -31,6 +31,20 @@ export interface Proxies { } } +export interface Provider { + name: string + proxies: Array + type: 'Proxy' | 'Rule' + vehicleType: 'HTTP' | 'File' | 'Compatible' + updatedAt?: string +} + +export interface ProxyProviders { + providers: { + [key: string]: Provider + } +} + interface History { time: string delay: number @@ -107,6 +121,33 @@ export async function updateRules () { return req.put('rules') } +export async function getProxyProviders () { + const req = await getInstance() + return req.get('providers/proxies', { + validateStatus (status) { + // compatible old version + return (status >= 200 && status < 300) || status === 404 + } + }) + // compatible old version + .then(resp => { + if (resp.status === 404) { + resp.data = { providers: {} } + } + return resp + }) +} + +export async function updateProvider (name: string) { + const req = await getInstance() + return req.put(`providers/proxies/${name}`) +} + +export async function healthCheckProvider (name: string) { + const req = await getInstance() + return req.get(`providers/proxies/${name}/healthcheck`) +} + export async function getProxies () { const req = await getInstance() return req.get('proxies') diff --git a/src/models/Config.ts b/src/models/Config.ts index a1d3630..bb7e40a 100644 --- a/src/models/Config.ts +++ b/src/models/Config.ts @@ -115,5 +115,7 @@ export interface Data { proxyGroup?: API.Group[] + proxyProviders?: API.Provider[] + rules?: API.Rule[] } diff --git a/src/stores/HookStore.ts b/src/stores/HookStore.ts index 675e640..ad7e31d 100644 --- a/src/stores/HookStore.ts +++ b/src/stores/HookStore.ts @@ -12,6 +12,7 @@ function useData () { general: {}, proxy: [], proxyGroup: [], + proxyProviders: [], rules: [] }) @@ -26,12 +27,12 @@ function useData () { } async function fetch () { - const [resp, err] = await to(Promise.all([API.getConfig(), API.getProxies(), API.getRules()])) + const [resp, err] = await to(Promise.all([API.getConfig(), API.getProxies(), API.getRules(), API.getProxyProviders()])) if (err && (!err.response || err.response.status === 401)) { show() } - const [{ data: general }, rawProxies, rules] = resp + const [{ data: general }, rawProxies, rules, proxyProviders] = resp set('general', { port: general.port, @@ -52,9 +53,15 @@ function useData () { .map(key => ({ ...rawProxies.data.proxies[key], name: key })) const [proxy, groups] = partition(proxies, proxy => !policyGroup.has(proxy.type)) + const providers = Object.keys(proxyProviders.data.providers) + .map(name => proxyProviders.data.providers[name]) + .filter(pd => pd.name !== 'default') + .filter(pd => pd.vehicleType !== 'Compatible') + set({ proxy: proxy as API.Proxy[], proxyGroup: general.mode === 'Global' ? [proxyList] : groups as API.Group[], + proxyProviders: providers, rules: rules.data.rules }) diff --git a/src/styles/common.scss b/src/styles/common.scss index a232614..7b699d5 100644 --- a/src/styles/common.scss +++ b/src/styles/common.scss @@ -56,9 +56,9 @@ body { } .page { - padding: 20px 35px 30px 0; + padding: 20px 35px 30px 20px; width: 100%; - height: 100vh; + min-height: 100vh; margin: 0 auto; display: flex; flex-direction: column; diff --git a/src/styles/iconfont.scss b/src/styles/iconfont.scss index e2c4138..4a42cad 100644 --- a/src/styles/iconfont.scss +++ b/src/styles/iconfont.scss @@ -6,7 +6,7 @@ @font-face { font-family: "clash-iconfont"; - src: url('//at.alicdn.com/t/font_841708_w4jpsvny2x.ttf?t=1572182107550') format('truetype'); + src: url('//at.alicdn.com/t/font_841708_ok9czskbhel.ttf?t=1576162884356') format('truetype'); } .clash-iconfont { @@ -18,6 +18,10 @@ color: $color-primary-dark; } +.icon-update::before { content: "\e66f"; } + +.icon-healthcheck::before { content: "\e63c"; } + .icon-speed::before { content: "\e61b"; } .icon-close::before { content: "\e602"; }