From ce3ed3d99f6c8fcc13747c9fea12ce7f6cbebd72 Mon Sep 17 00:00:00 2001 From: Haishan Date: Fri, 12 Nov 2021 20:39:01 +0800 Subject: [PATCH] Add float action button to pause/start log streaming --- package.json | 4 +++ prettier.config.js | 3 -- src/api/logs.ts | 21 +++++++------ src/components/Connections.tsx | 6 +--- src/components/Logs.tsx | 54 ++++++++++++++++++---------------- src/components/Root.scss | 12 ++++---- 6 files changed, 50 insertions(+), 50 deletions(-) delete mode 100644 prettier.config.js diff --git a/package.json b/package.json index a76251c..e449804 100644 --- a/package.json +++ b/package.json @@ -2,6 +2,10 @@ "name": "yacd", "version": "0.3.3", "description": "Yet another Clash dashboard", + "prettier": { + "printWidth": 100, + "singleQuote": true + }, "scripts": { "lint": "eslint --fix --cache src", "dev": "vite", diff --git a/prettier.config.js b/prettier.config.js deleted file mode 100644 index 7d93e18..0000000 --- a/prettier.config.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = { - singleQuote: true -}; diff --git a/src/api/logs.ts b/src/api/logs.ts index cb74bf3..044a31b 100644 --- a/src/api/logs.ts +++ b/src/api/logs.ts @@ -5,6 +5,12 @@ import { LogsAPIConfig } from 'src/types'; import { buildLogsWebSocketURL, getURLAndInit } from '../misc/request-helper'; type AppendLogFn = (x: Log) => void; +enum WebSocketReadyState { + Connecting = 0, + Open = 1, + Closing = 2, + Closed = 3, +} const endpoint = '/logs'; const textDecoder = new TextDecoder('utf-8'); @@ -86,20 +92,13 @@ function makeConnStr(c: LogsAPIConfig) { let prevConnStr: string; let controller: AbortController; -// 1 OPEN -// other value CLOSED -// similar to ws readyState but not the same -// https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/readyState -let wsState: number; export function fetchLogs(apiConfig: LogsAPIConfig, appendLog: AppendLogFn) { if (apiConfig.logLevel === 'uninit') return; - if (fetched || wsState === 1) return; + if (fetched || (ws && ws.readyState === WebSocketReadyState.Open)) return; prevAppendLogFn = appendLog; - wsState = 1; const url = buildLogsWebSocketURL(apiConfig, endpoint); ws = new WebSocket(url); ws.addEventListener('error', () => { - wsState = 3; fetchLogsWithFetch(apiConfig, appendLog); }); ws.addEventListener('message', function (event) { @@ -107,10 +106,14 @@ export function fetchLogs(apiConfig: LogsAPIConfig, appendLog: AppendLogFn) { }); } +export function stop() { + ws.close(); + if (controller) controller.abort(); +} + export function reconnect(apiConfig: LogsAPIConfig) { if (!prevAppendLogFn || !ws) return; ws.close(); - wsState = 3; fetched = false; fetchLogs(apiConfig, prevAppendLogFn); } diff --git a/src/components/Connections.tsx b/src/components/Connections.tsx index e5b4466..4c8bcff 100644 --- a/src/components/Connections.tsx +++ b/src/components/Connections.tsx @@ -235,11 +235,7 @@ function Conn({ apiConfig }) { isRefreshPaused ? : } mainButtonStyles={ - isRefreshPaused - ? { - background: '#e74c3c', - } - : {} + isRefreshPaused ? { background: '#e74c3c' } : {} } style={fabPosition} text={isRefreshPaused ? 'Resume Refresh' : 'Pause Refresh'} diff --git a/src/components/Logs.tsx b/src/components/Logs.tsx index 019edd5..8f4ed1e 100644 --- a/src/components/Logs.tsx +++ b/src/components/Logs.tsx @@ -1,12 +1,8 @@ import cx from 'clsx'; import * as React from 'react'; import { useTranslation } from 'react-i18next'; -import { - areEqual, - FixedSizeList as List, - ListChildComponentProps, -} from 'react-window'; -import { fetchLogs } from 'src/api/logs'; +import { areEqual, FixedSizeList as List, ListChildComponentProps } from 'react-window'; +import { fetchLogs, stop as stopLogs, reconnect as reconnectLogs } from 'src/api/logs'; import ContentHeader from 'src/components/ContentHeader'; import LogSearch from 'src/components/LogSearch'; import { connect } from 'src/components/StateProvider'; @@ -16,10 +12,12 @@ 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 { Pause, Play } from 'react-feather'; +import { Fab, position as fabPosition } from './shared/Fab'; import s from './Logs.module.scss'; -const { useCallback, memo, useEffect } = React; +const { useState, useCallback, memo, useEffect } = React; const paddingBottom = 30; const colors = { @@ -51,23 +49,22 @@ function itemKey(index: number, data: LogLineProps[]) { return item.id; } -const Row = memo( - ({ index, style, data }: ListChildComponentProps) => { - const r = data[index]; - return ( -
- -
- ); - }, - areEqual -); +const Row = memo(({ index, style, data }: ListChildComponentProps) => { + const r = data[index]; + return ( +
+ +
+ ); +}, areEqual); function Logs({ dispatch, logLevel, apiConfig, logs }) { - const appendLogInternal = useCallback( - (log) => dispatch(appendLog(log)), - [dispatch] - ); + const [isRefreshPaused, setIsRefreshPaused] = useState(false); + const toggleIsRefreshPaused = useCallback(() => { + isRefreshPaused ? reconnectLogs({ ...apiConfig, logLevel }) : stopLogs(); + setIsRefreshPaused((x) => !x); + }, [isRefreshPaused, apiConfig, logLevel]); + const appendLogInternal = useCallback((log) => dispatch(appendLog(log)), [dispatch]); useEffect(() => { fetchLogs({ ...apiConfig, logLevel }, appendLogInternal); }, [apiConfig, logLevel, appendLogInternal]); @@ -80,10 +77,7 @@ function Logs({ dispatch, logLevel, apiConfig, logs }) {
{logs.length === 0 ? ( -
+
@@ -101,6 +95,14 @@ function Logs({ dispatch, logLevel, apiConfig, logs }) { > {Row} + + : } + mainButtonStyles={isRefreshPaused ? { background: '#e74c3c' } : {}} + style={fabPosition} + text={isRefreshPaused ? 'Resume Refresh' : 'Pause Refresh'} + onClick={toggleIsRefreshPaused} + >
)}
diff --git a/src/components/Root.scss b/src/components/Root.scss index 55198ab..0df5819 100644 --- a/src/components/Root.scss +++ b/src/components/Root.scss @@ -53,18 +53,14 @@ :root { --font-mono: 'Roboto Mono', Menlo, monospace; - --font-normal: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica, - Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol, - 'PingFang SC', 'Microsoft YaHei', '微软雅黑', Arial, sans-serif; + // prettier-ignore + --font-normal: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol, 'PingFang SC', 'Microsoft YaHei', '微软雅黑', Arial, sans-serif; --color-focus-blue: #1a73e8; --btn-bg: #387cec; } body { - font-family: 'Open Sans', -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, - Helvetica, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji, - Segoe UI Symbol, 'PingFang SC', 'Microsoft YaHei', '微软雅黑', Arial, - sans-serif; + font-family: var(--font-normal); -webkit-tap-highlight-color: rgba(0, 0, 0, 0); -webkit-text-size-adjust: 100%; -webkit-font-smoothing: antialiased; @@ -135,10 +131,12 @@ body { :root[data-theme='dark'] { @include dark; + color-scheme: dark; } :root[data-theme='light'] { @include light; + color-scheme: light; } .flexCenter {