feat: multi backends management
This commit is contained in:
parent
a8f0d3d4b4
commit
15bc0f69a8
15 changed files with 517 additions and 218 deletions
|
@ -27,6 +27,7 @@ rules:
|
|||
'@typescript-eslint/explicit-function-return-type': 'off'
|
||||
'@typescript-eslint/no-explicit-any': 'off'
|
||||
'@typescript-eslint/camelcase': 'off'
|
||||
'no-use-before-define': 'off'
|
||||
'@typescript-eslint/no-unused-vars':
|
||||
- 'error'
|
||||
- argsIgnorePattern: '^_'
|
||||
|
|
24
package.json
24
package.json
|
@ -34,14 +34,14 @@
|
|||
"dependencies": {
|
||||
"@babel/runtime": "^7.11.2",
|
||||
"@hsjs/react-cache": "0.0.0-alpha.aa94237",
|
||||
"@sentry/browser": "^5.22.3",
|
||||
"@sentry/browser": "^5.23.0",
|
||||
"chart.js": "^2.9.2",
|
||||
"clsx": "^1.1.0",
|
||||
"core-js": "^3.6.2",
|
||||
"date-fns": "^2.16.0",
|
||||
"framer-motion": "^2.6.7",
|
||||
"framer-motion": "^2.6.13",
|
||||
"history": "^5.0.0",
|
||||
"immer": "^7.0.8",
|
||||
"immer": "^7.0.9",
|
||||
"invariant": "^2.2.4",
|
||||
"lodash-es": "^4.17.14",
|
||||
"memoize-one": "^5.1.1",
|
||||
|
@ -52,7 +52,7 @@
|
|||
"react-feather": "^2.0.3",
|
||||
"react-icons": "^3.10.0",
|
||||
"react-modal": "^3.11.1",
|
||||
"react-query": "^2.15.4",
|
||||
"react-query": "^2.17.0",
|
||||
"react-router": "6.0.0-beta.0",
|
||||
"react-router-dom": "6.0.0-beta.0",
|
||||
"react-switch": "^5.0.1",
|
||||
|
@ -92,37 +92,37 @@
|
|||
"copy-webpack-plugin": "^6.0.4",
|
||||
"css-loader": "^4.3.0",
|
||||
"cssnano": "^4.1.7",
|
||||
"eslint": "^7.6.0",
|
||||
"eslint": "^7.9.0",
|
||||
"eslint-config-airbnb-base": "^14.1.0",
|
||||
"eslint-config-prettier": "^6.11.0",
|
||||
"eslint-config-react-app": "^5.2.1",
|
||||
"eslint-import-resolver-webpack": "^0.12.2",
|
||||
"eslint-plugin-flowtype": "^5.1.0",
|
||||
"eslint-plugin-flowtype": "^5.2.0",
|
||||
"eslint-plugin-import": "^2.22.0",
|
||||
"eslint-plugin-jest": "^24.0.0",
|
||||
"eslint-plugin-jest": "^24.0.1",
|
||||
"eslint-plugin-jsx-a11y": "^6.3.1",
|
||||
"eslint-plugin-react": "^7.20.6",
|
||||
"eslint-plugin-react-hooks": "^4.0.8",
|
||||
"eslint-plugin-react-hooks": "^4.1.2",
|
||||
"eslint-plugin-simple-import-sort": "^5.0.3",
|
||||
"file-loader": "^6.0.0",
|
||||
"fork-ts-checker-notifier-webpack-plugin": "^3.0.0",
|
||||
"fork-ts-checker-webpack-plugin": "^5.0.14",
|
||||
"fork-ts-checker-webpack-plugin": "^5.2.0",
|
||||
"html-webpack-plugin": "^4.3.0",
|
||||
"husky": "^4.3.0",
|
||||
"lint-staged": "^10.2.13",
|
||||
"mini-css-extract-plugin": "^0.11.0",
|
||||
"mini-css-extract-plugin": "^0.11.2",
|
||||
"postcss": "^7.0.32",
|
||||
"postcss-custom-media": "^7.0.8",
|
||||
"postcss-extend-rule": "^3.0.0",
|
||||
"postcss-import": "^12.0.1",
|
||||
"postcss-loader": "^4.0.0",
|
||||
"postcss-loader": "^4.0.1",
|
||||
"postcss-nested": "^4.2.3",
|
||||
"postcss-simple-vars": "^5.0.2",
|
||||
"prettier": "^2.1.1",
|
||||
"react-refresh": "^0.8.2",
|
||||
"resize-observer-polyfill": "^1.5.1",
|
||||
"style-loader": "^1.2.1",
|
||||
"terser-webpack-plugin": "^4.1.0",
|
||||
"terser-webpack-plugin": "^4.2.0",
|
||||
"ts-loader": "^8.0.3",
|
||||
"typescript": "^4.0.2",
|
||||
"webpack": "^4.44.1",
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
}
|
||||
|
||||
.body {
|
||||
padding: 30px 0 0;
|
||||
padding: 15px 0 0;
|
||||
}
|
||||
|
||||
.hostnamePort {
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import * as React from 'react';
|
||||
import { fetchConfigs } from 'src/api/configs';
|
||||
import { BackendList } from 'src/components/BackendList';
|
||||
import { ClashAPIConfig } from 'src/types';
|
||||
|
||||
import { getClashAPIConfig, updateClashAPIConfig } from '../store/app';
|
||||
import { addClashAPIConfig, getClashAPIConfig } from '../store/app';
|
||||
import s0 from './APIConfig.module.css';
|
||||
import Button from './Button';
|
||||
import Field from './Field';
|
||||
|
@ -16,9 +17,9 @@ const mapState = (s) => ({
|
|||
apiConfig: getClashAPIConfig(s),
|
||||
});
|
||||
|
||||
function APIConfig({ apiConfig, dispatch }) {
|
||||
const [baseURL, setBaseURL] = useState(apiConfig.baseURL);
|
||||
const [secret, setSecret] = useState(apiConfig.secret);
|
||||
function APIConfig({ dispatch }) {
|
||||
const [baseURL, setBaseURL] = useState('');
|
||||
const [secret, setSecret] = useState('');
|
||||
const [errMsg, setErrMsg] = useState('');
|
||||
|
||||
const userTouchedFlagRef = useRef(false);
|
||||
|
@ -47,14 +48,21 @@ function APIConfig({ apiConfig, dispatch }) {
|
|||
if (ret[0] !== Ok) {
|
||||
setErrMsg(ret[1]);
|
||||
} else {
|
||||
dispatch(updateClashAPIConfig({ baseURL, secret }));
|
||||
dispatch(addClashAPIConfig({ baseURL, secret }));
|
||||
}
|
||||
});
|
||||
}, [baseURL, secret, dispatch]);
|
||||
|
||||
const handleContentOnKeyDown = useCallback(
|
||||
(e) => {
|
||||
(e: React.KeyboardEvent<HTMLInputElement>) => {
|
||||
if (
|
||||
e.target instanceof Element &&
|
||||
(!e.target.tagName || e.target.tagName.toUpperCase() !== 'INPUT')
|
||||
) {
|
||||
return;
|
||||
}
|
||||
if (e.key !== 'Enter') return;
|
||||
|
||||
onConfirm();
|
||||
},
|
||||
[onConfirm]
|
||||
|
@ -90,8 +98,10 @@ function APIConfig({ apiConfig, dispatch }) {
|
|||
</div>
|
||||
<div className={s0.error}>{errMsg ? errMsg : null}</div>
|
||||
<div className={s0.footer}>
|
||||
<Button label="Confirm" onClick={onConfirm} />
|
||||
<Button label="Add" onClick={onConfirm} />
|
||||
</div>
|
||||
<div style={{ height: 20 }} />
|
||||
<BackendList />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -12,11 +12,11 @@
|
|||
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.container {
|
||||
position: relative;
|
||||
top: 10%;
|
||||
margin-left: 20px;
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
|
99
src/components/BackendList.module.css
Normal file
99
src/components/BackendList.module.css
Normal file
|
@ -0,0 +1,99 @@
|
|||
.ul {
|
||||
position: relative;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
line-height: 1.8;
|
||||
|
||||
--width-max-content: 230px;
|
||||
}
|
||||
|
||||
.li {
|
||||
position: relative;
|
||||
margin: 5px 0;
|
||||
padding: 10px 0;
|
||||
border-radius: 10px;
|
||||
display: grid;
|
||||
place-content: center;
|
||||
grid-template-columns: 40px 1fr 40px;
|
||||
grid-template-rows: 30px;
|
||||
grid-template-areas: 'close url .';
|
||||
column-gap: 10px;
|
||||
}
|
||||
|
||||
.li:hover {
|
||||
background-color: var(--bg-near-transparent);
|
||||
}
|
||||
|
||||
.close {
|
||||
opacity: 0;
|
||||
grid-area: close;
|
||||
place-self: center;
|
||||
}
|
||||
|
||||
.li:hover .close,
|
||||
.li:hover .eye {
|
||||
opacity: 1;
|
||||
}
|
||||
.close:focus,
|
||||
.eye:focus {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.hasSecret {
|
||||
grid-template-rows: repeat(2, 30px);
|
||||
grid-template-areas:
|
||||
'close url .'
|
||||
'close secret eye';
|
||||
}
|
||||
|
||||
.url {
|
||||
grid-area: url;
|
||||
}
|
||||
.secret {
|
||||
grid-area: secret;
|
||||
}
|
||||
.eye {
|
||||
grid-area: eye;
|
||||
opacity: 0;
|
||||
place-self: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.url,
|
||||
.secret {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.btn {
|
||||
outline: none;
|
||||
appearance: none;
|
||||
border: 1px solid transparent;
|
||||
background-color: transparent;
|
||||
color: inherit;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 5px;
|
||||
border-radius: 100px;
|
||||
}
|
||||
.btn:focus {
|
||||
border-color: var(--color-focus-blue);
|
||||
}
|
||||
.btn:hover:enabled {
|
||||
background-color: var(--color-focus-blue);
|
||||
}
|
||||
.btn:active:enabled {
|
||||
transform: scale(0.97);
|
||||
}
|
||||
.btn:disabled {
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
.url {
|
||||
cursor: pointer;
|
||||
}
|
||||
.url:hover {
|
||||
color: var(--color-text-highlight);
|
||||
}
|
147
src/components/BackendList.tsx
Normal file
147
src/components/BackendList.tsx
Normal file
|
@ -0,0 +1,147 @@
|
|||
import cx from 'clsx';
|
||||
import * as React from 'react';
|
||||
import { Eye, EyeOff, X as Close } from 'react-feather';
|
||||
import { useToggle } from 'src/hooks/basic';
|
||||
import {
|
||||
getClashAPIConfigs,
|
||||
getSelectedClashAPIConfigIndex,
|
||||
} from 'src/store/app';
|
||||
import { ClashAPIConfig } from 'src/types';
|
||||
|
||||
import s from './BackendList.module.css';
|
||||
import { connect, useStoreActions } from './StateProvider';
|
||||
|
||||
type Config = ClashAPIConfig & { addedAt: number };
|
||||
|
||||
const mapState = (s) => ({
|
||||
apiConfigs: getClashAPIConfigs(s),
|
||||
selectedClashAPIConfigIndex: getSelectedClashAPIConfigIndex(s),
|
||||
});
|
||||
|
||||
export const BackendList = connect(mapState)(BackendListImpl);
|
||||
|
||||
function BackendListImpl({
|
||||
apiConfigs,
|
||||
selectedClashAPIConfigIndex,
|
||||
}: {
|
||||
apiConfigs: Config[];
|
||||
selectedClashAPIConfigIndex: number;
|
||||
}) {
|
||||
const {
|
||||
app: { removeClashAPIConfig, selectClashAPIConfig },
|
||||
} = useStoreActions();
|
||||
|
||||
const onRemove = React.useCallback(
|
||||
(conf: ClashAPIConfig) => {
|
||||
removeClashAPIConfig(conf);
|
||||
},
|
||||
[removeClashAPIConfig]
|
||||
);
|
||||
const onSelect = React.useCallback(
|
||||
(conf: ClashAPIConfig) => {
|
||||
selectClashAPIConfig(conf);
|
||||
},
|
||||
[selectClashAPIConfig]
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<ul className={s.ul}>
|
||||
{apiConfigs.map((item, idx) => {
|
||||
return (
|
||||
<li
|
||||
className={cx(s.li, {
|
||||
[s.hasSecret]: item.secret,
|
||||
[s.isSelected]: idx === selectedClashAPIConfigIndex,
|
||||
})}
|
||||
key={item.baseURL + item.secret}
|
||||
>
|
||||
<Item
|
||||
disableRemove={idx === selectedClashAPIConfigIndex}
|
||||
baseURL={item.baseURL}
|
||||
secret={item.secret}
|
||||
onRemove={onRemove}
|
||||
onSelect={onSelect}
|
||||
/>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function Item({
|
||||
baseURL,
|
||||
secret,
|
||||
disableRemove,
|
||||
onRemove,
|
||||
onSelect,
|
||||
}: {
|
||||
baseURL: string;
|
||||
secret: string;
|
||||
disableRemove: boolean;
|
||||
onRemove: (x: ClashAPIConfig) => void;
|
||||
onSelect: (x: ClashAPIConfig) => void;
|
||||
}) {
|
||||
const [show, toggle] = useToggle();
|
||||
const Icon = show ? EyeOff : Eye;
|
||||
|
||||
const handleTap = React.useCallback((e: React.KeyboardEvent) => {
|
||||
e.stopPropagation();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
disabled={disableRemove}
|
||||
onClick={() => onRemove({ baseURL, secret })}
|
||||
className={s.close}
|
||||
>
|
||||
<Close size={20} />
|
||||
</Button>
|
||||
<span
|
||||
className={s.url}
|
||||
tabIndex={0}
|
||||
role="button"
|
||||
onClick={() => onSelect({ baseURL, secret })}
|
||||
onKeyUp={handleTap}
|
||||
>
|
||||
{baseURL}
|
||||
</span>
|
||||
<span />
|
||||
{secret ? (
|
||||
<>
|
||||
<span className={s.secret}>{show ? secret : '***'}</span>
|
||||
|
||||
<Button onClick={toggle} className={s.eye}>
|
||||
<Icon size={20} />
|
||||
</Button>
|
||||
</>
|
||||
) : null}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function Button({
|
||||
children,
|
||||
onClick,
|
||||
className,
|
||||
disabled,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
|
||||
onClick?: (e: React.MouseEvent<HTMLButtonElement>) => unknown;
|
||||
className: string;
|
||||
disabled?: boolean;
|
||||
}) {
|
||||
return (
|
||||
<button
|
||||
disabled={disabled}
|
||||
className={cx(className, s.btn)}
|
||||
onClick={onClick}
|
||||
>
|
||||
{children}
|
||||
</button>
|
||||
);
|
||||
}
|
|
@ -2,12 +2,12 @@ import PropTypes from 'prop-types';
|
|||
import React from 'react';
|
||||
|
||||
import {
|
||||
clearStorage,
|
||||
getClashAPIConfig,
|
||||
getLatencyTestUrl,
|
||||
getSelectedChartStyleIndex,
|
||||
} from '../store/app';
|
||||
import { fetchConfigs, getConfigs, updateConfigs } from '../store/configs';
|
||||
import { openModal } from '../store/modals';
|
||||
import Button from './Button';
|
||||
import s0 from './Config.module.css';
|
||||
import ContentHeader from './ContentHeader';
|
||||
|
@ -104,6 +104,10 @@ function ConfigImpl({
|
|||
refConfigs.current = configs;
|
||||
}, [configs]);
|
||||
|
||||
const openAPIConfigModal = useCallback(() => {
|
||||
dispatch(openModal('apiConfig'));
|
||||
}, [dispatch]);
|
||||
|
||||
const setConfigState = useCallback(
|
||||
(name, val) => {
|
||||
setConfigStateInternal({
|
||||
|
@ -256,7 +260,7 @@ function ConfigImpl({
|
|||
</div>
|
||||
<div>
|
||||
<div className={s0.label}>Action</div>
|
||||
<Button label="Log out" onClick={clearStorage} />
|
||||
<Button label="Switch backend" onClick={openAPIConfigModal} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,13 +1,10 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
|
||||
// import { getSentry } from '../misc/sentry';
|
||||
import { deriveMessageFromError } from '../misc/errors';
|
||||
import { getSentry } from '../misc/sentry';
|
||||
import ErrorBoundaryFallback from './ErrorBoundaryFallback';
|
||||
|
||||
// XXX this is no Hook equivalents for componentDidCatch
|
||||
// we have to use class for now
|
||||
|
||||
class ErrorBoundary extends Component {
|
||||
static propTypes = {
|
||||
children: PropTypes.node,
|
||||
|
@ -15,44 +12,13 @@ class ErrorBoundary extends Component {
|
|||
|
||||
state = { error: null };
|
||||
|
||||
loadSentry = async () => {
|
||||
if (this.sentry) return this.sentry;
|
||||
const x = await getSentry();
|
||||
this.sentry = x;
|
||||
return this.sentry;
|
||||
};
|
||||
|
||||
// static getDerivedStateFromError(error) {
|
||||
// return { error };
|
||||
// }
|
||||
|
||||
componentDidMount() {
|
||||
// this.loadSentry();
|
||||
static getDerivedStateFromError(error) {
|
||||
return { error };
|
||||
}
|
||||
|
||||
componentDidCatch(error, _info) {
|
||||
this.setState({ error });
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log(error, errorInfo);
|
||||
// this.setState({ error });
|
||||
// this.loadSentry().then(Sentry => {
|
||||
// Sentry.withScope(scope => {
|
||||
// Object.keys(errorInfo).forEach(key => {
|
||||
// scope.setExtra(key, errorInfo[key]);
|
||||
// });
|
||||
// Sentry.captureException(error);
|
||||
// });
|
||||
// });
|
||||
}
|
||||
|
||||
showReportDialog = () => {
|
||||
this.loadSentry().then((Sentry) => Sentry.showReportDialog());
|
||||
};
|
||||
|
||||
render() {
|
||||
if (this.state.error) {
|
||||
const { message, detail } = deriveMessageFromError(this.state.error);
|
||||
//render fallback UI
|
||||
return <ErrorBoundaryFallback message={message} detail={detail} />;
|
||||
} else {
|
||||
return this.props.children;
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
import './Root.css';
|
||||
|
||||
import React, { lazy, Suspense } from 'react';
|
||||
import { HashRouter as Router, Route, Routes } from 'react-router-dom';
|
||||
import { HashRouter as Router, useRoutes } from 'react-router-dom';
|
||||
import { RecoilRoot } from 'recoil';
|
||||
import { About } from 'src/components/about/About';
|
||||
|
||||
import { actions, initialState } from '../store';
|
||||
import APIConfig from './APIConfig';
|
||||
import APIDiscovery from './APIDiscovery';
|
||||
import ErrorBoundary from './ErrorBoundary';
|
||||
import Home from './Home';
|
||||
|
@ -52,33 +53,50 @@ const Rules = lazy(() =>
|
|||
);
|
||||
|
||||
const routes = [
|
||||
['home', '/', <Home />],
|
||||
['connections', '/connections', <Connections />],
|
||||
['configs', '/configs', <Config />],
|
||||
['logs', '/logs', <Logs />],
|
||||
['proxies', '/proxies', <Proxies />],
|
||||
['rules', '/rules', <Rules />],
|
||||
['about', '/about', <About />],
|
||||
__DEV__ ? ['style', '/style', <StyleGuide />] : false,
|
||||
{ path: '/', element: <Home /> },
|
||||
{ path: '/connections', element: <Connections /> },
|
||||
{ path: '/configs', element: <Config /> },
|
||||
{ path: '/logs', element: <Logs /> },
|
||||
{ path: '/proxies', element: <Proxies /> },
|
||||
{ path: '/rules', element: <Rules /> },
|
||||
{ path: '/about', element: <About /> },
|
||||
__DEV__ ? { path: '/style', element: <StyleGuide /> } : false,
|
||||
].filter(Boolean);
|
||||
|
||||
function RouteInnerApp() {
|
||||
return useRoutes(routes);
|
||||
}
|
||||
|
||||
function SideBarApp() {
|
||||
return (
|
||||
<>
|
||||
<APIDiscovery />
|
||||
<SideBar />
|
||||
<div className={s0.content}>
|
||||
<Suspense fallback={<Loading2 />}>
|
||||
<RouteInnerApp />
|
||||
</Suspense>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function App() {
|
||||
return useRoutes([
|
||||
{ path: '/backend', element: <APIConfig /> },
|
||||
{ path: '*', element: <SideBarApp /> },
|
||||
]);
|
||||
}
|
||||
|
||||
const Root = () => (
|
||||
<ErrorBoundary>
|
||||
<RecoilRoot>
|
||||
<StateProvider initialState={initialState} actions={actions}>
|
||||
<Router>
|
||||
<div className={s0.app}>
|
||||
<APIDiscovery />
|
||||
<SideBar />
|
||||
<div className={s0.content}>
|
||||
<Suspense fallback={<Loading2 />}>
|
||||
<Routes>
|
||||
{routes.map(([key, path, element]) => (
|
||||
<Route key={key} path={path} element={element} />
|
||||
))}
|
||||
</Routes>
|
||||
</Suspense>
|
||||
</div>
|
||||
<Suspense fallback={<Loading2 />}>
|
||||
<App />
|
||||
</Suspense>
|
||||
</div>
|
||||
</Router>
|
||||
</StateProvider>
|
||||
|
|
|
@ -25,8 +25,7 @@ export default connect(mapState)(TrafficChart);
|
|||
|
||||
function TrafficChart({ apiConfig, selectedChartStyleIndex }) {
|
||||
const Chart = chartJSResource.read();
|
||||
const { hostname, port, secret } = apiConfig;
|
||||
const traffic = fetchData({ hostname, port, secret });
|
||||
const traffic = fetchData(apiConfig);
|
||||
const data = useMemo(
|
||||
() => ({
|
||||
labels: traffic.labels,
|
||||
|
|
110
src/store/app.js
110
src/store/app.js
|
@ -1,4 +1,4 @@
|
|||
import { clearState, loadState, saveState } from '../misc/storage';
|
||||
import { loadState, saveState } from '../misc/storage';
|
||||
import { debounce, trimTrailingSlash } from '../misc/utils';
|
||||
import { fetchConfigs } from './configs';
|
||||
import { closeModal } from './modals';
|
||||
|
@ -7,6 +7,9 @@ export const getClashAPIConfig = (s) => {
|
|||
const idx = s.app.selectedClashAPIConfigIndex;
|
||||
return s.app.clashAPIConfigs[idx];
|
||||
};
|
||||
export const getSelectedClashAPIConfigIndex = (s) =>
|
||||
s.app.selectedClashAPIConfigIndex;
|
||||
export const getClashAPIConfigs = (s) => s.app.clashAPIConfigs;
|
||||
export const getTheme = (s) => s.app.theme;
|
||||
export const getSelectedChartStyleIndex = (s) => s.app.selectedChartStyleIndex;
|
||||
export const getLatencyTestUrl = (s) => s.app.latencyTestUrl;
|
||||
|
@ -17,6 +20,66 @@ export const getAutoCloseOldConns = (s) => s.app.autoCloseOldConns;
|
|||
|
||||
const saveStateDebounced = debounce(saveState, 600);
|
||||
|
||||
function findClashAPIConfigIndex(getState, { baseURL, secret }) {
|
||||
const arr = getClashAPIConfigs(getState());
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
const x = arr[i];
|
||||
if (x.baseURL === baseURL && x.secret === secret) return i;
|
||||
}
|
||||
}
|
||||
|
||||
export function addClashAPIConfig({ baseURL, secret }) {
|
||||
return async (dispatch, getState) => {
|
||||
const idx = findClashAPIConfigIndex(getState, { baseURL, secret });
|
||||
// already exists
|
||||
if (idx) return;
|
||||
|
||||
const clashAPIConfig = { baseURL, secret, addedAt: Date.now() };
|
||||
dispatch('addClashAPIConfig', (s) => {
|
||||
s.app.clashAPIConfigs.push(clashAPIConfig);
|
||||
});
|
||||
// side effect
|
||||
saveState(getState().app);
|
||||
};
|
||||
}
|
||||
|
||||
export function removeClashAPIConfig({ baseURL, secret }) {
|
||||
return async (dispatch, getState) => {
|
||||
const idx = findClashAPIConfigIndex(getState, { baseURL, secret });
|
||||
dispatch('removeClashAPIConfig', (s) => {
|
||||
s.app.clashAPIConfigs = [
|
||||
...s.app.clashAPIConfigs.slice(0, idx),
|
||||
...s.app.clashAPIConfigs.slice(idx + 1),
|
||||
];
|
||||
});
|
||||
// side effect
|
||||
saveState(getState().app);
|
||||
};
|
||||
}
|
||||
|
||||
export function selectClashAPIConfig({ baseURL, secret }) {
|
||||
return async (dispatch, getState) => {
|
||||
const idx = findClashAPIConfigIndex(getState, { baseURL, secret });
|
||||
const curr = getSelectedClashAPIConfigIndex(getState());
|
||||
if (curr !== idx) {
|
||||
dispatch('selectClashAPIConfig', (s) => {
|
||||
s.app.selectedClashAPIConfigIndex = idx;
|
||||
});
|
||||
}
|
||||
// side effect
|
||||
saveState(getState().app);
|
||||
|
||||
// manual clean up is too complex
|
||||
// we just reload the app
|
||||
try {
|
||||
window.location.reload();
|
||||
} catch (err) {
|
||||
// ignore
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// unused
|
||||
export function updateClashAPIConfig({ baseURL, secret }) {
|
||||
return async (dispatch, getState) => {
|
||||
const clashAPIConfig = { baseURL, secret };
|
||||
|
@ -55,15 +118,6 @@ export function switchTheme() {
|
|||
};
|
||||
}
|
||||
|
||||
export function clearStorage() {
|
||||
clearState();
|
||||
try {
|
||||
window.location.reload();
|
||||
} catch (err) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
export function selectChartStyleIndex(selectedChartStyleIndex) {
|
||||
return (dispatch, getState) => {
|
||||
dispatch('appSelectChartStyleIndex', (s) => {
|
||||
|
@ -97,6 +151,7 @@ export function updateCollapsibleIsOpen(prefix, name, v) {
|
|||
const defaultClashAPIConfig = {
|
||||
baseURL: 'http://127.0.0.1:7892',
|
||||
secret: '',
|
||||
addedAt: 0,
|
||||
};
|
||||
// type Theme = 'light' | 'dark';
|
||||
const defaultState = {
|
||||
|
@ -133,25 +188,24 @@ export function initialState() {
|
|||
const query = parseConfigQueryString();
|
||||
|
||||
const conf = s.clashAPIConfigs[s.selectedClashAPIConfigIndex];
|
||||
const url = new URL(conf.baseURL);
|
||||
if (query.hostname) {
|
||||
url.hostname = query.hostname;
|
||||
}
|
||||
if (query.port) {
|
||||
url.port = query.port;
|
||||
}
|
||||
// url.href is a stringifier and it appends a trailing slash
|
||||
// that is not we want
|
||||
conf.baseURL = trimTrailingSlash(url.href);
|
||||
|
||||
if (query.secret) {
|
||||
conf.secret = query.secret;
|
||||
}
|
||||
|
||||
if (query.theme) {
|
||||
if (query.theme === 'dark' || query.theme === 'light') {
|
||||
s.theme = query.theme;
|
||||
if (conf) {
|
||||
const url = new URL(conf.baseURL);
|
||||
if (query.hostname) {
|
||||
url.hostname = query.hostname;
|
||||
}
|
||||
if (query.port) {
|
||||
url.port = query.port;
|
||||
}
|
||||
// url.href is a stringifier and it appends a trailing slash
|
||||
// that is not we want
|
||||
conf.baseURL = trimTrailingSlash(url.href);
|
||||
if (query.secret) {
|
||||
conf.secret = query.secret;
|
||||
}
|
||||
}
|
||||
|
||||
if (query.theme === 'dark' || query.theme === 'light') {
|
||||
s.theme = query.theme;
|
||||
}
|
||||
// set initial theme
|
||||
setTheme(s.theme);
|
||||
|
|
|
@ -2,8 +2,8 @@ import * as configsAPI from '../api/configs';
|
|||
import * as trafficAPI from '../api/traffic';
|
||||
import { openModal } from './modals';
|
||||
|
||||
export const getConfigs = s => s.configs.configs;
|
||||
export const getLogLevel = s => s.configs.configs['log-level'];
|
||||
export const getConfigs = (s) => s.configs.configs;
|
||||
export const getLogLevel = (s) => s.configs.configs['log-level'];
|
||||
|
||||
export function fetchConfigs(apiConfig) {
|
||||
return async (dispatch, getState) => {
|
||||
|
@ -11,25 +11,20 @@ export function fetchConfigs(apiConfig) {
|
|||
try {
|
||||
res = await configsAPI.fetchConfigs(apiConfig);
|
||||
} catch (err) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('Error fetch configs', err);
|
||||
// TypeError and AbortError
|
||||
dispatch(openModal('apiConfig'));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!res.ok) {
|
||||
if (res.status === 404 || res.status === 401) {
|
||||
dispatch(openModal('apiConfig'));
|
||||
} else {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('Error fetch configs', res.statusText);
|
||||
}
|
||||
console.log('Error fetch configs', res.statusText);
|
||||
dispatch(openModal('apiConfig'));
|
||||
return;
|
||||
}
|
||||
|
||||
const payload = await res.json();
|
||||
|
||||
dispatch('store/configs#fetchConfigs', s => {
|
||||
dispatch('store/configs#fetchConfigs', (s) => {
|
||||
s.configs.configs = payload;
|
||||
});
|
||||
|
||||
|
@ -47,25 +42,25 @@ export function fetchConfigs(apiConfig) {
|
|||
}
|
||||
|
||||
function markHaveFetchedConfig() {
|
||||
return dispatch => {
|
||||
dispatch('store/configs#markHaveFetchedConfig', s => {
|
||||
return (dispatch) => {
|
||||
dispatch('store/configs#markHaveFetchedConfig', (s) => {
|
||||
s.configs.haveFetchedConfig = true;
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export function updateConfigs(apiConfig, partialConfg) {
|
||||
return async dispatch => {
|
||||
return async (dispatch) => {
|
||||
configsAPI
|
||||
.updateConfigs(apiConfig, partialConfg)
|
||||
.then(
|
||||
res => {
|
||||
(res) => {
|
||||
if (res.ok === false) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('Error update configs', res.statusText);
|
||||
}
|
||||
},
|
||||
err => {
|
||||
(err) => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('Error update configs', err);
|
||||
throw err;
|
||||
|
@ -75,7 +70,7 @@ export function updateConfigs(apiConfig, partialConfg) {
|
|||
dispatch(fetchConfigs(apiConfig));
|
||||
});
|
||||
|
||||
dispatch('storeConfigsOptimisticUpdateConfigs', s => {
|
||||
dispatch('storeConfigsOptimisticUpdateConfigs', (s) => {
|
||||
s.configs.configs = { ...s.configs.configs, ...partialConfg };
|
||||
});
|
||||
};
|
||||
|
@ -88,7 +83,7 @@ export const initialState = {
|
|||
'redir-port': 0,
|
||||
'allow-lan': false,
|
||||
mode: 'Rule',
|
||||
'log-level': 'info'
|
||||
'log-level': 'info',
|
||||
},
|
||||
haveFetchedConfig: false
|
||||
haveFetchedConfig: false,
|
||||
};
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import {
|
||||
initialState as app,
|
||||
removeClashAPIConfig,
|
||||
selectChartStyleIndex,
|
||||
selectClashAPIConfig,
|
||||
updateAppConfig,
|
||||
updateCollapsibleIsOpen,
|
||||
} from './app';
|
||||
|
@ -24,6 +26,8 @@ export const actions = {
|
|||
app: {
|
||||
updateCollapsibleIsOpen,
|
||||
updateAppConfig,
|
||||
removeClashAPIConfig,
|
||||
selectClashAPIConfig,
|
||||
},
|
||||
proxies: proxiesActions,
|
||||
};
|
||||
|
|
180
yarn.lock
180
yarn.lock
|
@ -1021,7 +1021,7 @@
|
|||
dependencies:
|
||||
regenerator-runtime "^0.13.4"
|
||||
|
||||
"@babel/runtime@^7.11.2", "@babel/runtime@^7.8.4":
|
||||
"@babel/runtime@^7.11.2", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.4":
|
||||
version "7.11.2"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.11.2.tgz#f549c13c754cc40b87644b9fa9f09a6a95fe0736"
|
||||
integrity sha512-TeWkU52so0mPtDcaCTxNBI/IHiz0pZgr8VEFqXFtZWpYD08ZB6FaSwVAS8MKRQAP3bYKiVjwysOJgMFY28o6Tw==
|
||||
|
@ -1218,56 +1218,56 @@
|
|||
schema-utils "^2.6.5"
|
||||
source-map "^0.7.3"
|
||||
|
||||
"@sentry/browser@^5.22.3":
|
||||
version "5.22.3"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-5.22.3.tgz#7a64bd1cf01bf393741a3e4bf35f82aa927f5b4e"
|
||||
integrity sha512-2TzE/CoBa5ZkvxJizDdi1Iz1ldmXSJpFQ1mL07PIXBjCt0Wxf+WOuFSj5IP4L40XHfJE5gU8wEvSH0VDR8nXtA==
|
||||
"@sentry/browser@^5.23.0":
|
||||
version "5.23.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-5.23.0.tgz#fc3a7a435a4524aa949f91d442435efda153fc0a"
|
||||
integrity sha512-lBBHb/NFDOy1K5E/noDkgaibTtxp8F8gmAaVhhpGvOjlcBp1wzNJhWRePYKWgjJ7yFudxGi4Qbferdhm9RwzbA==
|
||||
dependencies:
|
||||
"@sentry/core" "5.22.3"
|
||||
"@sentry/types" "5.22.3"
|
||||
"@sentry/utils" "5.22.3"
|
||||
"@sentry/core" "5.23.0"
|
||||
"@sentry/types" "5.23.0"
|
||||
"@sentry/utils" "5.23.0"
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@sentry/core@5.22.3":
|
||||
version "5.22.3"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/core/-/core-5.22.3.tgz#030f435f2b518f282ba8bd954dac90cd70888bd7"
|
||||
integrity sha512-eGL5uUarw3o4i9QUb9JoFHnhriPpWCaqeaIBB06HUpdcvhrjoowcKZj1+WPec5lFg5XusE35vez7z/FPzmJUDw==
|
||||
"@sentry/core@5.23.0":
|
||||
version "5.23.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/core/-/core-5.23.0.tgz#4d12ee4f5593e66fa5ffde0f9e9164a5468e5cec"
|
||||
integrity sha512-K8Wp/g1opaauKJh2w5Z1Vw/YdudHQgH6Ng5fBazHZxA7zB9R8EbVKDsjy8XEcyHsWB7fTSlYX/7coqmZNOADdg==
|
||||
dependencies:
|
||||
"@sentry/hub" "5.22.3"
|
||||
"@sentry/minimal" "5.22.3"
|
||||
"@sentry/types" "5.22.3"
|
||||
"@sentry/utils" "5.22.3"
|
||||
"@sentry/hub" "5.23.0"
|
||||
"@sentry/minimal" "5.23.0"
|
||||
"@sentry/types" "5.23.0"
|
||||
"@sentry/utils" "5.23.0"
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@sentry/hub@5.22.3":
|
||||
version "5.22.3"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/hub/-/hub-5.22.3.tgz#08309a70d2ea8d5e313d05840c1711f34f2fffe5"
|
||||
integrity sha512-INo47m6N5HFEs/7GMP9cqxOIt7rmRxdERunA3H2L37owjcr77MwHVeeJ9yawRS6FMtbWXplgWTyTIWIYOuqVbw==
|
||||
"@sentry/hub@5.23.0":
|
||||
version "5.23.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/hub/-/hub-5.23.0.tgz#7350c2971fafdb9f883f0629fd1b35d2288cd6e1"
|
||||
integrity sha512-P0sevLI9qAQc1J+AcHzNXwj83aG3GKiABVQJp0rgCUMtrXqLawa+j8pOHg8p7QWroHM7TKDMKeny9WemXBgzBQ==
|
||||
dependencies:
|
||||
"@sentry/types" "5.22.3"
|
||||
"@sentry/utils" "5.22.3"
|
||||
"@sentry/types" "5.23.0"
|
||||
"@sentry/utils" "5.23.0"
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@sentry/minimal@5.22.3":
|
||||
version "5.22.3"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/minimal/-/minimal-5.22.3.tgz#706e4029ae5494123d3875c658ba8911aa5cc440"
|
||||
integrity sha512-HoINpYnVYCpNjn2XIPIlqH5o4BAITpTljXjtAftOx6Hzj+Opjg8tR8PWliyKDvkXPpc4kXK9D6TpEDw8MO0wZA==
|
||||
"@sentry/minimal@5.23.0":
|
||||
version "5.23.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/minimal/-/minimal-5.23.0.tgz#93df781a98f0b334425f68b1fa0f4e1a86d8fa45"
|
||||
integrity sha512-/w/B7ShMVu/tLI0/A5X+w6GfdZIQdFQihWyIK1vXaYS5NS6biGI3K6DcACuMrD/h4BsqlfgdXSOHHrmCJcyCXQ==
|
||||
dependencies:
|
||||
"@sentry/hub" "5.22.3"
|
||||
"@sentry/types" "5.22.3"
|
||||
"@sentry/hub" "5.23.0"
|
||||
"@sentry/types" "5.23.0"
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@sentry/types@5.22.3":
|
||||
version "5.22.3"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/types/-/types-5.22.3.tgz#d1d547b30ee8bd7771fa893af74c4f3d71f0fd18"
|
||||
integrity sha512-cv+VWK0YFgCVDvD1/HrrBWOWYG3MLuCUJRBTkV/Opdy7nkdNjhCAJQrEyMM9zX0sac8FKWKOHT0sykNh8KgmYw==
|
||||
"@sentry/types@5.23.0":
|
||||
version "5.23.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/types/-/types-5.23.0.tgz#935701637c2d5b1c123ac292bc253bddf451b076"
|
||||
integrity sha512-PbN5MVWxrq05sZ707lc8lleV0xSsI6jWr9h9snvbAuMjcauE0lmdWmjoWKY3PAz2s1mGYFh55kIo8SmQuVwbYg==
|
||||
|
||||
"@sentry/utils@5.22.3":
|
||||
version "5.22.3"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-5.22.3.tgz#e3bda3e789239eb16d436f768daa12829f33d18f"
|
||||
integrity sha512-AHNryXMBvIkIE+GQxTlmhBXD0Ksh+5w1SwM5qi6AttH+1qjWLvV6WB4+4pvVvEoS8t5F+WaVUZPQLmCCWp6zKw==
|
||||
"@sentry/utils@5.23.0":
|
||||
version "5.23.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-5.23.0.tgz#5e15f684b5a8f9c86e4ba15d81101eac7d6c240b"
|
||||
integrity sha512-D5gQDM0wEjKxhE+YNvCuCHo/6JuaORF2/3aOhoJBR+dy9EACRspg7kp3+9KF44xd2HVEXkSVCJkv8/+sHePYRQ==
|
||||
dependencies:
|
||||
"@sentry/types" "5.22.3"
|
||||
"@sentry/types" "5.23.0"
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@types/anymatch@*":
|
||||
|
@ -3665,7 +3665,7 @@ eslint-module-utils@^2.6.0:
|
|||
debug "^2.6.9"
|
||||
pkg-dir "^2.0.0"
|
||||
|
||||
eslint-plugin-flowtype@^5.1.0:
|
||||
eslint-plugin-flowtype@^5.2.0:
|
||||
version "5.2.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-flowtype/-/eslint-plugin-flowtype-5.2.0.tgz#a4bef5dc18f9b2bdb41569a4ab05d73805a3d261"
|
||||
integrity sha512-z7ULdTxuhlRJcEe1MVljePXricuPOrsWfScRXFhNzVD5dmTHWjIF57AxD0e7AbEoLSbjSsaA5S+hCg43WvpXJQ==
|
||||
|
@ -3692,10 +3692,10 @@ eslint-plugin-import@^2.22.0:
|
|||
resolve "^1.17.0"
|
||||
tsconfig-paths "^3.9.0"
|
||||
|
||||
eslint-plugin-jest@^24.0.0:
|
||||
version "24.0.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-24.0.0.tgz#6b1c460c529104c7d16d889e76fe708b281c4d14"
|
||||
integrity sha512-a0G7hSDbuBCW4PNT6MVpAyfnGbUDOqxzOyhR6wT2BIBnR7MhvfAqd6KKfsTjX+Z3gxzIHiEsihzdClU4cSc6qQ==
|
||||
eslint-plugin-jest@^24.0.1:
|
||||
version "24.0.1"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-24.0.1.tgz#ad5e091d47cf895e15dc115e18f98471135a334f"
|
||||
integrity sha512-8tYFDqOHGr7vVfdVYspmlV4sRBTylrM4gSLgkGKlO6F+djDOEJ+tEU7I50smUs7AIvFnNZutXUQAMgI9s9N6xQ==
|
||||
dependencies:
|
||||
"@typescript-eslint/experimental-utils" "^4.0.1"
|
||||
|
||||
|
@ -3716,10 +3716,10 @@ eslint-plugin-jsx-a11y@^6.3.1:
|
|||
jsx-ast-utils "^2.4.1"
|
||||
language-tags "^1.0.5"
|
||||
|
||||
eslint-plugin-react-hooks@^4.0.8:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.1.0.tgz#6323fbd5e650e84b2987ba76370523a60f4e7925"
|
||||
integrity sha512-36zilUcDwDReiORXmcmTc6rRumu9JIM3WjSvV0nclHoUQ0CNrX866EwONvLR/UqaeqFutbAnVu8PEmctdo2SRQ==
|
||||
eslint-plugin-react-hooks@^4.1.2:
|
||||
version "4.1.2"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.1.2.tgz#2eb53731d11c95826ef7a7272303eabb5c9a271e"
|
||||
integrity sha512-ykUeqkGyUGgwTtk78C0o8UG2fzwmgJ0qxBGPp2WqRKsTwcLuVf01kTDRAtOsd4u6whX2XOC8749n2vPydP82fg==
|
||||
|
||||
eslint-plugin-react@^7.20.6:
|
||||
version "7.20.6"
|
||||
|
@ -3781,10 +3781,10 @@ eslint-visitor-keys@^2.0.0:
|
|||
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz#21fdc8fbcd9c795cc0321f0563702095751511a8"
|
||||
integrity sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ==
|
||||
|
||||
eslint@^7.6.0:
|
||||
version "7.8.1"
|
||||
resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.8.1.tgz#e59de3573fb6a5be8ff526c791571646d124a8fa"
|
||||
integrity sha512-/2rX2pfhyUG0y+A123d0ccXtMm7DV7sH1m3lk9nk2DZ2LReq39FXHueR9xZwshE5MdfSf0xunSaMWRqyIA6M1w==
|
||||
eslint@^7.9.0:
|
||||
version "7.9.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.9.0.tgz#522aeccc5c3a19017cf0cb46ebfd660a79acf337"
|
||||
integrity sha512-V6QyhX21+uXp4T+3nrNfI3hQNBDa/P8ga7LoQOenwrlEFXrEnUEE+ok1dMtaS3b6rmLXhT1TkTIsG75HMLbknA==
|
||||
dependencies:
|
||||
"@babel/code-frame" "^7.0.0"
|
||||
"@eslint/eslintrc" "^0.1.3"
|
||||
|
@ -4243,10 +4243,10 @@ fork-ts-checker-webpack-plugin@1.5.0:
|
|||
tapable "^1.0.0"
|
||||
worker-rpc "^0.1.0"
|
||||
|
||||
fork-ts-checker-webpack-plugin@^5.0.14:
|
||||
version "5.1.0"
|
||||
resolved "https://registry.yarnpkg.com/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-5.1.0.tgz#586fbee24aeea950c53bab529e32017f543e71cf"
|
||||
integrity sha512-vuKyEjSLGbhQbEr5bifXXOkr9iV73L6n72mHoHIv7okvrf7O7z6RKeplM6C6ATPsukoQivij+Ba1vcptL60Z2g==
|
||||
fork-ts-checker-webpack-plugin@^5.2.0:
|
||||
version "5.2.0"
|
||||
resolved "https://registry.yarnpkg.com/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-5.2.0.tgz#780a14fb0da02a892baedd7aa0d089a1eda7b3e5"
|
||||
integrity sha512-NEKcI0+osT5bBFZ1SFGzJMQETjQWZrSvMO1g0nAR/w0t328Z41eN8BJEIZyFCl2HsuiJpa9AN474Nh2qLVwGLQ==
|
||||
dependencies:
|
||||
"@babel/code-frame" "^7.8.3"
|
||||
"@types/json-schema" "^7.0.5"
|
||||
|
@ -4272,14 +4272,14 @@ fragment-cache@^0.2.1:
|
|||
dependencies:
|
||||
map-cache "^0.2.2"
|
||||
|
||||
framer-motion@^2.6.7:
|
||||
version "2.6.7"
|
||||
resolved "https://registry.yarnpkg.com/framer-motion/-/framer-motion-2.6.7.tgz#f01685750573ab24fec8b53ac48a05e8fec6deef"
|
||||
integrity sha512-szwqKCH/yvMtaFmtphP3np/1/5HMDbPWfmAbVMXDvofC3qDi4+XsxVnI60egCnmYGxPeE5t4JFbZjAqOZy4HPg==
|
||||
framer-motion@^2.6.13:
|
||||
version "2.6.13"
|
||||
resolved "https://registry.yarnpkg.com/framer-motion/-/framer-motion-2.6.13.tgz#cc26faf53bffb2d9a558bc71a3adaf3c437c0e55"
|
||||
integrity sha512-ScEdiDyAT+kH51axqxFcHA9RLnJ8TpMQBf8HwcXBOt6wDVptHdewEnE3T7semz+7OY0sj0RCsUDhQaWo78aMqQ==
|
||||
dependencies:
|
||||
framesync "^4.1.0"
|
||||
hey-listen "^1.0.8"
|
||||
popmotion "9.0.0-rc.13"
|
||||
popmotion "9.0.0-rc.14"
|
||||
style-value-types "^3.1.9"
|
||||
tslib "^1.10.0"
|
||||
optionalDependencies:
|
||||
|
@ -4829,10 +4829,10 @@ immer@1.10.0:
|
|||
resolved "https://registry.yarnpkg.com/immer/-/immer-1.10.0.tgz#bad67605ba9c810275d91e1c2a47d4582e98286d"
|
||||
integrity sha512-O3sR1/opvCDGLEVcvrGTMtLac8GJ5IwZC4puPrLuRj3l7ICKvkmA0vGuU9OW8mV9WIBRnaxp5GJh9IEAaNOoYg==
|
||||
|
||||
immer@^7.0.8:
|
||||
version "7.0.8"
|
||||
resolved "https://registry.yarnpkg.com/immer/-/immer-7.0.8.tgz#41dcbc5669a76500d017bef3ad0d03ce0a1d7c1e"
|
||||
integrity sha512-XnpIN8PXBBaOD43U8Z17qg6RQiKQYGDGGCIbz1ixmLGwBkSWwmrmx5X7d+hTtXDM8ur7m5OdLE0PiO+y5RB3pw==
|
||||
immer@^7.0.9:
|
||||
version "7.0.9"
|
||||
resolved "https://registry.yarnpkg.com/immer/-/immer-7.0.9.tgz#28e7552c21d39dd76feccd2b800b7bc86ee4a62e"
|
||||
integrity sha512-Vs/gxoM4DqNAYR7pugIxi0Xc8XAun/uy7AQu4fLLqaTBHxjOP9pJ266Q9MWA/ly4z6rAFZbvViOtihxUZ7O28A==
|
||||
|
||||
import-fresh@^2.0.0:
|
||||
version "2.0.0"
|
||||
|
@ -5760,10 +5760,10 @@ mimic-fn@^2.1.0:
|
|||
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
|
||||
integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==
|
||||
|
||||
mini-css-extract-plugin@^0.11.0:
|
||||
version "0.11.0"
|
||||
resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-0.11.0.tgz#3918953075109d4ca204bf1e8a393a78d3cc821f"
|
||||
integrity sha512-dVWGuWJlQw2lZxsxBI3hOsoxg1k3DruLR0foHQLSkQMfk+qLJbv9dUk8fjmjWQKN9ef2n54ehA2FjClAsQhrWQ==
|
||||
mini-css-extract-plugin@^0.11.2:
|
||||
version "0.11.2"
|
||||
resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-0.11.2.tgz#e3af4d5e04fbcaaf11838ab230510073060b37bf"
|
||||
integrity sha512-h2LknfX4U1kScXxH8xE9LCOqT5B+068EAj36qicMb8l4dqdJoyHcmWmpd+ueyZfgu/POvIn+teoUnTtei2ikug==
|
||||
dependencies:
|
||||
loader-utils "^1.1.0"
|
||||
normalize-url "1.9.1"
|
||||
|
@ -6554,10 +6554,10 @@ please-upgrade-node@^3.2.0:
|
|||
dependencies:
|
||||
semver-compare "^1.0.0"
|
||||
|
||||
popmotion@9.0.0-rc.13:
|
||||
version "9.0.0-rc.13"
|
||||
resolved "https://registry.yarnpkg.com/popmotion/-/popmotion-9.0.0-rc.13.tgz#0280968fb0dc3bd273cc3dbd4da24b01b6cfbcce"
|
||||
integrity sha512-M8Ksx2THYrAUrptE5Ydd3jazQAMoXxqpClR/K8xK87v4UrJUTCb39kNar/ucJ4Rs7qPqPdVu+iqPKOJhpMylXQ==
|
||||
popmotion@9.0.0-rc.14:
|
||||
version "9.0.0-rc.14"
|
||||
resolved "https://registry.yarnpkg.com/popmotion/-/popmotion-9.0.0-rc.14.tgz#e57351b7b85a3e42b7a16affbbd440138797c11f"
|
||||
integrity sha512-zdMw1OSKjFBH+KKpZx7P+cGSUb3QCqg5QD12f6llucUeEFT+SDZYxvTY09JI23ZcJyzxgKFT1anbLq0eZ9bj3g==
|
||||
dependencies:
|
||||
framesync "^4.1.0"
|
||||
hey-listen "^1.0.8"
|
||||
|
@ -6652,10 +6652,10 @@ postcss-import@^12.0.1:
|
|||
read-cache "^1.0.0"
|
||||
resolve "^1.1.7"
|
||||
|
||||
postcss-loader@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-4.0.0.tgz#bfd04521d2a2db69cb9e8a7cee0a5ae704e751dc"
|
||||
integrity sha512-LdpfM9yCVFeJzofnaFvLf3g9oMuH2mIIqOcu81n6JHxzRNBl78GHiYWUJ5gf4c7A7VZiBCeWwfVAMw/mQCAM3Q==
|
||||
postcss-loader@^4.0.1:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-4.0.1.tgz#d1a2cf443005bcd65e856d3881204886646398d4"
|
||||
integrity sha512-m+2fe21cs/1Oz4Yds90al5uqVSc0yJRhYSfCRnsnVLt3z0QoNPpqLdgW7CGVWmlUlKEGL9vmq+P4hHS6Orb5DA==
|
||||
dependencies:
|
||||
cosmiconfig "^7.0.0"
|
||||
klona "^2.0.3"
|
||||
|
@ -7239,10 +7239,12 @@ react-modal@^3.11.1:
|
|||
react-lifecycles-compat "^3.0.0"
|
||||
warning "^4.0.3"
|
||||
|
||||
react-query@^2.15.4:
|
||||
version "2.15.4"
|
||||
resolved "https://registry.yarnpkg.com/react-query/-/react-query-2.15.4.tgz#0447510fc30aebba5a4b99879ec4b7155d9b3f20"
|
||||
integrity sha512-gEF2Ebe1MtiyLKEaVEIlRMl6rwvP/RVwP+Xf68vhXDfD91IeZkkW7plRjoaHa0/NeUPM1AgTHb1Z7NtqaYDXjA==
|
||||
react-query@^2.17.0:
|
||||
version "2.17.0"
|
||||
resolved "https://registry.yarnpkg.com/react-query/-/react-query-2.17.0.tgz#ac6c1a17398ab86c65008d7a9a4c0d5385626d10"
|
||||
integrity sha512-YtJ6Oot1vHyAHmllPdINEc0+3NnkjIWUcXZBUiScf+MpcRbzn8zBTc+QLF8dta+Zfm/HPHz1fFMYjAmTOSc/zw==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.5.5"
|
||||
|
||||
react-refresh@^0.8.2:
|
||||
version "0.8.3"
|
||||
|
@ -8383,19 +8385,19 @@ terser-webpack-plugin@^1.4.3:
|
|||
webpack-sources "^1.4.0"
|
||||
worker-farm "^1.7.0"
|
||||
|
||||
terser-webpack-plugin@^4.1.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-4.1.0.tgz#6e9d6ae4e1a900d88ddce8da6a47507ea61f44bc"
|
||||
integrity sha512-0ZWDPIP8BtEDZdChbufcXUigOYk6dOX/P/X0hWxqDDcVAQLb8Yy/0FAaemSfax3PAA67+DJR778oz8qVbmy4hA==
|
||||
terser-webpack-plugin@^4.2.0:
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-4.2.0.tgz#6240a71101a55c6823d84e11c8fff380b9dfd26f"
|
||||
integrity sha512-Wi0YFbWKG8gBXhbJmrMusRcoXl/C9U5BzIPC2Tn3Si0hejGhhIh0gPf9rEfOCxwigzRPLC8PXv42qDiRTocMXg==
|
||||
dependencies:
|
||||
cacache "^15.0.5"
|
||||
find-cache-dir "^3.3.1"
|
||||
jest-worker "^26.3.0"
|
||||
p-limit "^3.0.2"
|
||||
schema-utils "^2.6.6"
|
||||
schema-utils "^2.7.1"
|
||||
serialize-javascript "^4.0.0"
|
||||
source-map "^0.6.1"
|
||||
terser "^5.0.0"
|
||||
terser "^5.3.0"
|
||||
webpack-sources "^1.4.3"
|
||||
|
||||
terser@^4.1.2:
|
||||
|
@ -8416,10 +8418,10 @@ terser@^4.6.3:
|
|||
source-map "~0.6.1"
|
||||
source-map-support "~0.5.12"
|
||||
|
||||
terser@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/terser/-/terser-5.0.0.tgz#269640e4e92f15d628de1e5f01c4c61e1ba3d765"
|
||||
integrity sha512-olH2DwGINoSuEpSGd+BsPuAQaA3OrHnHnFL/rDB2TVNc3srUbz/rq/j2BlF4zDXI+JqAvGr86bIm1R2cJgZ3FA==
|
||||
terser@^5.3.0:
|
||||
version "5.3.1"
|
||||
resolved "https://registry.yarnpkg.com/terser/-/terser-5.3.1.tgz#f50fe20ab48b15234fe9bdd86b10148ad5fca787"
|
||||
integrity sha512-yD80f4hdwCWTH5mojzxe1q8bN1oJbsK/vfJGLcPZM/fl+/jItIVNKhFIHqqR71OipFWMLgj3Kc+GIp6CeIqfnA==
|
||||
dependencies:
|
||||
commander "^2.20.0"
|
||||
source-map "~0.6.1"
|
||||
|
|
Loading…
Reference in a new issue