chore: run ts-migrate
This commit is contained in:
parent
e62c916548
commit
ff1a39d04e
67 changed files with 476 additions and 294 deletions
|
@ -52,6 +52,7 @@
|
|||
"react": "0.0.0-experimental-94c0244ba",
|
||||
"react-dom": "0.0.0-experimental-94c0244ba",
|
||||
"react-feather": "^2.0.3",
|
||||
"react-helmet": "^6.1.0",
|
||||
"react-icons": "^3.10.0",
|
||||
"react-modal": "^3.11.1",
|
||||
"react-query": "^2.23.1",
|
||||
|
@ -82,6 +83,8 @@
|
|||
"@types/lodash-es": "^4.17.3",
|
||||
"@types/react": "^16.9.52",
|
||||
"@types/react-dom": "^16.9.8",
|
||||
"@types/react-helmet": "^6.1.0",
|
||||
"@types/react-tabs": "^2.3.2",
|
||||
"@typescript-eslint/eslint-plugin": "^4.4.0",
|
||||
"@typescript-eslint/parser": "^4.4.0",
|
||||
"autoprefixer": "^10.0.0",
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { getURLAndInit } from 'src/misc/request-helper';
|
||||
import { ClashGeneralConfig } from 'src/store/types';
|
||||
import { ClashAPIConfig } from 'src/types';
|
||||
|
||||
const endpoint = '/configs';
|
||||
|
@ -12,7 +13,7 @@ export async function fetchConfigs(apiConfig: ClashAPIConfig) {
|
|||
// req body
|
||||
// { Path: string }
|
||||
|
||||
type ClashConfigPartial = { 'socks-port'?: unknown };
|
||||
type ClashConfigPartial = Partial<ClashGeneralConfig>;
|
||||
function configsPatchWorkaround(o: ClashConfigPartial) {
|
||||
// backward compatibility for older clash using `socket-port`
|
||||
if ('socks-port' in o) {
|
||||
|
|
|
@ -9,11 +9,13 @@ const subscribers = [];
|
|||
|
||||
// see also https://github.com/Dreamacro/clash/blob/dev/constant/metadata.go#L41
|
||||
type UUID = string;
|
||||
type ConnectionItem = {
|
||||
type ConnNetwork = 'tcp' | 'udp';
|
||||
type ConnType = 'HTTP' | 'HTTP Connect' | 'Socks5' | 'Redir' | 'Unknown';
|
||||
export type ConnectionItem = {
|
||||
id: UUID;
|
||||
metadata: {
|
||||
network: 'tcp' | 'udp';
|
||||
type: 'HTTP' | 'HTTP Connect' | 'Socks5' | 'Redir' | 'Unknown';
|
||||
network: ConnNetwork;
|
||||
type: ConnType;
|
||||
sourceIP: string;
|
||||
destinationIP: string;
|
||||
sourcePort: string;
|
||||
|
@ -24,7 +26,7 @@ type ConnectionItem = {
|
|||
download: number;
|
||||
// e.g. "2019-11-30T22:48:13.416668+08:00",
|
||||
start: string;
|
||||
chains: Array<string>;
|
||||
chains: string[];
|
||||
// e.g. 'Match', 'DomainKeyword'
|
||||
rule: string;
|
||||
};
|
||||
|
|
|
@ -80,6 +80,7 @@ function APIConfig({ dispatch }) {
|
|||
<div className={s0.hostnamePort}>
|
||||
<Field
|
||||
id="baseURL"
|
||||
// @ts-expect-error ts-migrate(2322) FIXME: Type '{ id: string; name: string; label: string; t... Remove this comment to see the full error message
|
||||
name="baseURL"
|
||||
label="API Base URL"
|
||||
type="text"
|
||||
|
@ -88,6 +89,7 @@ function APIConfig({ dispatch }) {
|
|||
/>
|
||||
<Field
|
||||
id="secret"
|
||||
// @ts-expect-error ts-migrate(2322) FIXME: Type '{ id: string; name: string; label: string; v... Remove this comment to see the full error message
|
||||
name="secret"
|
||||
label="Secret(optional)"
|
||||
value={secret}
|
||||
|
|
|
@ -15,6 +15,7 @@ function APIDiscovery({ dispatch, apiConfig, modals }) {
|
|||
if (!window.fetch) {
|
||||
const { detail } = errors[DOES_NOT_SUPPORT_FETCH];
|
||||
const err = new Error(detail);
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'code' does not exist on type 'Error'.
|
||||
err.code = DOES_NOT_SUPPORT_FETCH;
|
||||
throw err;
|
||||
}
|
||||
|
@ -31,6 +32,7 @@ function APIDiscovery({ dispatch, apiConfig, modals }) {
|
|||
isOpen={modals.apiConfig}
|
||||
className={s0.content}
|
||||
overlayClassName={s0.overlay}
|
||||
// @ts-expect-error ts-migrate(2322) FIXME: Type '{ children: Element; isOpen: any; className:... Remove this comment to see the full error message
|
||||
shouldCloseOnOverlayClick={false}
|
||||
shouldCloseOnEsc={false}
|
||||
onRequestClose={closeApiConfigModal}
|
|
@ -114,6 +114,7 @@ function Item({
|
|||
<>
|
||||
<span className={s.secret}>{show ? secret : '***'}</span>
|
||||
|
||||
{/* @ts-expect-error ts-migrate(2322) FIXME: Type 'boolean | (() => void)' is not assignable to... Remove this comment to see the full error message */}
|
||||
<Button onClick={toggle} className={s.eye}>
|
||||
<Icon size={20} />
|
||||
</Button>
|
||||
|
|
|
@ -50,10 +50,12 @@ const variantsCollpapsibleChildContainer = {
|
|||
},
|
||||
};
|
||||
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'isOpen' does not exist on type '{ childr... Remove this comment to see the full error message
|
||||
const Collapsible = memo(({ children, isOpen }) => {
|
||||
const module = framerMotionResouce.read();
|
||||
const motion = module.motion;
|
||||
const previous = usePrevious(isOpen);
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'height' does not exist on type 'MutableR... Remove this comment to see the full error message
|
||||
const [refToMeature, { height }] = useMeasure();
|
||||
return (
|
||||
<div>
|
|
@ -119,11 +119,11 @@ function ConfigImpl({
|
|||
);
|
||||
|
||||
const handleSwitchOnChange = useCallback(
|
||||
(checked) => {
|
||||
(checked: boolean) => {
|
||||
const name = 'allow-lan';
|
||||
const value = checked;
|
||||
setConfigState(name, value);
|
||||
dispatch(updateConfigs(apiConfig, { [name]: value }));
|
||||
dispatch(updateConfigs(apiConfig, { 'allow-lan': value }));
|
||||
},
|
||||
[apiConfig, dispatch, setConfigState]
|
||||
);
|
||||
|
@ -200,6 +200,7 @@ function ConfigImpl({
|
|||
name={f.key}
|
||||
value={configState[f.key]}
|
||||
onChange={handleInputOnChange}
|
||||
// @ts-expect-error ts-migrate(2322) FIXME: Type '{ name: string; value: any; onChange: (e: an... Remove this comment to see the full error message
|
||||
onBlur={handleInputOnBlur}
|
||||
/>
|
||||
</div>
|
||||
|
@ -267,6 +268,7 @@ function ConfigImpl({
|
|||
);
|
||||
}
|
||||
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'propTypes' does not exist on type '(prop... Remove this comment to see the full error message
|
||||
Config.propTypes = {
|
||||
configs: PropTypes.object,
|
||||
};
|
|
@ -3,6 +3,8 @@ import './Connections.css';
|
|||
import React from 'react';
|
||||
import { Pause, Play, X as IconClose } from 'react-feather';
|
||||
import { Tab, TabList, TabPanel, Tabs } from 'react-tabs';
|
||||
import { ConnectionItem } from 'src/api/connections';
|
||||
import { State } from 'src/store/types';
|
||||
|
||||
import * as connAPI from '../api/connections';
|
||||
import useRemainingViewPortHeight from '../hooks/useRemainingViewPortHeight';
|
||||
|
@ -19,7 +21,7 @@ const { useEffect, useState, useRef, useCallback } = React;
|
|||
|
||||
const paddingBottom = 30;
|
||||
|
||||
function arrayToIdKv(items) {
|
||||
function arrayToIdKv<T extends { id: string }>(items: T[]) {
|
||||
const o = {};
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
const item = items[i];
|
||||
|
@ -28,9 +30,30 @@ function arrayToIdKv(items) {
|
|||
return o;
|
||||
}
|
||||
|
||||
function filterConns(conns, keyword) {
|
||||
const hasSubstring = (s, pat) => s.toLowerCase().includes(pat.toLowerCase());
|
||||
type FormattedConn = {
|
||||
id: string;
|
||||
upload: number;
|
||||
download: number;
|
||||
start: number;
|
||||
chains: string;
|
||||
rule: string;
|
||||
destinationPort: string;
|
||||
destinationIP: string;
|
||||
sourceIP: string;
|
||||
sourcePort: string;
|
||||
source: string;
|
||||
host: string;
|
||||
type: string;
|
||||
network: string;
|
||||
downloadSpeedCurr?: number;
|
||||
uploadSpeedCurr?: number;
|
||||
};
|
||||
|
||||
function hasSubstring(s: string, pat: string) {
|
||||
return s.toLowerCase().includes(pat.toLowerCase());
|
||||
}
|
||||
|
||||
function filterConns(conns: FormattedConn[], keyword: string) {
|
||||
return !keyword
|
||||
? conns
|
||||
: conns.filter((conn) =>
|
||||
|
@ -47,10 +70,13 @@ function filterConns(conns, keyword) {
|
|||
);
|
||||
}
|
||||
|
||||
function formatConnectionDataItem(i, prevKv, now) {
|
||||
function formatConnectionDataItem(
|
||||
i: ConnectionItem,
|
||||
prevKv: Record<string, { upload: number; download: number }>,
|
||||
now: number
|
||||
): FormattedConn {
|
||||
const { id, metadata, upload, download, start, chains, rule } = i;
|
||||
// eslint-disable-next-line prefer-const
|
||||
let {
|
||||
const {
|
||||
host,
|
||||
destinationPort,
|
||||
destinationIP,
|
||||
|
@ -60,27 +86,27 @@ function formatConnectionDataItem(i, prevKv, now) {
|
|||
sourcePort,
|
||||
} = metadata;
|
||||
// host could be an empty string if it's direct IP connection
|
||||
if (host === '') host = destinationIP;
|
||||
|
||||
let host2 = host;
|
||||
if (host2 === '') host2 = destinationIP;
|
||||
const prev = prevKv[id];
|
||||
const ret = {
|
||||
id,
|
||||
upload,
|
||||
download,
|
||||
start: now - new Date(start),
|
||||
start: now - new Date(start).valueOf(),
|
||||
chains: chains.reverse().join(' / '),
|
||||
rule,
|
||||
...metadata,
|
||||
host: `${host}:${destinationPort}`,
|
||||
host: `${host2}:${destinationPort}`,
|
||||
type: `${type}(${network})`,
|
||||
source: `${sourceIP}:${sourcePort}`,
|
||||
downloadSpeedCurr: download - (prev ? prev.download : 0),
|
||||
uploadSpeedCurr: upload - (prev ? prev.upload : 0),
|
||||
};
|
||||
const prev = prevKv[id];
|
||||
ret.downloadSpeedCurr = download - (prev ? prev.download : 0);
|
||||
ret.uploadSpeedCurr = upload - (prev ? prev.upload : 0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
function renderTableOrPlaceholder(conns) {
|
||||
function renderTableOrPlaceholder(conns: FormattedConn[]) {
|
||||
return conns.length > 0 ? (
|
||||
<ConnectionTable data={conns} />
|
||||
) : (
|
||||
|
@ -119,13 +145,13 @@ function Conn({ apiConfig }) {
|
|||
const read = useCallback(
|
||||
({ connections }) => {
|
||||
const prevConnsKv = arrayToIdKv(prevConnsRef.current);
|
||||
const now = new Date();
|
||||
const x = connections.map((c) =>
|
||||
const now = Date.now();
|
||||
const x = connections.map((c: ConnectionItem) =>
|
||||
formatConnectionDataItem(c, prevConnsKv, now)
|
||||
);
|
||||
const closed = [];
|
||||
for (const c of prevConnsRef.current) {
|
||||
const idx = x.findIndex((conn) => conn.id === c.id);
|
||||
const idx = x.findIndex((conn: ConnectionItem) => conn.id === c.id);
|
||||
if (idx < 0) closed.push(c);
|
||||
}
|
||||
setClosedConns((prev) => {
|
||||
|
@ -165,12 +191,14 @@ function Conn({ apiConfig }) {
|
|||
<Tab>
|
||||
<span>Active</span>
|
||||
<span className={s.connQty}>
|
||||
{/* @ts-expect-error ts-migrate(2786) FIXME: 'ConnQty' cannot be used as a JSX component. */}
|
||||
<ConnQty qty={filteredConns.length} />
|
||||
</span>
|
||||
</Tab>
|
||||
<Tab>
|
||||
<span>Closed</span>
|
||||
<span className={s.connQty}>
|
||||
{/* @ts-expect-error ts-migrate(2786) FIXME: 'ConnQty' cannot be used as a JSX component. */}
|
||||
<ConnQty qty={filteredClosedConns.length} />
|
||||
</span>
|
||||
</Tab>
|
||||
|
@ -187,11 +215,13 @@ function Conn({ apiConfig }) {
|
|||
</div>
|
||||
</div>
|
||||
<div
|
||||
// @ts-expect-error ts-migrate(2322) FIXME: Type 'number | MutableRefObject<any>' is not assig... Remove this comment to see the full error message
|
||||
ref={refContainer}
|
||||
style={{ padding: 30, paddingBottom, paddingTop: 0 }}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
// @ts-expect-error ts-migrate(2362) FIXME: The left-hand side of an arithmetic operation must... Remove this comment to see the full error message
|
||||
height: containerHeight - paddingBottom,
|
||||
overflow: 'auto',
|
||||
}}
|
||||
|
@ -234,7 +264,7 @@ function Conn({ apiConfig }) {
|
|||
);
|
||||
}
|
||||
|
||||
const mapState = (s) => ({
|
||||
const mapState = (s: State) => ({
|
||||
apiConfig: getClashAPIConfig(s),
|
||||
});
|
||||
|
|
@ -1,9 +1,13 @@
|
|||
import PropTypes from 'prop-types';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import s0 from './ContentHeader.module.css';
|
||||
|
||||
function ContentHeader({ title }) {
|
||||
type Props = {
|
||||
title: string;
|
||||
};
|
||||
|
||||
function ContentHeader({ title }: Props) {
|
||||
return (
|
||||
<div className={s0.root}>
|
||||
<h1 className={s0.h1}>{title}</h1>
|
||||
|
@ -11,8 +15,4 @@ function ContentHeader({ title }) {
|
|||
);
|
||||
}
|
||||
|
||||
ContentHeader.propTypes = {
|
||||
title: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
export default React.memo(ContentHeader);
|
|
@ -1,18 +1,21 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import * as React from 'react';
|
||||
|
||||
// import { getSentry } from '../misc/sentry';
|
||||
import { deriveMessageFromError } from '../misc/errors';
|
||||
import { deriveMessageFromError,Err } from '../misc/errors';
|
||||
import ErrorBoundaryFallback from './ErrorBoundaryFallback';
|
||||
|
||||
class ErrorBoundary extends Component {
|
||||
static propTypes = {
|
||||
children: PropTypes.node,
|
||||
};
|
||||
type Props = {
|
||||
children: React.ReactNode;
|
||||
};
|
||||
|
||||
type State = {
|
||||
error?: Err;
|
||||
};
|
||||
|
||||
class ErrorBoundary extends React.Component<Props, State> {
|
||||
state = { error: null };
|
||||
|
||||
static getDerivedStateFromError(error) {
|
||||
static getDerivedStateFromError(error: Err) {
|
||||
return { error };
|
||||
}
|
||||
|
|
@ -1,4 +1,3 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
|
||||
import s0 from './ErrorBoundaryFallback.module.css';
|
||||
|
@ -6,7 +5,12 @@ import SvgGithub from './SvgGithub';
|
|||
import SvgYacd from './SvgYacd';
|
||||
const yacdRepoIssueUrl = 'https://github.com/haishanh/yacd/issues';
|
||||
|
||||
function ErrorBoundaryFallback({ message, detail }) {
|
||||
type Props = {
|
||||
message?: string;
|
||||
detail?: string;
|
||||
};
|
||||
|
||||
function ErrorBoundaryFallback({ message, detail }: Props) {
|
||||
return (
|
||||
<div className={s0.root}>
|
||||
<div className={s0.yacd}>
|
||||
|
@ -24,8 +28,4 @@ function ErrorBoundaryFallback({ message, detail }) {
|
|||
);
|
||||
}
|
||||
|
||||
ErrorBoundaryFallback.propTypes = {
|
||||
message: PropTypes.string,
|
||||
};
|
||||
|
||||
export default ErrorBoundaryFallback;
|
|
@ -1,12 +1,19 @@
|
|||
import cx from 'clsx';
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
|
||||
import s from './Field.module.css';
|
||||
|
||||
const { useCallback } = React;
|
||||
|
||||
export default function Field({ id, label, value, onChange, ...props }) {
|
||||
type Props = {
|
||||
value?: string | number;
|
||||
type?: 'text' | 'number';
|
||||
onChange?: (...args: any[]) => any;
|
||||
id?: string;
|
||||
label?: string;
|
||||
};
|
||||
|
||||
export default function Field({ id, label, value, onChange, ...props }: Props) {
|
||||
const valueOnChange = useCallback((e) => onChange(e), [onChange]);
|
||||
const labelClassName = cx({
|
||||
[s.floatAbove]: typeof value === 'string' && value !== '',
|
||||
|
@ -20,11 +27,3 @@ export default function Field({ id, label, value, onChange, ...props }) {
|
|||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Field.propTypes = {
|
||||
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
||||
type: PropTypes.oneOf(['text', 'number']),
|
||||
onChange: PropTypes.func,
|
||||
id: PropTypes.string,
|
||||
label: PropTypes.string,
|
||||
};
|
|
@ -1,8 +1,14 @@
|
|||
import cx from 'clsx';
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
|
||||
const Icon = ({ id, width = 20, height = 20, className, ...props }) => {
|
||||
type Props = {
|
||||
id: string;
|
||||
width?: number;
|
||||
height?: number;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
const Icon = ({ id, width = 20, height = 20, className, ...props }: Props) => {
|
||||
const c = cx('icon', id, className);
|
||||
const href = '#' + id;
|
||||
return (
|
||||
|
@ -12,11 +18,4 @@ const Icon = ({ id, width = 20, height = 20, className, ...props }) => {
|
|||
);
|
||||
};
|
||||
|
||||
Icon.propTypes = {
|
||||
id: PropTypes.string.isRequired,
|
||||
width: PropTypes.number,
|
||||
height: PropTypes.number,
|
||||
className: PropTypes.string,
|
||||
};
|
||||
|
||||
export default React.memo(Icon);
|
|
@ -1,11 +1,19 @@
|
|||
import PropTypes from 'prop-types';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import s0 from './Input.module.css';
|
||||
|
||||
const { useState, useRef, useEffect, useCallback } = React;
|
||||
|
||||
export default function Input(props) {
|
||||
type InputProps = {
|
||||
value?: string | number;
|
||||
type?: string;
|
||||
onChange?: (...args: any[]) => any;
|
||||
name?: string;
|
||||
placeholder?: string;
|
||||
};
|
||||
|
||||
export default function Input(props: InputProps) {
|
||||
return <input className={s0.input} {...props} />;
|
||||
}
|
||||
|
||||
|
@ -32,11 +40,3 @@ export function SelfControlledInput({ value, ...restProps }) {
|
|||
/>
|
||||
);
|
||||
}
|
||||
|
||||
Input.propTypes = {
|
||||
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
||||
type: PropTypes.string,
|
||||
onChange: PropTypes.func,
|
||||
name: PropTypes.string,
|
||||
placeholder: PropTypes.string,
|
||||
};
|
|
@ -1,9 +1,13 @@
|
|||
import PropTypes from 'prop-types';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import s0 from './Loading.module.css';
|
||||
|
||||
const Loading = ({ height }) => {
|
||||
type Props = {
|
||||
height?: string;
|
||||
};
|
||||
|
||||
const Loading = ({ height }: Props) => {
|
||||
const style = height ? { height } : {};
|
||||
return (
|
||||
<div className={s0.loading} style={style}>
|
||||
|
@ -12,8 +16,4 @@ const Loading = ({ height }) => {
|
|||
);
|
||||
};
|
||||
|
||||
Loading.propTypes = {
|
||||
height: PropTypes.string,
|
||||
};
|
||||
|
||||
export default Loading;
|
|
@ -1,5 +1,4 @@
|
|||
import cx from 'clsx';
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import { areEqual, FixedSizeList as List } from 'react-window';
|
||||
|
||||
|
@ -26,7 +25,14 @@ const colors = {
|
|||
error: '#c11c1c',
|
||||
};
|
||||
|
||||
function LogLine({ time, even, payload, type }) {
|
||||
type LogLineProps = {
|
||||
time?: string;
|
||||
even?: boolean;
|
||||
payload?: string;
|
||||
type?: string;
|
||||
};
|
||||
|
||||
function LogLine({ time, even, payload, type }: LogLineProps) {
|
||||
const className = cx({ even }, s0.log);
|
||||
return (
|
||||
<div className={className}>
|
||||
|
@ -41,18 +47,12 @@ function LogLine({ time, even, payload, type }) {
|
|||
);
|
||||
}
|
||||
|
||||
LogLine.propTypes = {
|
||||
time: PropTypes.string,
|
||||
even: PropTypes.bool,
|
||||
payload: PropTypes.string,
|
||||
type: PropTypes.string,
|
||||
};
|
||||
|
||||
function itemKey(index, data) {
|
||||
const item = data[index];
|
||||
return item.id;
|
||||
}
|
||||
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'index' does not exist on type '{ childre... Remove this comment to see the full error message
|
||||
const Row = memo(({ index, style, data }) => {
|
||||
const r = data[index];
|
||||
return (
|
||||
|
@ -78,10 +78,12 @@ function Logs({ dispatch, logLevel, apiConfig, logs }) {
|
|||
<div>
|
||||
<ContentHeader title="Logs" />
|
||||
<LogSearch />
|
||||
{/* @ts-expect-error ts-migrate(2322) FIXME: Type 'number | MutableRefObject<any>' is not assig... Remove this comment to see the full error message */}
|
||||
<div ref={refLogsContainer} style={{ paddingBottom }}>
|
||||
{logs.length === 0 ? (
|
||||
<div
|
||||
className={s0.logPlaceholder}
|
||||
// @ts-expect-error ts-migrate(2362) FIXME: The left-hand side of an arithmetic operation must... Remove this comment to see the full error message
|
||||
style={{ height: containerHeight - paddingBottom }}
|
||||
>
|
||||
<div className={s0.logPlaceholderIcon}>
|
||||
|
@ -92,6 +94,7 @@ function Logs({ dispatch, logLevel, apiConfig, logs }) {
|
|||
) : (
|
||||
<div className={s0.logsWrapper}>
|
||||
<List
|
||||
// @ts-expect-error ts-migrate(2362) FIXME: The left-hand side of an arithmetic operation must... Remove this comment to see the full error message
|
||||
height={containerHeight - paddingBottom}
|
||||
width="100%"
|
||||
itemCount={logs.length}
|
|
@ -1,18 +1,18 @@
|
|||
import cx from 'clsx';
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import Modal from 'react-modal';
|
||||
|
||||
import s0 from './Modal.module.css';
|
||||
|
||||
function ModalAPIConfig({
|
||||
isOpen,
|
||||
onRequestClose,
|
||||
className,
|
||||
overlayClassName,
|
||||
children,
|
||||
...otherProps
|
||||
}) {
|
||||
type Props = {
|
||||
isOpen: boolean;
|
||||
onRequestClose: (...args: any[]) => any;
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
overlayClassName?: string;
|
||||
};
|
||||
|
||||
function ModalAPIConfig({ isOpen, onRequestClose, className, overlayClassName, children, ...otherProps }: Props) {
|
||||
const contentCls = cx(className, s0.content);
|
||||
const overlayCls = cx(overlayClassName, s0.overlay);
|
||||
return (
|
||||
|
@ -28,12 +28,4 @@ function ModalAPIConfig({
|
|||
);
|
||||
}
|
||||
|
||||
ModalAPIConfig.propTypes = {
|
||||
isOpen: PropTypes.bool.isRequired,
|
||||
onRequestClose: PropTypes.func.isRequired,
|
||||
children: PropTypes.node.isRequired,
|
||||
className: PropTypes.string,
|
||||
overlayClassName: PropTypes.string,
|
||||
};
|
||||
|
||||
export default React.memo(ModalAPIConfig);
|
|
@ -1,4 +1,4 @@
|
|||
import PropTypes from 'prop-types';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import s0 from './Rule.module.css';
|
||||
|
@ -17,7 +17,14 @@ function getStyleFor({ proxy }) {
|
|||
return { color };
|
||||
}
|
||||
|
||||
function Rule({ type, payload, proxy, id }) {
|
||||
type Props = {
|
||||
id?: number;
|
||||
type?: string;
|
||||
payload?: string;
|
||||
proxy?: string;
|
||||
};
|
||||
|
||||
function Rule({ type, payload, proxy, id }: Props) {
|
||||
const styleProxy = getStyleFor({ proxy });
|
||||
return (
|
||||
<div className={s0.rule}>
|
||||
|
@ -33,11 +40,4 @@ function Rule({ type, payload, proxy, id }) {
|
|||
);
|
||||
}
|
||||
|
||||
Rule.propTypes = {
|
||||
id: PropTypes.number,
|
||||
type: PropTypes.string,
|
||||
payload: PropTypes.string,
|
||||
proxy: PropTypes.string,
|
||||
};
|
||||
|
||||
export default Rule;
|
|
@ -43,6 +43,7 @@ function getItemSizeFactory({ provider }) {
|
|||
};
|
||||
}
|
||||
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'index' does not exist on type '{ childre... Remove this comment to see the full error message
|
||||
const Row = memo(({ index, style, data }) => {
|
||||
const { rules, provider, apiConfig } = data;
|
||||
const providerQty = provider.names.length;
|
||||
|
@ -110,6 +111,7 @@ function Rules({ apiConfig }) {
|
|||
const { rules, provider } = useRuleAndProvider(apiConfig);
|
||||
const invalidateQueries = useInvalidateQueries();
|
||||
|
||||
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type '{ rules: RuleItem[]; provider: {... Remove this comment to see the full error message
|
||||
const getItemSize = getItemSizeFactory({ rules, provider });
|
||||
|
||||
return (
|
||||
|
@ -118,8 +120,10 @@ function Rules({ apiConfig }) {
|
|||
<ContentHeader title="Rules" />
|
||||
<TextFilter />
|
||||
</div>
|
||||
{/* @ts-expect-error ts-migrate(2322) FIXME: Type 'number | MutableRefObject<any>' is not assig... Remove this comment to see the full error message */}
|
||||
<div ref={refRulesContainer} style={{ paddingBottom }}>
|
||||
<VariableSizeList
|
||||
// @ts-expect-error ts-migrate(2362) FIXME: The left-hand side of an arithmetic operation must... Remove this comment to see the full error message
|
||||
height={containerHeight - paddingBottom}
|
||||
width="100%"
|
||||
itemCount={rules.length + provider.names.length}
|
|
@ -1,15 +1,16 @@
|
|||
import cx from 'clsx';
|
||||
import { array, func, number } from 'prop-types';
|
||||
import React from 'react';
|
||||
|
||||
import s from './Selection.module.css';
|
||||
|
||||
export default function Selection({
|
||||
OptionComponent,
|
||||
optionPropsList,
|
||||
selectedIndex,
|
||||
onChange,
|
||||
}) {
|
||||
type SelectionProps = {
|
||||
OptionComponent?: (...args: any[]) => any;
|
||||
optionPropsList?: any[];
|
||||
selectedIndex?: number;
|
||||
onChange?: (...args: any[]) => any;
|
||||
};
|
||||
|
||||
export default function Selection({ OptionComponent, optionPropsList, selectedIndex, onChange, }: SelectionProps) {
|
||||
return (
|
||||
<div className={s.root}>
|
||||
{optionPropsList.map((props, idx) => {
|
||||
|
@ -35,14 +36,8 @@ export default function Selection({
|
|||
);
|
||||
}
|
||||
|
||||
Selection.propTypes = {
|
||||
OptionComponent: func,
|
||||
optionPropsList: array,
|
||||
selectedIndex: number,
|
||||
onChange: func,
|
||||
};
|
||||
|
||||
// for test
|
||||
// @ts-expect-error ts-migrate(7030) FIXME: Not all code paths return a value.
|
||||
export function Option({ title }) {
|
||||
// eslint-disable-next-line no-undef
|
||||
if (__DEV__) {
|
|
@ -40,9 +40,13 @@ const icons = {
|
|||
};
|
||||
|
||||
const SideBarRow = React.memo(function SideBarRow({
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'isActive' does not exist on type '{ chil... Remove this comment to see the full error message
|
||||
isActive,
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'to' does not exist on type '{ children?:... Remove this comment to see the full error message
|
||||
to,
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'iconId' does not exist on type '{ childr... Remove this comment to see the full error message
|
||||
iconId,
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'labelText' does not exist on type '{ chi... Remove this comment to see the full error message
|
||||
labelText,
|
||||
}) {
|
||||
const Comp = icons[iconId];
|
||||
|
@ -55,6 +59,7 @@ const SideBarRow = React.memo(function SideBarRow({
|
|||
);
|
||||
});
|
||||
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'propTypes' does not exist on type 'Named... Remove this comment to see the full error message
|
||||
SideBarRow.propTypes = {
|
||||
isActive: PropTypes.bool.isRequired,
|
||||
to: PropTypes.string.isRequired,
|
||||
|
@ -107,6 +112,7 @@ function SideBar({ dispatch, theme }) {
|
|||
{pages.map(({ to, iconId, labelText }) => (
|
||||
<SideBarRow
|
||||
key={to}
|
||||
// @ts-expect-error ts-migrate(2322) FIXME: Type '{ key: string; to: string; isActive: boolean... Remove this comment to see the full error message
|
||||
to={to}
|
||||
isActive={location.pathname === to}
|
||||
iconId={iconId}
|
|
@ -42,6 +42,7 @@ export default function Provider({ initialState, actions = {}, children }) {
|
|||
const getState = useCallback(() => stateRef.current, []);
|
||||
useEffect(() => {
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'getState2' does not exist on type 'Windo... Remove this comment to see the full error message
|
||||
window.getState2 = getState;
|
||||
}
|
||||
}, [getState]);
|
||||
|
@ -94,6 +95,7 @@ export function connect(mapStateToProps) {
|
|||
// steal from https://github.com/reduxjs/redux/blob/master/src/bindActionCreators.ts
|
||||
function bindAction(action, dispatch) {
|
||||
return function (...args) {
|
||||
// @ts-expect-error ts-migrate(2683) FIXME: 'this' implicitly has type 'any' because it does n... Remove this comment to see the full error message
|
||||
return dispatch(action.apply(this, args));
|
||||
};
|
||||
}
|
|
@ -51,12 +51,15 @@ class StyleGuide extends PureComponent {
|
|||
render() {
|
||||
return (
|
||||
<div>
|
||||
{/* @ts-expect-error ts-migrate(2741) FIXME: Property 'style' is missing in type '{ children: E... Remove this comment to see the full error message */}
|
||||
<Pane>
|
||||
<SwitchExample />
|
||||
</Pane>
|
||||
{/* @ts-expect-error ts-migrate(2741) FIXME: Property 'style' is missing in type '{ children: E... Remove this comment to see the full error message */}
|
||||
<Pane>
|
||||
<Input />
|
||||
</Pane>
|
||||
{/* @ts-expect-error ts-migrate(2741) FIXME: Property 'style' is missing in type '{ children: E... Remove this comment to see the full error message */}
|
||||
<Pane>
|
||||
<ToggleSwitch
|
||||
name="test"
|
||||
|
@ -65,6 +68,7 @@ class StyleGuide extends PureComponent {
|
|||
onChange={noop}
|
||||
/>
|
||||
</Pane>
|
||||
{/* @ts-expect-error ts-migrate(2741) FIXME: Property 'style' is missing in type '{ children: E... Remove this comment to see the full error message */}
|
||||
<Pane>
|
||||
<Button text="Test Lxatency" start={<Zap size={16} />} />
|
||||
<Button text="Test Lxatency" start={<Zap size={16} />} isLoading />
|
|
@ -1,7 +1,12 @@
|
|||
import PropTypes from 'prop-types';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
export default function SvgGithub({ width = 24, height = 24 } = {}) {
|
||||
type Props = {
|
||||
width?: number;
|
||||
height?: number;
|
||||
};
|
||||
|
||||
export default function SvgGithub({ width = 24, height = 24 }: Props = {}) {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
|
@ -18,8 +23,3 @@ export default function SvgGithub({ width = 24, height = 24 } = {}) {
|
|||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
SvgGithub.propTypes = {
|
||||
width: PropTypes.number,
|
||||
height: PropTypes.number,
|
||||
};
|
|
@ -1,16 +1,23 @@
|
|||
import cx from 'clsx';
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
|
||||
import s from './SvgYacd.module.css';
|
||||
|
||||
type Props = {
|
||||
width?: number;
|
||||
height?: number;
|
||||
animate?: boolean;
|
||||
c0?: string;
|
||||
c1?: string;
|
||||
};
|
||||
|
||||
function SvgYacd({
|
||||
width = 320,
|
||||
height = 320,
|
||||
animate = false,
|
||||
c0 = 'currentColor',
|
||||
c1 = '#eee',
|
||||
}) {
|
||||
}: Props) {
|
||||
const faceClasName = cx({ [s.path]: animate });
|
||||
return (
|
||||
<svg
|
||||
|
@ -40,9 +47,4 @@ function SvgYacd({
|
|||
);
|
||||
}
|
||||
|
||||
SvgYacd.propTypes = {
|
||||
width: PropTypes.number,
|
||||
height: PropTypes.number,
|
||||
};
|
||||
|
||||
export default SvgYacd;
|
|
@ -1,15 +1,23 @@
|
|||
import PropTypes from 'prop-types';
|
||||
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
|
||||
import s0 from './ToggleSwitch.module.css';
|
||||
|
||||
function ToggleSwitch({ options, value, name, onChange }) {
|
||||
type Props = {
|
||||
options?: any[];
|
||||
value?: string;
|
||||
name?: string;
|
||||
onChange?: (...args: any[]) => any;
|
||||
};
|
||||
|
||||
function ToggleSwitch({ options, value, name, onChange }: Props) {
|
||||
const idxSelected = useMemo(
|
||||
() => options.map((o) => o.value).indexOf(value),
|
||||
[options, value]
|
||||
);
|
||||
|
||||
const getPortionPercentage = useCallback(
|
||||
// @ts-expect-error ts-migrate(7030) FIXME: Not all code paths return a value.
|
||||
(idx) => {
|
||||
const w = Math.floor(100 / options.length);
|
||||
if (idx === options.length - 1) {
|
||||
|
@ -59,11 +67,4 @@ function ToggleSwitch({ options, value, name, onChange }) {
|
|||
);
|
||||
}
|
||||
|
||||
ToggleSwitch.propTypes = {
|
||||
options: PropTypes.array,
|
||||
value: PropTypes.string,
|
||||
name: PropTypes.string,
|
||||
onChange: PropTypes.func,
|
||||
};
|
||||
|
||||
export default React.memo(ToggleSwitch);
|
|
@ -50,6 +50,7 @@ function TrafficChart({ apiConfig, selectedChartStyleIndex }) {
|
|||
useLineChart(Chart, 'trafficChart', data, traffic);
|
||||
|
||||
return (
|
||||
// @ts-expect-error ts-migrate(2322) FIXME: Type '{ position: string; maxWidth: number; }' is ... Remove this comment to see the full error message
|
||||
<div style={chartWrapperStyle}>
|
||||
<canvas id="trafficChart" />
|
||||
</div>
|
|
@ -1,6 +1,7 @@
|
|||
import { formatDistance } from 'date-fns';
|
||||
import * as React from 'react';
|
||||
import { RotateCw, Zap } from 'react-feather';
|
||||
import { DelayMapping } from 'src/store/types';
|
||||
|
||||
import { framerMotionResouce } from '../../misc/motion';
|
||||
import {
|
||||
|
@ -10,7 +11,6 @@ import {
|
|||
getProxySortBy,
|
||||
} from '../../store/app';
|
||||
import {
|
||||
DelayMapping,
|
||||
getDelay,
|
||||
healthcheckProviderByName,
|
||||
updateProviderByName,
|
||||
|
@ -89,6 +89,7 @@ function ProxyProviderImpl({
|
|||
<div className={s.updatedAt}>
|
||||
<small>Updated {timeAgo} ago</small>
|
||||
</div>
|
||||
{/* @ts-expect-error ts-migrate(2322) FIXME: Type '{ children: Element[]; isOpen: boolean; }' i... Remove this comment to see the full error message */}
|
||||
<Collapsible isOpen={isOpen}>
|
||||
<ProxyList all={proxies} />
|
||||
<div className={s.actionFooter}>
|
||||
|
@ -101,6 +102,7 @@ function ProxyProviderImpl({
|
|||
/>
|
||||
</div>
|
||||
</Collapsible>
|
||||
{/* @ts-expect-error ts-migrate(2322) FIXME: Type '{ children: Element; isOpen: boolean; }' is ... Remove this comment to see the full error message */}
|
||||
<Collapsible isOpen={!isOpen}>
|
||||
<ProxyListSummaryView all={proxies} />
|
||||
</Collapsible>
|
||||
|
|
|
@ -1,14 +1,12 @@
|
|||
import * as React from 'react';
|
||||
import { useRecoilState } from 'recoil';
|
||||
import { DelayMapping, ProxiesMapping, ProxyItem } from 'src/store/types';
|
||||
|
||||
import {
|
||||
// types
|
||||
DelayMapping,
|
||||
NonProxyTypes,
|
||||
ProxiesMapping,
|
||||
// atom
|
||||
proxyFilterText,
|
||||
ProxyItem,
|
||||
} from '../../store/proxies';
|
||||
|
||||
const { useMemo } = React;
|
||||
|
|
3
src/custom.d.ts
vendored
3
src/custom.d.ts
vendored
|
@ -1,3 +1,6 @@
|
|||
/// <reference types="react/experimental" />
|
||||
/// <reference types="react-dom/experimental" />
|
||||
|
||||
// for css modules
|
||||
declare module '*.module.css' {
|
||||
const classes: { [key: string]: string };
|
||||
|
|
|
@ -13,6 +13,7 @@ export default function useLineChart(
|
|||
extraChartOptions = {}
|
||||
) {
|
||||
useEffect(() => {
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'getContext' does not exist on type 'HTML... Remove this comment to see the full error message
|
||||
const ctx = document.getElementById(elementId).getContext('2d');
|
||||
const c = new Chart(ctx, {
|
||||
type: 'line',
|
|
@ -3,14 +3,16 @@ export const DOES_NOT_SUPPORT_FETCH = 0;
|
|||
export const errors = {
|
||||
[DOES_NOT_SUPPORT_FETCH]: {
|
||||
message: 'Browser not supported!',
|
||||
detail: 'This browser does not support "fetch", please choose another one.'
|
||||
detail: 'This browser does not support "fetch", please choose another one.',
|
||||
},
|
||||
default: {
|
||||
message: 'Oops, something went wrong!'
|
||||
}
|
||||
message: 'Oops, something went wrong!',
|
||||
},
|
||||
};
|
||||
|
||||
export function deriveMessageFromError(err) {
|
||||
export type Err = { code: number };
|
||||
|
||||
export function deriveMessageFromError(err: Err) {
|
||||
const { code } = err;
|
||||
if (typeof code === 'number') {
|
||||
return errors[code];
|
|
@ -1,16 +0,0 @@
|
|||
// steal from https://github.com/sindresorhus/pretty-bytes/blob/master/index.js
|
||||
|
||||
const UNITS = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
|
||||
|
||||
export default number => {
|
||||
if (number < 1000) {
|
||||
return number + ' B';
|
||||
}
|
||||
const exponent = Math.min(
|
||||
Math.floor(Math.log10(number) / 3),
|
||||
UNITS.length - 1
|
||||
);
|
||||
number = Number((number / Math.pow(1000, exponent)).toPrecision(3));
|
||||
const unit = UNITS[exponent];
|
||||
return number + ' ' + unit;
|
||||
};
|
13
src/misc/pretty-bytes.ts
Normal file
13
src/misc/pretty-bytes.ts
Normal file
|
@ -0,0 +1,13 @@
|
|||
// steal from https://github.com/sindresorhus/pretty-bytes/blob/master/index.js
|
||||
|
||||
const UNITS = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
|
||||
|
||||
export default function prettyBytes(n: number) {
|
||||
if (n < 1000) {
|
||||
return n + ' B';
|
||||
}
|
||||
const exponent = Math.min(Math.floor(Math.log10(n) / 3), UNITS.length - 1);
|
||||
n = Number((n / Math.pow(1000, exponent)).toPrecision(3));
|
||||
const unit = UNITS[exponent];
|
||||
return n + ' ' + unit;
|
||||
}
|
|
@ -19,7 +19,7 @@ export function debounce<T extends any[]>(
|
|||
fn: (...args: T) => unknown,
|
||||
timeout: number
|
||||
) {
|
||||
let timeoutId: number;
|
||||
let timeoutId: ReturnType<typeof setTimeout>;
|
||||
return (...args: T) => {
|
||||
if (timeoutId) clearTimeout(timeoutId);
|
||||
timeoutId = setTimeout(() => {
|
||||
|
|
|
@ -1,26 +1,31 @@
|
|||
import { DispatchFn, GetStateFn, State, StateApp } from 'src/store/types';
|
||||
|
||||
import { loadState, saveState } from '../misc/storage';
|
||||
import { debounce, trimTrailingSlash } from '../misc/utils';
|
||||
import { fetchConfigs } from './configs';
|
||||
import { closeModal } from './modals';
|
||||
|
||||
export const getClashAPIConfig = (s) => {
|
||||
export const getClashAPIConfig = (s: State) => {
|
||||
const idx = s.app.selectedClashAPIConfigIndex;
|
||||
return s.app.clashAPIConfigs[idx];
|
||||
};
|
||||
export const getSelectedClashAPIConfigIndex = (s) =>
|
||||
export const getSelectedClashAPIConfigIndex = (s: State) =>
|
||||
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;
|
||||
export const getCollapsibleIsOpen = (s) => s.app.collapsibleIsOpen;
|
||||
export const getProxySortBy = (s) => s.app.proxySortBy;
|
||||
export const getHideUnavailableProxies = (s) => s.app.hideUnavailableProxies;
|
||||
export const getAutoCloseOldConns = (s) => s.app.autoCloseOldConns;
|
||||
export const getClashAPIConfigs = (s: State) => s.app.clashAPIConfigs;
|
||||
export const getTheme = (s: State) => s.app.theme;
|
||||
export const getSelectedChartStyleIndex = (s: State) =>
|
||||
s.app.selectedChartStyleIndex;
|
||||
export const getLatencyTestUrl = (s: State) => s.app.latencyTestUrl;
|
||||
export const getCollapsibleIsOpen = (s: State) => s.app.collapsibleIsOpen;
|
||||
export const getProxySortBy = (s: State) => s.app.proxySortBy;
|
||||
export const getHideUnavailableProxies = (s: State) =>
|
||||
s.app.hideUnavailableProxies;
|
||||
export const getAutoCloseOldConns = (s: State) => s.app.autoCloseOldConns;
|
||||
|
||||
const saveStateDebounced = debounce(saveState, 600);
|
||||
|
||||
function findClashAPIConfigIndex(getState, { baseURL, secret }) {
|
||||
// @ts-expect-error ts-migrate(7030) FIXME: Not all code paths return a value.
|
||||
function findClashAPIConfigIndex(getState: GetStateFn, { baseURL, secret }) {
|
||||
const arr = getClashAPIConfigs(getState());
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
const x = arr[i];
|
||||
|
@ -29,7 +34,7 @@ function findClashAPIConfigIndex(getState, { baseURL, secret }) {
|
|||
}
|
||||
|
||||
export function addClashAPIConfig({ baseURL, secret }) {
|
||||
return async (dispatch, getState) => {
|
||||
return async (dispatch: DispatchFn, getState: GetStateFn) => {
|
||||
const idx = findClashAPIConfigIndex(getState, { baseURL, secret });
|
||||
// already exists
|
||||
if (idx) return;
|
||||
|
@ -44,7 +49,7 @@ export function addClashAPIConfig({ baseURL, secret }) {
|
|||
}
|
||||
|
||||
export function removeClashAPIConfig({ baseURL, secret }) {
|
||||
return async (dispatch, getState) => {
|
||||
return async (dispatch: DispatchFn, getState: GetStateFn) => {
|
||||
const idx = findClashAPIConfigIndex(getState, { baseURL, secret });
|
||||
dispatch('removeClashAPIConfig', (s) => {
|
||||
s.app.clashAPIConfigs = [
|
||||
|
@ -58,7 +63,7 @@ export function removeClashAPIConfig({ baseURL, secret }) {
|
|||
}
|
||||
|
||||
export function selectClashAPIConfig({ baseURL, secret }) {
|
||||
return async (dispatch, getState) => {
|
||||
return async (dispatch: DispatchFn, getState: GetStateFn) => {
|
||||
const idx = findClashAPIConfigIndex(getState, { baseURL, secret });
|
||||
const curr = getSelectedClashAPIConfigIndex(getState());
|
||||
if (curr !== idx) {
|
||||
|
@ -81,7 +86,7 @@ export function selectClashAPIConfig({ baseURL, secret }) {
|
|||
|
||||
// unused
|
||||
export function updateClashAPIConfig({ baseURL, secret }) {
|
||||
return async (dispatch, getState) => {
|
||||
return async (dispatch: DispatchFn, getState: GetStateFn) => {
|
||||
const clashAPIConfig = { baseURL, secret };
|
||||
dispatch('appUpdateClashAPIConfig', (s) => {
|
||||
s.app.clashAPIConfigs[0] = clashAPIConfig;
|
||||
|
@ -105,7 +110,7 @@ function setTheme(theme = 'dark') {
|
|||
}
|
||||
|
||||
export function switchTheme() {
|
||||
return (dispatch, getState) => {
|
||||
return (dispatch: DispatchFn, getState: GetStateFn) => {
|
||||
const currentTheme = getTheme(getState());
|
||||
const theme = currentTheme === 'light' ? 'dark' : 'light';
|
||||
// side effect
|
||||
|
@ -118,8 +123,8 @@ export function switchTheme() {
|
|||
};
|
||||
}
|
||||
|
||||
export function selectChartStyleIndex(selectedChartStyleIndex) {
|
||||
return (dispatch, getState) => {
|
||||
export function selectChartStyleIndex(selectedChartStyleIndex: number) {
|
||||
return (dispatch: DispatchFn, getState: GetStateFn) => {
|
||||
dispatch('appSelectChartStyleIndex', (s) => {
|
||||
s.app.selectedChartStyleIndex = selectedChartStyleIndex;
|
||||
});
|
||||
|
@ -128,8 +133,8 @@ export function selectChartStyleIndex(selectedChartStyleIndex) {
|
|||
};
|
||||
}
|
||||
|
||||
export function updateAppConfig(name, value) {
|
||||
return (dispatch, getState) => {
|
||||
export function updateAppConfig(name: string, value: unknown) {
|
||||
return (dispatch: DispatchFn, getState: GetStateFn) => {
|
||||
dispatch('appUpdateAppConfig', (s) => {
|
||||
s.app[name] = value;
|
||||
});
|
||||
|
@ -138,9 +143,13 @@ export function updateAppConfig(name, value) {
|
|||
};
|
||||
}
|
||||
|
||||
export function updateCollapsibleIsOpen(prefix, name, v) {
|
||||
return (dispatch, getState) => {
|
||||
dispatch('updateCollapsibleIsOpen', (s) => {
|
||||
export function updateCollapsibleIsOpen(
|
||||
prefix: string,
|
||||
name: string,
|
||||
v: boolean
|
||||
) {
|
||||
return (dispatch: DispatchFn, getState: GetStateFn) => {
|
||||
dispatch('updateCollapsibleIsOpen', (s: State) => {
|
||||
s.app.collapsibleIsOpen[`${prefix}:${name}`] = v;
|
||||
});
|
||||
// side effect
|
||||
|
@ -154,7 +163,7 @@ const defaultClashAPIConfig = {
|
|||
addedAt: 0,
|
||||
};
|
||||
// type Theme = 'light' | 'dark';
|
||||
const defaultState = {
|
||||
const defaultState: StateApp = {
|
||||
selectedClashAPIConfigIndex: 0,
|
||||
clashAPIConfigs: [defaultClashAPIConfig],
|
||||
|
||||
|
@ -172,7 +181,7 @@ const defaultState = {
|
|||
|
||||
function parseConfigQueryString() {
|
||||
const { search } = window.location;
|
||||
const collector = {};
|
||||
const collector: Record<string, string> = {};
|
||||
if (typeof search !== 'string' || search === '') return collector;
|
||||
const qs = search.replace(/^\?/, '').split('&');
|
||||
for (let i = 0; i < qs.length; i++) {
|
|
@ -1,13 +1,23 @@
|
|||
import {
|
||||
ClashGeneralConfig,
|
||||
DispatchFn,
|
||||
GetStateFn,
|
||||
State,
|
||||
StateConfigs,
|
||||
} from 'src/store/types';
|
||||
import { ClashAPIConfig } from 'src/types';
|
||||
|
||||
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: State) => s.configs.configs;
|
||||
export const getHaveFetched = (s: State) => s.configs.haveFetchedConfig;
|
||||
export const getLogLevel = (s: State) => s.configs.configs['log-level'];
|
||||
|
||||
export function fetchConfigs(apiConfig) {
|
||||
return async (dispatch, getState) => {
|
||||
let res;
|
||||
export function fetchConfigs(apiConfig: ClashAPIConfig) {
|
||||
return async (dispatch: DispatchFn, getState: GetStateFn) => {
|
||||
let res: Response;
|
||||
try {
|
||||
res = await configsAPI.fetchConfigs(apiConfig);
|
||||
} catch (err) {
|
||||
|
@ -28,9 +38,9 @@ export function fetchConfigs(apiConfig) {
|
|||
s.configs.configs = payload;
|
||||
});
|
||||
|
||||
const configsCurr = getConfigs(getState());
|
||||
const haveFetchedConfig = getHaveFetched(getState());
|
||||
|
||||
if (configsCurr.haveFetchedConfig) {
|
||||
if (haveFetchedConfig) {
|
||||
// normally user will land on the "traffic chart" page first
|
||||
// calling this here will let the data start streaming
|
||||
// the traffic chart should already subscribed to the streaming
|
||||
|
@ -42,15 +52,18 @@ export function fetchConfigs(apiConfig) {
|
|||
}
|
||||
|
||||
function markHaveFetchedConfig() {
|
||||
return (dispatch) => {
|
||||
dispatch('store/configs#markHaveFetchedConfig', (s) => {
|
||||
return (dispatch: DispatchFn) => {
|
||||
dispatch('store/configs#markHaveFetchedConfig', (s: State) => {
|
||||
s.configs.haveFetchedConfig = true;
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export function updateConfigs(apiConfig, partialConfg) {
|
||||
return async (dispatch) => {
|
||||
export function updateConfigs(
|
||||
apiConfig: ClashAPIConfig,
|
||||
partialConfg: Partial<ClashGeneralConfig>
|
||||
) {
|
||||
return async (dispatch: DispatchFn) => {
|
||||
configsAPI
|
||||
.updateConfigs(apiConfig, partialConfg)
|
||||
.then(
|
||||
|
@ -76,7 +89,7 @@ export function updateConfigs(apiConfig, partialConfg) {
|
|||
};
|
||||
}
|
||||
|
||||
export const initialState = {
|
||||
export const initialState: StateConfigs = {
|
||||
configs: {
|
||||
port: 7890,
|
||||
'socks-port': 7891,
|
|
@ -1,58 +1,23 @@
|
|||
import { atom } from 'recoil';
|
||||
/* import { ProxyItem, ProxiesMapping, DelayMapping } from 'src/store/types'; */
|
||||
import {
|
||||
DispatchFn,
|
||||
FormattedProxyProvider,
|
||||
GetStateFn,
|
||||
ProxiesMapping,
|
||||
ProxyItem,
|
||||
ProxyProvider,
|
||||
State,
|
||||
StateProxies,
|
||||
SwitchProxyCtxItem,
|
||||
} from 'src/store/types';
|
||||
import { ClashAPIConfig } from 'src/types';
|
||||
|
||||
import * as connAPI from '../api/connections';
|
||||
import * as proxiesAPI from '../api/proxies';
|
||||
import { getAutoCloseOldConns, getLatencyTestUrl } from './app';
|
||||
|
||||
type PrimitiveProxyType = 'Shadowsocks' | 'Snell' | 'Socks5' | 'Http' | 'Vmess';
|
||||
|
||||
type LatencyHistory = Array<{ time: string; delay: number }>;
|
||||
|
||||
export type ProxyItem = {
|
||||
name: string;
|
||||
type: PrimitiveProxyType;
|
||||
history: LatencyHistory;
|
||||
all?: string[];
|
||||
now?: string;
|
||||
};
|
||||
|
||||
type ProxyProvider = {
|
||||
name: string;
|
||||
type: 'Proxy';
|
||||
updatedAt: string;
|
||||
vehicleType: 'HTTP' | 'File' | 'Compatible';
|
||||
proxies: Array<ProxyItem>;
|
||||
};
|
||||
|
||||
type FormattedProxyProvider = Omit<ProxyProvider, 'proxies'> & {
|
||||
proxies: string[];
|
||||
};
|
||||
|
||||
export type ProxiesMapping = Record<string, ProxyItem>;
|
||||
export type DelayMapping = Record<string, { number?: number }>;
|
||||
|
||||
type SwitchProxyCtxItem = { groupName: string; itemName: string };
|
||||
type SwitchProxyCtx = {
|
||||
to: SwitchProxyCtxItem;
|
||||
};
|
||||
|
||||
type ProxiesState = {
|
||||
proxies: ProxiesMapping;
|
||||
delay: DelayMapping;
|
||||
groupNames: string[];
|
||||
proxyProviders?: FormattedProxyProvider[];
|
||||
dangleProxyNames?: string[];
|
||||
|
||||
showModalClosePrevConns: boolean;
|
||||
switchProxyCtx?: SwitchProxyCtx;
|
||||
};
|
||||
|
||||
type GlobalState = {
|
||||
proxies: ProxiesState;
|
||||
};
|
||||
|
||||
export const initialState: ProxiesState = {
|
||||
export const initialState: StateProxies = {
|
||||
proxies: {},
|
||||
delay: {},
|
||||
groupNames: [],
|
||||
|
@ -78,14 +43,12 @@ export const NonProxyTypes = [
|
|||
'Unknown',
|
||||
];
|
||||
|
||||
export const getProxies = (s: GlobalState) => s.proxies.proxies;
|
||||
export const getDelay = (s: GlobalState) => s.proxies.delay;
|
||||
export const getProxyGroupNames = (s: GlobalState) => s.proxies.groupNames;
|
||||
export const getProxyProviders = (s: GlobalState) =>
|
||||
s.proxies.proxyProviders || [];
|
||||
export const getDangleProxyNames = (s: GlobalState) =>
|
||||
s.proxies.dangleProxyNames;
|
||||
export const getShowModalClosePrevConns = (s: GlobalState) =>
|
||||
export const getProxies = (s: State) => s.proxies.proxies;
|
||||
export const getDelay = (s: State) => s.proxies.delay;
|
||||
export const getProxyGroupNames = (s: State) => s.proxies.groupNames;
|
||||
export const getProxyProviders = (s: State) => s.proxies.proxyProviders || [];
|
||||
export const getDangleProxyNames = (s: State) => s.proxies.dangleProxyNames;
|
||||
export const getShowModalClosePrevConns = (s: State) =>
|
||||
s.proxies.showModalClosePrevConns;
|
||||
|
||||
export function fetchProxies(apiConfig: ClashAPIConfig) {
|
||||
|
@ -120,7 +83,7 @@ export function fetchProxies(apiConfig: ClashAPIConfig) {
|
|||
if (!providerProxies[v]) dangleProxyNames.push(v);
|
||||
}
|
||||
|
||||
dispatch('store/proxies#fetchProxies', (s: GlobalState) => {
|
||||
dispatch('store/proxies#fetchProxies', (s: State) => {
|
||||
s.proxies.proxies = proxies;
|
||||
s.proxies.groupNames = groupNames;
|
||||
s.proxies.delay = delayNext;
|
||||
|
@ -131,7 +94,7 @@ export function fetchProxies(apiConfig: ClashAPIConfig) {
|
|||
}
|
||||
|
||||
export function updateProviderByName(apiConfig: ClashAPIConfig, name: string) {
|
||||
return async (dispatch) => {
|
||||
return async (dispatch: DispatchFn) => {
|
||||
try {
|
||||
await proxiesAPI.updateProviderByName(apiConfig, name);
|
||||
} catch (x) {
|
||||
|
@ -143,7 +106,10 @@ export function updateProviderByName(apiConfig: ClashAPIConfig, name: string) {
|
|||
};
|
||||
}
|
||||
|
||||
async function healthcheckProviderByNameInternal(apiConfig, name) {
|
||||
async function healthcheckProviderByNameInternal(
|
||||
apiConfig: ClashAPIConfig,
|
||||
name: string
|
||||
) {
|
||||
try {
|
||||
await proxiesAPI.healthcheckProviderByName(apiConfig, name);
|
||||
} catch (x) {
|
||||
|
@ -151,8 +117,11 @@ async function healthcheckProviderByNameInternal(apiConfig, name) {
|
|||
}
|
||||
}
|
||||
|
||||
export function healthcheckProviderByName(apiConfig, name) {
|
||||
return async (dispatch) => {
|
||||
export function healthcheckProviderByName(
|
||||
apiConfig: ClashAPIConfig,
|
||||
name: string
|
||||
) {
|
||||
return async (dispatch: DispatchFn) => {
|
||||
await healthcheckProviderByNameInternal(apiConfig, name);
|
||||
// should be optimized
|
||||
// but ¯\_(ツ)_/¯
|
||||
|
@ -206,8 +175,8 @@ function resolveChain(
|
|||
}
|
||||
|
||||
async function switchProxyImpl(
|
||||
dispatch: any,
|
||||
getState: () => GlobalState,
|
||||
dispatch: DispatchFn,
|
||||
getState: GetStateFn,
|
||||
apiConfig: ClashAPIConfig,
|
||||
groupName: string,
|
||||
itemName: string
|
||||
|
@ -243,8 +212,8 @@ async function switchProxyImpl(
|
|||
}
|
||||
|
||||
function closeModalClosePrevConns() {
|
||||
return (dispatch) => {
|
||||
dispatch('closeModalClosePrevConns', (s: GlobalState) => {
|
||||
return (dispatch: DispatchFn) => {
|
||||
dispatch('closeModalClosePrevConns', (s: State) => {
|
||||
s.proxies.showModalClosePrevConns = false;
|
||||
});
|
||||
};
|
||||
|
@ -263,7 +232,7 @@ function closePrevConns(
|
|||
}
|
||||
|
||||
function closePrevConnsAndTheModal(apiConfig: ClashAPIConfig) {
|
||||
return async (dispatch, getState) => {
|
||||
return async (dispatch: DispatchFn, getState: GetStateFn) => {
|
||||
const s = getState();
|
||||
const switchTo = s.proxies.switchProxyCtx?.to;
|
||||
if (!switchTo) {
|
||||
|
@ -276,15 +245,19 @@ function closePrevConnsAndTheModal(apiConfig: ClashAPIConfig) {
|
|||
const proxies = s.proxies.proxies;
|
||||
closePrevConns(apiConfig, proxies, switchTo);
|
||||
|
||||
dispatch('closePrevConnsAndTheModal', (s: GlobalState) => {
|
||||
dispatch('closePrevConnsAndTheModal', (s: State) => {
|
||||
s.proxies.showModalClosePrevConns = false;
|
||||
s.proxies.switchProxyCtx = undefined;
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export function switchProxy(apiConfig, groupName, itemName) {
|
||||
return async (dispatch, getState) => {
|
||||
export function switchProxy(
|
||||
apiConfig: ClashAPIConfig,
|
||||
groupName: string,
|
||||
itemName: string
|
||||
) {
|
||||
return async (dispatch: DispatchFn, getState: GetStateFn) => {
|
||||
// switch proxy asynchronously
|
||||
switchProxyImpl(dispatch, getState, apiConfig, groupName, itemName).catch(
|
||||
noop
|
||||
|
@ -300,8 +273,8 @@ export function switchProxy(apiConfig, groupName, itemName) {
|
|||
};
|
||||
}
|
||||
|
||||
function requestDelayForProxyOnce(apiConfig, name) {
|
||||
return async (dispatch, getState) => {
|
||||
function requestDelayForProxyOnce(apiConfig: ClashAPIConfig, name: string) {
|
||||
return async (dispatch: DispatchFn, getState: GetStateFn) => {
|
||||
const latencyTestUrl = getLatencyTestUrl(getState());
|
||||
const res = await proxiesAPI.requestDelayForProxy(
|
||||
apiConfig,
|
||||
|
@ -329,14 +302,17 @@ function requestDelayForProxyOnce(apiConfig, name) {
|
|||
};
|
||||
}
|
||||
|
||||
export function requestDelayForProxy(apiConfig, name) {
|
||||
return async (dispatch) => {
|
||||
export function requestDelayForProxy(apiConfig: ClashAPIConfig, name: string) {
|
||||
return async (dispatch: DispatchFn) => {
|
||||
await dispatch(requestDelayForProxyOnce(apiConfig, name));
|
||||
};
|
||||
}
|
||||
|
||||
export function requestDelayForProxies(apiConfig, names) {
|
||||
return async (dispatch, getState) => {
|
||||
export function requestDelayForProxies(
|
||||
apiConfig: ClashAPIConfig,
|
||||
names: string[]
|
||||
) {
|
||||
return async (dispatch: DispatchFn, getState: GetStateFn) => {
|
||||
const proxyNames = getDangleProxyNames(getState());
|
||||
|
||||
const works = names
|
||||
|
@ -348,8 +324,8 @@ export function requestDelayForProxies(apiConfig, names) {
|
|||
};
|
||||
}
|
||||
|
||||
export function requestDelayAll(apiConfig) {
|
||||
return async (dispatch, getState) => {
|
||||
export function requestDelayAll(apiConfig: ClashAPIConfig) {
|
||||
return async (dispatch: DispatchFn, getState: GetStateFn) => {
|
||||
const proxyNames = getDangleProxyNames(getState());
|
||||
await Promise.all(
|
||||
proxyNames.map((p) => dispatch(requestDelayForProxy(apiConfig, p)))
|
||||
|
@ -363,9 +339,9 @@ export function requestDelayAll(apiConfig) {
|
|||
};
|
||||
}
|
||||
|
||||
function retrieveGroupNamesFrom(proxies) {
|
||||
function retrieveGroupNamesFrom(proxies: Record<string, ProxyItem>) {
|
||||
let groupNames = [];
|
||||
let globalAll;
|
||||
let globalAll: string[];
|
||||
const proxyNames = [];
|
||||
for (const prop in proxies) {
|
||||
const p = proxies[prop];
|
||||
|
|
91
src/store/types.ts
Normal file
91
src/store/types.ts
Normal file
|
@ -0,0 +1,91 @@
|
|||
import type { ClashAPIConfig } from 'src/types';
|
||||
|
||||
export type ClashAPIConfigWithAddedAt = ClashAPIConfig & { addedAt?: number };
|
||||
export type StateApp = {
|
||||
selectedClashAPIConfigIndex: number;
|
||||
clashAPIConfigs: ClashAPIConfigWithAddedAt[];
|
||||
|
||||
latencyTestUrl: string;
|
||||
selectedChartStyleIndex: number;
|
||||
theme: string;
|
||||
|
||||
collapsibleIsOpen: Record<string, boolean>;
|
||||
proxySortBy: string;
|
||||
hideUnavailableProxies: boolean;
|
||||
autoCloseOldConns: boolean;
|
||||
};
|
||||
|
||||
export type ClashGeneralConfig = {
|
||||
port: number;
|
||||
'socks-port': number;
|
||||
'redir-port': number;
|
||||
'allow-lan': boolean;
|
||||
mode: string;
|
||||
'log-level': string;
|
||||
};
|
||||
|
||||
///// store.proxies
|
||||
|
||||
type LatencyHistory = Array<{ time: string; delay: number }>;
|
||||
type PrimitiveProxyType = 'Shadowsocks' | 'Snell' | 'Socks5' | 'Http' | 'Vmess';
|
||||
export type ProxyItem = {
|
||||
name: string;
|
||||
type: PrimitiveProxyType;
|
||||
history: LatencyHistory;
|
||||
all?: string[];
|
||||
now?: string;
|
||||
};
|
||||
export type ProxiesMapping = Record<string, ProxyItem>;
|
||||
export type DelayMapping = Record<string, { number?: number }>;
|
||||
|
||||
export type ProxyProvider = {
|
||||
name: string;
|
||||
type: 'Proxy';
|
||||
updatedAt: string;
|
||||
vehicleType: 'HTTP' | 'File' | 'Compatible';
|
||||
proxies: Array<ProxyItem>;
|
||||
};
|
||||
|
||||
export type FormattedProxyProvider = Omit<ProxyProvider, 'proxies'> & {
|
||||
proxies: string[];
|
||||
};
|
||||
|
||||
export type SwitchProxyCtxItem = { groupName: string; itemName: string };
|
||||
type SwitchProxyCtx = {
|
||||
to: SwitchProxyCtxItem;
|
||||
};
|
||||
export type StateProxies = {
|
||||
proxies: ProxiesMapping;
|
||||
delay: DelayMapping;
|
||||
groupNames: string[];
|
||||
proxyProviders?: FormattedProxyProvider[];
|
||||
dangleProxyNames?: string[];
|
||||
|
||||
showModalClosePrevConns: boolean;
|
||||
switchProxyCtx?: SwitchProxyCtx;
|
||||
};
|
||||
|
||||
///// store.configs
|
||||
|
||||
export type StateConfigs = {
|
||||
configs: ClashGeneralConfig;
|
||||
haveFetchedConfig: boolean;
|
||||
};
|
||||
|
||||
//////
|
||||
|
||||
export type State = {
|
||||
app: StateApp;
|
||||
configs: StateConfigs;
|
||||
proxies: StateProxies;
|
||||
};
|
||||
export type GetStateFn = () => State;
|
||||
export interface DispatchFn {
|
||||
(msg: string, change: (s: State) => void): void;
|
||||
(
|
||||
action: (dispatch: DispatchFn, getState: GetStateFn) => Promise<void>
|
||||
): ReturnType<typeof action>;
|
||||
(action: (dispatch: DispatchFn, getState: GetStateFn) => void): ReturnType<
|
||||
typeof action
|
||||
>;
|
||||
}
|
|
@ -93,7 +93,7 @@ module.exports = {
|
|||
// https://webpack.js.org/configuration/devtool/
|
||||
devtool: isDev ? 'eval-source-map' : 'source-map',
|
||||
entry: {
|
||||
app: { import: ['./src/app.js'], dependOn: 'libs' },
|
||||
app: { import: ['./src/app.tsx'], dependOn: 'libs' },
|
||||
libs: { import: ['react-query', 'react-modal'], dependOn: 'vendor' },
|
||||
vendor: { import: ['react', 'react-dom'], dependOn: 'corejs' },
|
||||
corejs: { import: 'core-js' },
|
||||
|
|
34
yarn.lock
34
yarn.lock
|
@ -1335,6 +1335,20 @@
|
|||
dependencies:
|
||||
"@types/react" "*"
|
||||
|
||||
"@types/react-helmet@^6.1.0":
|
||||
version "6.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/react-helmet/-/react-helmet-6.1.0.tgz#af586ed685f4905e2adc7462d1d65ace52beee7a"
|
||||
integrity sha512-PYRoU1XJFOzQ3BHvWL1T8iDNbRjdMDJMT5hFmZKGbsq09kbSqJy61uwEpTrbTNWDopVphUT34zUSVLK9pjsgYQ==
|
||||
dependencies:
|
||||
"@types/react" "*"
|
||||
|
||||
"@types/react-tabs@^2.3.2":
|
||||
version "2.3.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/react-tabs/-/react-tabs-2.3.2.tgz#99fb6866bbc6912d44f7bbc99eca03fbbd217960"
|
||||
integrity sha512-QfMelaJSdMcp+CenKhATp12XFFqqUcLXILgwpX3dgWfVYNZPtsLXZDDCRsVn+kwmBOWB+DFPKpQorxbUhnXINw==
|
||||
dependencies:
|
||||
"@types/react" "*"
|
||||
|
||||
"@types/react@*", "@types/react@^16.9.52":
|
||||
version "16.9.55"
|
||||
resolved "https://registry.yarnpkg.com/@types/react/-/react-16.9.55.tgz#47078587f5bfe028a23b6b46c7b94ac0d436acff"
|
||||
|
@ -5974,6 +5988,11 @@ react-dom@0.0.0-experimental-94c0244ba:
|
|||
object-assign "^4.1.1"
|
||||
scheduler "0.0.0-experimental-94c0244ba"
|
||||
|
||||
react-fast-compare@^3.1.1:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-3.2.0.tgz#641a9da81b6a6320f270e89724fb45a0b39e43bb"
|
||||
integrity sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA==
|
||||
|
||||
react-feather@^2.0.3:
|
||||
version "2.0.8"
|
||||
resolved "https://registry.yarnpkg.com/react-feather/-/react-feather-2.0.8.tgz#455baf1470f756a57e2ad6c72545444ce5925781"
|
||||
|
@ -5981,6 +6000,16 @@ react-feather@^2.0.3:
|
|||
dependencies:
|
||||
prop-types "^15.7.2"
|
||||
|
||||
react-helmet@^6.1.0:
|
||||
version "6.1.0"
|
||||
resolved "https://registry.yarnpkg.com/react-helmet/-/react-helmet-6.1.0.tgz#a750d5165cb13cf213e44747502652e794468726"
|
||||
integrity sha512-4uMzEY9nlDlgxr61NL3XbKRy1hEkXmKNXhjbAIOVw5vcFrsdYbH2FEwcNyWvWinl103nXgzYNlns9ca+8kFiWw==
|
||||
dependencies:
|
||||
object-assign "^4.1.1"
|
||||
prop-types "^15.7.2"
|
||||
react-fast-compare "^3.1.1"
|
||||
react-side-effect "^2.1.0"
|
||||
|
||||
react-icons@^3.10.0:
|
||||
version "3.11.0"
|
||||
resolved "https://registry.yarnpkg.com/react-icons/-/react-icons-3.11.0.tgz#2ca2903dfab8268ca18ebd8cc2e879921ec3b254"
|
||||
|
@ -6040,6 +6069,11 @@ react-router@6.0.0-beta.0:
|
|||
dependencies:
|
||||
prop-types "^15.7.2"
|
||||
|
||||
react-side-effect@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/react-side-effect/-/react-side-effect-2.1.0.tgz#1ce4a8b4445168c487ed24dab886421f74d380d3"
|
||||
integrity sha512-IgmcegOSi5SNX+2Snh1vqmF0Vg/CbkycU9XZbOHJlZ6kMzTmi3yc254oB1WCkgA7OQtIAoLmcSFuHTc/tlcqXg==
|
||||
|
||||
react-switch@^5.0.1:
|
||||
version "5.0.1"
|
||||
resolved "https://registry.yarnpkg.com/react-switch/-/react-switch-5.0.1.tgz#449277f4c3aed5286fffd0f50d5cbc2a23330406"
|
||||
|
|
Loading…
Reference in a new issue