summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorHaishan <[email protected]>2022-05-08 18:37:08 +0800
committerHaishan <[email protected]>2022-05-08 23:30:24 +0800
commite8f927bfd3faa6234674fa256010f0e2f53339e0 (patch)
tree64246333af7cd800053078404cc5777c88f1414d /src
parent3458ef250de9b26bcff4522479708ca9fa5a553c (diff)
Upgrade chart.js
Diffstat (limited to 'src')
-rw-r--r--src/api/connections.ts1
-rw-r--r--src/api/traffic.ts30
-rw-r--r--src/app.tsx10
-rw-r--r--src/components/Connections.tsx2
-rw-r--r--src/components/Root.tsx12
-rw-r--r--src/components/Rules.tsx10
-rw-r--r--src/components/StateProvider.tsx36
-rw-r--r--src/components/StyleGuide.tsx11
-rw-r--r--src/components/SwitchThemed.tsx13
-rw-r--r--src/components/ToggleSwitch.tsx3
-rw-r--r--src/components/TrafficChart.tsx10
-rw-r--r--src/components/TrafficChartSample.tsx24
-rw-r--r--src/hooks/useLineChart.ts24
-rw-r--r--src/misc/chart-lib.ts23
-rw-r--r--src/misc/chart.ts73
-rw-r--r--src/misc/constants.ts1
-rw-r--r--src/misc/sentry.ts10
-rw-r--r--src/store/app.ts1
-rw-r--r--src/store/types.ts6
19 files changed, 128 insertions, 172 deletions
diff --git a/src/api/connections.ts b/src/api/connections.ts
index d4ed58e..9c94d31 100644
--- a/src/api/connections.ts
+++ b/src/api/connections.ts
@@ -29,6 +29,7 @@ export type ConnectionItem = {
chains: string[];
// e.g. 'Match', 'DomainKeyword'
rule: string;
+ rulePayload?: string;
};
type ConnectionsData = {
downloadTotal: number;
diff --git a/src/api/traffic.ts b/src/api/traffic.ts
index e50ec5e..cd18aac 100644
--- a/src/api/traffic.ts
+++ b/src/api/traffic.ts
@@ -1,31 +1,33 @@
+import { ClashAPIConfig } from '$src/types';
+
import { buildWebSocketURL, getURLAndInit } from '../misc/request-helper';
+
const endpoint = '/traffic';
const textDecoder = new TextDecoder('utf-8');
const Size = 150;
const traffic = {
- labels: Array(Size),
- // labels: [],
+ labels: Array(Size).fill(0),
up: Array(Size),
down: Array(Size),
size: Size,
subscribers: [],
- appendData(o) {
+ appendData(o: { up: number; down: number }) {
+ this.up.shift();
+ this.down.shift();
+ this.labels.shift();
+
+ const l = Date.now();
this.up.push(o.up);
this.down.push(o.down);
- const t = new Date();
- const l = '' + t.getMinutes() + t.getSeconds();
this.labels.push(l);
- if (this.up.length > this.size) this.up.shift();
- if (this.down.length > this.size) this.down.shift();
- if (this.labels.length > this.size) this.labels.shift();
this.subscribers.forEach((f) => f(o));
},
- subscribe(listener) {
+ subscribe(listener: (x:any) => void) {
this.subscribers.push(listener);
return () => {
const idx = this.subscribers.indexOf(listener);
@@ -37,11 +39,11 @@ const traffic = {
let fetched = false;
let decoded = '';
-function parseAndAppend(x) {
+function parseAndAppend(x: string) {
traffic.appendData(JSON.parse(x));
}
-function pump(reader) {
+function pump(reader: ReadableStreamDefaultReader) {
return reader.read().then(({ done, value }) => {
const str = textDecoder.decode(value, { stream: !done });
decoded += str;
@@ -73,8 +75,8 @@ function pump(reader) {
// other value CLOSED
// similar to ws readyState but not the same
// https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/readyState
-let wsState;
-function fetchData(apiConfig) {
+let wsState: number;
+function fetchData(apiConfig: ClashAPIConfig) {
if (fetched || wsState === 1) return traffic;
wsState = 1;
const url = buildWebSocketURL(apiConfig, endpoint);
@@ -92,7 +94,7 @@ function fetchData(apiConfig) {
return traffic;
}
-function fetchDataWithFetch(apiConfig) {
+function fetchDataWithFetch(apiConfig: ClashAPIConfig) {
if (fetched) return traffic;
fetched = true;
const { url, init } = getURLAndInit(apiConfig);
diff --git a/src/app.tsx b/src/app.tsx
index 94ee328..64e19ee 100644
--- a/src/app.tsx
+++ b/src/app.tsx
@@ -1,22 +1,22 @@
import 'modern-normalize/modern-normalize.css';
import './misc/i18n';
-import React from 'react';
-import ReactDOM from 'react-dom';
+import * as React from 'react';
+import { createRoot } from 'react-dom/client';
import Modal from 'react-modal';
import Root from './components/Root';
import * as swRegistration from './swRegistration';
const rootEl = document.getElementById('app');
+const root = createRoot(rootEl);
Modal.setAppElement(rootEl);
-ReactDOM.render(
+root.render(
<React.StrictMode>
<Root />
- </React.StrictMode>,
- rootEl
+ </React.StrictMode>
);
swRegistration.register();
diff --git a/src/components/Connections.tsx b/src/components/Connections.tsx
index 2c82fa6..435345b 100644
--- a/src/components/Connections.tsx
+++ b/src/components/Connections.tsx
@@ -88,7 +88,7 @@ function formatConnectionDataItem(
download,
start: now - new Date(start).valueOf(),
chains: chains.reverse().join(' / '),
- rule: (rulePayload == null | rulePayload === '') ? rule : (`${rule}(${rulePayload})`),
+ rule: !rulePayload ? rule : `${rule}(${rulePayload})`,
...metadata,
host: `${host2}:${destinationPort}`,
type: `${type}(${network})`,
diff --git a/src/components/Root.tsx b/src/components/Root.tsx
index f94d2a2..d4a8aa0 100644
--- a/src/components/Root.tsx
+++ b/src/components/Root.tsx
@@ -3,9 +3,9 @@ import '@fontsource/roboto-mono/latin-400.css';
import '@fontsource/open-sans/latin-400.css';
import '@fontsource/open-sans/latin-700.css';
-import React, { lazy, Suspense } from 'react';
+import * as React from 'react';
import { QueryClientProvider } from 'react-query';
-import { PartialRouteObject } from 'react-router';
+import { RouteObject } from 'react-router';
import { HashRouter as Router, useRoutes } from 'react-router-dom';
import { RecoilRoot } from 'recoil';
import { About } from 'src/components/about/About';
@@ -24,6 +24,8 @@ import SideBar from './SideBar';
import StateProvider from './StateProvider';
import StyleGuide from './StyleGuide';
+const { lazy, Suspense } = React;
+
const Connections = lazy(() => import('./Connections'));
const Config = lazy(() => import('./Config'));
const Logs = lazy(() => import('./Logs'));
@@ -38,10 +40,8 @@ const routes = [
{ path: '/proxies', element: <Proxies /> },
{ path: '/rules', element: <Rules /> },
{ path: '/about', element: <About /> },
- process.env.NODE_ENV === 'development'
- ? { path: '/style', element: <StyleGuide /> }
- : false,
-].filter(Boolean) as PartialRouteObject[];
+ process.env.NODE_ENV === 'development' ? { path: '/style', element: <StyleGuide /> } : false,
+].filter(Boolean) as RouteObject[];
function RouteInnerApp() {
return useRoutes(routes);
diff --git a/src/components/Rules.tsx b/src/components/Rules.tsx
index 9019fab..47644e7 100644
--- a/src/components/Rules.tsx
+++ b/src/components/Rules.tsx
@@ -20,7 +20,13 @@ const { memo } = React;
const paddingBottom = 30;
-function itemKey(index: number, { rules, provider }) {
+type ItemData = {
+ rules: any[];
+ provider: any;
+ apiConfig: ClashAPIConfig;
+};
+
+function itemKey(index: number, { rules, provider }: ItemData) {
const providerQty = provider.names.length;
if (index < providerQty) {
@@ -88,10 +94,8 @@ function Rules({ apiConfig }: RulesProps) {
<ContentHeader title={t('Rules')} />
<TextFilter placeholder="Filter" textAtom={ruleFilterText} />
</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}
diff --git a/src/components/StateProvider.tsx b/src/components/StateProvider.tsx
index 6769108..1ef48d7 100644
--- a/src/components/StateProvider.tsx
+++ b/src/components/StateProvider.tsx
@@ -6,16 +6,8 @@ import React from 'react';
// this is just workaround
immer.setAutoFreeze(false);
-const {
- createContext,
- memo,
- useMemo,
- useRef,
- useEffect,
- useCallback,
- useContext,
- useState,
-} = React;
+const { createContext, memo, useMemo, useRef, useEffect, useCallback, useContext, useState } =
+ React;
export { immer };
@@ -46,7 +38,7 @@ export default function Provider({ initialState, actions = {}, children }) {
}
}, [getState]);
const dispatch = useCallback(
- (actionId, fn) => {
+ (actionId: string | ((a: any, b: any) => any), fn: (s: any) => void) => {
if (typeof actionId === 'function') return actionId(dispatch, getState);
const stateNext = produce(getState(), fn);
@@ -61,26 +53,21 @@ export default function Provider({ initialState, actions = {}, children }) {
},
[getState]
);
- const boundActions = useMemo(() => bindActions(actions, dispatch), [
- actions,
- dispatch,
- ]);
+ const boundActions = useMemo(() => bindActions(actions, dispatch), [actions, dispatch]);
return (
<StateContext.Provider value={state}>
<DispatchContext.Provider value={dispatch}>
- <ActionsContext.Provider value={boundActions}>
- {children}
- </ActionsContext.Provider>
+ <ActionsContext.Provider value={boundActions}>{children}</ActionsContext.Provider>
</DispatchContext.Provider>
</StateContext.Provider>
);
}
-export function connect(mapStateToProps) {
- return (Component) => {
+export function connect(mapStateToProps: any) {
+ return (Component: any) => {
const MemoComponent = memo(Component);
- function Connected(props) {
+ function Connected(props: any) {
const state = useContext(StateContext);
const dispatch = useContext(DispatchContext);
const mapped = mapStateToProps(state, props);
@@ -92,14 +79,13 @@ 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
+function bindAction(action: any, dispatch: any) {
+ return function (...args: any[]) {
return dispatch(action.apply(this, args));
};
}
-function bindActions(actions, dispatch) {
+function bindActions(actions: any, dispatch: any) {
const boundActions = {};
for (const key in actions) {
const action = actions[key];
diff --git a/src/components/StyleGuide.tsx b/src/components/StyleGuide.tsx
index ec0c29b..ee38697 100644
--- a/src/components/StyleGuide.tsx
+++ b/src/components/StyleGuide.tsx
@@ -21,9 +21,7 @@ const optionsRule = [
{ label: 'Direct', value: 'Direct' },
];
-const Pane = ({ children, style }) => (
- <div style={{ ...paneStyle, ...style }}>{children}</div>
-);
+const Pane = ({ children, style }) => <div style={{ ...paneStyle, ...style }}>{children}</div>;
function useToggle(initialState = false) {
const [onoff, setonoff] = React.useState(initialState);
@@ -52,12 +50,7 @@ class StyleGuide extends PureComponent {
</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"
- options={optionsRule}
- value="Rule"
- onChange={noop}
- />
+ <ToggleSwitch name="test" options={optionsRule} value="Rule" 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>
diff --git a/src/components/SwitchThemed.tsx b/src/components/SwitchThemed.tsx
index 7121acb..7289bd2 100644
--- a/src/components/SwitchThemed.tsx
+++ b/src/components/SwitchThemed.tsx
@@ -1,11 +1,14 @@
-import React from 'react';
-import S from 'react-switch';
+import * as React from 'react';
+import ReactSwitch from 'react-switch';
+
+import { State } from '$src/store/types';
import { getTheme } from '../store/app';
import { connect } from './StateProvider';
// workaround https://github.com/vitejs/vite/issues/2139#issuecomment-802981228
-const Switch = S.default ? S.default : S;
+// @ts-ignore
+const Switch = ReactSwitch.default ? ReactSwitch.default : ReactSwitch;
function SwitchThemed({ checked = false, onChange, theme, name }) {
const offColor = theme === 'dark' ? '#393939' : '#e9e9e9';
@@ -29,6 +32,4 @@ function SwitchThemed({ checked = false, onChange, theme, name }) {
);
}
-export default connect((s) => ({
- theme: getTheme(s),
-}))(SwitchThemed);
+export default connect((s: State) => ({ theme: getTheme(s) }))(SwitchThemed);
diff --git a/src/components/ToggleSwitch.tsx b/src/components/ToggleSwitch.tsx
index 0c84059..9eb1019 100644
--- a/src/components/ToggleSwitch.tsx
+++ b/src/components/ToggleSwitch.tsx
@@ -16,8 +16,7 @@ function ToggleSwitch({ options, value, name, onChange }: Props) {
);
const getPortionPercentage = useCallback(
- // @ts-expect-error ts-migrate(7030) FIXME: Not all code paths return a value.
- (idx) => {
+ (idx: number) => {
const w = Math.floor(100 / options.length);
if (idx === options.length - 1) {
return 100 - options.length * w + w;
diff --git a/src/components/TrafficChart.tsx b/src/components/TrafficChart.tsx
index 056cac6..367166a 100644
--- a/src/components/TrafficChart.tsx
+++ b/src/components/TrafficChart.tsx
@@ -1,6 +1,8 @@
import * as React from 'react';
import { useTranslation } from 'react-i18next';
+import { State } from '$src/store/types';
+
import { fetchData } from '../api/traffic';
import useLineChart from '../hooks/useLineChart';
import {
@@ -19,7 +21,7 @@ const chartWrapperStyle = {
maxWidth: 1000,
};
-const mapState = (s) => ({
+const mapState = (s: State) => ({
apiConfig: getClashAPIConfig(s),
selectedChartStyleIndex: getSelectedChartStyleIndex(s),
});
@@ -27,7 +29,7 @@ const mapState = (s) => ({
export default connect(mapState)(TrafficChart);
function TrafficChart({ apiConfig, selectedChartStyleIndex }) {
- const Chart = chartJSResource.read();
+ const ChartMod = chartJSResource.read();
const traffic = fetchData(apiConfig);
const { t } = useTranslation();
const data = useMemo(
@@ -48,10 +50,10 @@ function TrafficChart({ apiConfig, selectedChartStyleIndex }) {
},
],
}),
- [traffic, selectedChartStyleIndex, t]
+ [ traffic, selectedChartStyleIndex, t]
);
- useLineChart(Chart, 'trafficChart', data, traffic);
+ useLineChart(ChartMod.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
diff --git a/src/components/TrafficChartSample.tsx b/src/components/TrafficChartSample.tsx
index fb9b2ee..3e0bba3 100644
--- a/src/components/TrafficChartSample.tsx
+++ b/src/components/TrafficChartSample.tsx
@@ -1,19 +1,17 @@
-import React, { useMemo } from 'react';
+import * as React from 'react';
import useLineChart from '../hooks/useLineChart';
-import {
- chartJSResource,
- chartStyles,
- commonDataSetProps,
-} from '../misc/chart';
+import { chartJSResource, chartStyles, commonDataSetProps } from '../misc/chart';
-const extraChartOptions = {
- legend: {
- display: false,
+const { useMemo } = React;
+
+const extraChartOptions: import('chart.js').ChartOptions<'line'> = {
+ plugins: {
+ legend: { display: false },
},
scales: {
- xAxes: [{ display: false }],
- yAxes: [{ display: false }],
+ x: { display: false, type: 'category' },
+ y: { display: false, type: 'linear' },
},
};
@@ -22,7 +20,7 @@ const data2 = [184e3, 183e3, 196e3, 182e3, 190e3, 186e3, 182e3, 189e3];
const labels = data1;
export default function TrafficChart({ id }) {
- const Chart = chartJSResource.read();
+ const ChartMod = chartJSResource.read();
const data = useMemo(
() => ({
@@ -44,7 +42,7 @@ export default function TrafficChart({ id }) {
);
const eleId = 'chart-' + id;
- useLineChart(Chart, eleId, data, null, extraChartOptions);
+ useLineChart(ChartMod.Chart, eleId, data, null, extraChartOptions);
return (
<div style={{ width: 100, padding: 5 }}>
diff --git a/src/hooks/useLineChart.ts b/src/hooks/useLineChart.ts
index 2757ee1..a3205aa 100644
--- a/src/hooks/useLineChart.ts
+++ b/src/hooks/useLineChart.ts
@@ -1,28 +1,24 @@
+import type { ChartConfiguration } from 'chart.js';
import React from 'react';
import { commonChartOptions } from 'src/misc/chart';
const { useEffect } = React;
-const options = commonChartOptions;
export default function useLineChart(
- Chart,
- elementId,
- data,
- subscription,
+ chart: typeof import('chart.js').Chart,
+ elementId: string,
+ data: ChartConfiguration['data'],
+ subscription: any,
extraChartOptions = {}
) {
useEffect(() => {
- const ctx = document.getElementById(elementId).getContext('2d');
- const c = new Chart(ctx, {
- type: 'line',
- data,
- options: { ...options, ...extraChartOptions },
- });
- const unsubscribe =
- subscription && subscription.subscribe(() => c.update());
+ const ctx = (document.getElementById(elementId) as HTMLCanvasElement).getContext('2d');
+ const options = { ...commonChartOptions, ...extraChartOptions };
+ const c = new chart(ctx, { type: 'line', data, options });
+ const unsubscribe = subscription && subscription.subscribe(() => c.update());
return () => {
unsubscribe && unsubscribe();
c.destroy();
};
- }, [Chart, elementId, data, subscription, extraChartOptions]);
+ }, [chart, elementId, data, subscription, extraChartOptions]);
}
diff --git a/src/misc/chart-lib.ts b/src/misc/chart-lib.ts
new file mode 100644
index 0000000..9a2bf35
--- /dev/null
+++ b/src/misc/chart-lib.ts
@@ -0,0 +1,23 @@
+import {
+ CategoryScale,
+ Chart,
+ Filler,
+ Legend,
+ LinearScale,
+ LineController,
+ LineElement,
+ PointElement,
+} from 'chart.js';
+
+// see https://www.chartjs.org/docs/latest/getting-started/integration.html#bundlers-webpack-rollup-etc
+Chart.register(
+ LineElement,
+ PointElement,
+ LineController,
+ CategoryScale,
+ LinearScale,
+ Filler,
+ Legend
+);
+
+export { Chart };
diff --git a/src/misc/chart.ts b/src/misc/chart.ts
index 9e2c459..a6ee82e 100644
--- a/src/misc/chart.ts
+++ b/src/misc/chart.ts
@@ -1,71 +1,36 @@
import { unstable_createResource as createResource } from '@hsjs/react-cache';
import prettyBytes from './pretty-bytes';
-
export const chartJSResource = createResource(() => {
- return import(
- /* webpackChunkName: "chartjs" */
- /* webpackPrefetch: true */
- /* webpackPreload: true */
- 'chart.js/dist/Chart.min.js'
- ).then((c) => c.default);
+ return import('$src/misc/chart-lib');
});
-export const commonDataSetProps = {
- borderWidth: 1,
- lineTension: 0,
- pointRadius: 0,
-};
+export const commonDataSetProps = { borderWidth: 1, pointRadius: 0, tension: 0.2, fill: true };
-export const commonChartOptions = {
+export const commonChartOptions: import('chart.js').ChartOptions<'line'> = {
responsive: true,
maintainAspectRatio: true,
- title: {
- display: false,
- },
- legend: {
- display: true,
- position: 'top',
- labels: {
- fontColor: '#ccc',
- boxWidth: 20,
- },
- },
- tooltips: {
- enabled: false,
- mode: 'index',
- intersect: false,
- animationDuration: 100,
- },
- hover: {
- mode: 'nearest',
- intersect: true,
+ plugins: {
+ legend: { labels: { boxWidth: 20 } }
},
scales: {
- xAxes: [
- {
- display: false,
- gridLines: {
- display: false,
- },
- },
- ],
- yAxes: [
- {
+ x: { display: false, type: 'category' },
+ y: {
+ type: 'linear',
+ display: true,
+ grid: {
display: true,
- gridLines: {
- display: true,
- color: '#555',
- borderDash: [3, 6],
- drawBorder: false,
- },
- ticks: {
- callback(value) {
- return prettyBytes(value) + '/s ';
- },
+ color: '#555',
+ drawTicks: false,
+ borderDash: [3, 6],
+ drawBorder: false,
+ },
+ ticks: {
+ callback(value: number) {
+ return prettyBytes(value) + '/s ';
},
},
- ],
+ },
},
};
diff --git a/src/misc/constants.ts b/src/misc/constants.ts
deleted file mode 100644
index 5c66350..0000000
--- a/src/misc/constants.ts
+++ /dev/null
@@ -1 +0,0 @@
-// const ProxySortingOptions =
diff --git a/src/misc/sentry.ts b/src/misc/sentry.ts
deleted file mode 100644
index efedcb3..0000000
--- a/src/misc/sentry.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-const dsn = 'https://[email protected]/1359284';
-let Sentry;
-export async function getSentry() {
- if (Sentry) return Sentry;
- const s = await import('@sentry/browser');
- s.init({ dsn });
- // eslint-disable-next-line require-atomic-updates
- Sentry = s;
- return Sentry;
-}
diff --git a/src/store/app.ts b/src/store/app.ts
index 6680e6e..7262b32 100644
--- a/src/store/app.ts
+++ b/src/store/app.ts
@@ -22,7 +22,6 @@ export const getLogStreamingPaused = (s: State) => s.app.logStreamingPaused;
const saveStateDebounced = debounce(saveState, 600);
-// @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++) {
diff --git a/src/store/types.ts b/src/store/types.ts
index b9141ac..d12adaa 100644
--- a/src/store/types.ts
+++ b/src/store/types.ts
@@ -108,10 +108,8 @@ export type State = {
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<
+ (action: (dispatch: DispatchFn, getState: GetStateFn) => Promise<void>): ReturnType<
typeof action
>;
+ (action: (dispatch: DispatchFn, getState: GetStateFn) => void): ReturnType<typeof action>;
}