diff --git a/src/api/logs.ts b/src/api/logs.ts index a65c55e..cb74bf3 100644 --- a/src/api/logs.ts +++ b/src/api/logs.ts @@ -1,5 +1,6 @@ import { pad0 } from 'src/misc/utils'; -import { Log, LogsAPIConfig } from 'src/types'; +import { Log } from 'src/store/types'; +import { LogsAPIConfig } from 'src/types'; import { buildLogsWebSocketURL, getURLAndInit } from '../misc/request-helper'; @@ -15,6 +16,8 @@ const getRandomStr = () => { let even = false; let fetched = false; let decoded = ''; +let ws: WebSocket; +let prevAppendLogFn: AppendLogFn; function appendData(s: string, callback: AppendLogFn) { let o: Partial; @@ -31,7 +34,7 @@ function appendData(s: string, callback: AppendLogFn) { o.time = time; o.id = +now - 0 + getRandomStr(); o.even = even = !even; - callback(o); + callback(o as Log); } function formatDate(d: Date) { @@ -73,7 +76,14 @@ function pump(reader: ReadableStreamDefaultReader, appendLog: AppendLogFn) { }); } -let apiConfigSnapshot: LogsAPIConfig; +/** loose hashing of the connection configuration */ +function makeConnStr(c: LogsAPIConfig) { + const keys = Object.keys(c); + keys.sort(); + return keys.map((k) => c[k]).join('|'); +} + +let prevConnStr: string; let controller: AbortController; // 1 OPEN @@ -84,11 +94,11 @@ let wsState: number; export function fetchLogs(apiConfig: LogsAPIConfig, appendLog: AppendLogFn) { if (apiConfig.logLevel === 'uninit') return; if (fetched || wsState === 1) return; + prevAppendLogFn = appendLog; wsState = 1; const url = buildLogsWebSocketURL(apiConfig, endpoint); - const ws = new WebSocket(url); - ws.addEventListener('error', () => (wsState = 3)); - ws.addEventListener('close', function (_ev) { + ws = new WebSocket(url); + ws.addEventListener('error', () => { wsState = 3; fetchLogsWithFetch(apiConfig, appendLog); }); @@ -97,20 +107,23 @@ export function fetchLogs(apiConfig: LogsAPIConfig, appendLog: AppendLogFn) { }); } +export function reconnect(apiConfig: LogsAPIConfig) { + if (!prevAppendLogFn || !ws) return; + ws.close(); + wsState = 3; + fetched = false; + fetchLogs(apiConfig, prevAppendLogFn); +} + function fetchLogsWithFetch(apiConfig: LogsAPIConfig, appendLog: AppendLogFn) { - if ( - controller && - (apiConfigSnapshot.baseURL !== apiConfig.baseURL || - apiConfigSnapshot.secret !== apiConfig.secret || - apiConfigSnapshot.logLevel !== apiConfig.logLevel) - ) { + if (controller && makeConnStr(apiConfig) !== prevConnStr) { controller.abort(); } else if (fetched) { return; } fetched = true; - apiConfigSnapshot = { ...apiConfig }; + prevConnStr = makeConnStr(apiConfig); controller = new AbortController(); const signal = controller.signal; diff --git a/src/components/Config.tsx b/src/components/Config.tsx index 8c0678c..34bb20a 100644 --- a/src/components/Config.tsx +++ b/src/components/Config.tsx @@ -1,6 +1,7 @@ import * as React from 'react'; import { LogOut } from 'react-feather'; import { useTranslation } from 'react-i18next'; +import * as logsApi from 'src/api/logs'; import Select from 'src/components/shared/Select'; import { ClashGeneralConfig, DispatchFn, State } from 'src/store/types'; import { ClashAPIConfig } from 'src/types'; @@ -103,10 +104,7 @@ function ConfigImpl({ const setConfigState = useCallback( (name, val) => { - setConfigStateInternal({ - ...configState, - [name]: val, - }); + setConfigStateInternal({ ...configState, [name]: val }); }, [configState] ); @@ -128,6 +126,9 @@ function ConfigImpl({ case 'log-level': setConfigState(name, value); dispatch(updateConfigs(apiConfig, { [name]: value })); + if (name === 'log-level') { + logsApi.reconnect({ ...apiConfig, logLevel: value }); + } break; case 'redir-port': case 'socks-port': diff --git a/src/components/Logs.tsx b/src/components/Logs.tsx index 7271f0e..2b002d3 100644 --- a/src/components/Logs.tsx +++ b/src/components/Logs.tsx @@ -15,7 +15,7 @@ import useRemainingViewPortHeight from 'src/hooks/useRemainingViewPortHeight'; import { getClashAPIConfig } from 'src/store/app'; import { getLogLevel } from 'src/store/configs'; import { appendLog, getLogsForDisplay } from 'src/store/logs'; -import { Log,State } from 'src/store/types'; +import { Log, State } from 'src/store/types'; import s from './Logs.module.css'; @@ -23,8 +23,7 @@ const { useCallback, memo, useEffect } = React; const paddingBottom = 30; const colors = { - debug: 'none', - // debug: '#8a8a8a', + debug: '#28792c', info: 'var(--bg-log-info-tag)', warning: '#b99105', error: '#c11c1c', @@ -66,9 +65,7 @@ const Row = memo( function Logs({ dispatch, logLevel, apiConfig, logs }) { const appendLogInternal = useCallback( - (log) => { - dispatch(appendLog(log)); - }, + (log) => dispatch(appendLog(log)), [dispatch] ); useEffect(() => { diff --git a/src/components/StyleGuide.tsx b/src/components/StyleGuide.tsx index 1e6d95e..ec0c29b 100644 --- a/src/components/StyleGuide.tsx +++ b/src/components/StyleGuide.tsx @@ -4,7 +4,6 @@ import Loading from 'src/components/Loading'; import Button from './Button'; import Input from './Input'; -import { LoadingDot } from './shared/Basic'; import SwitchThemed from './SwitchThemed'; import ToggleSwitch from './ToggleSwitch'; @@ -17,18 +16,9 @@ const paneStyle = { }; const optionsRule = [ - { - label: 'Global', - value: 'Global', - }, - { - label: 'Rule', - value: 'Rule', - }, - { - label: 'Direct', - value: 'Direct', - }, + { label: 'Global', value: 'Global' }, + { label: 'Rule', value: 'Rule' }, + { label: 'Direct', value: 'Direct' }, ]; const Pane = ({ children, style }) => ( diff --git a/src/store/logs.ts b/src/store/logs.ts index 53dc9a5..4795154 100644 --- a/src/store/logs.ts +++ b/src/store/logs.ts @@ -1,6 +1,5 @@ import { createSelector } from 'reselect'; - -import { DispatchFn, GetStateFn, Log, State } from './types'; +import { DispatchFn, GetStateFn, Log, State } from 'src/store/types'; const LogSize = 300; @@ -44,7 +43,9 @@ export function appendLog(log: Log) { // mutate intentionally for performance logs[tail] = log; - dispatch('logsAppendLog', (s: State) => (s.logs.tail = tail)); + dispatch('logsAppendLog', (s: State) => { + s.logs.tail = tail; + }); }; }