Add float action button to pause/start log streaming

This commit is contained in:
Haishan 2021-11-12 20:39:01 +08:00
parent b1ea08a4ee
commit ce3ed3d99f
6 changed files with 50 additions and 50 deletions

View file

@ -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",

View file

@ -1,3 +0,0 @@
module.exports = {
singleQuote: true
};

View file

@ -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);
}

View file

@ -235,11 +235,7 @@ function Conn({ apiConfig }) {
isRefreshPaused ? <Play size={16} /> : <Pause size={16} />
}
mainButtonStyles={
isRefreshPaused
? {
background: '#e74c3c',
}
: {}
isRefreshPaused ? { background: '#e74c3c' } : {}
}
style={fabPosition}
text={isRefreshPaused ? 'Resume Refresh' : 'Pause Refresh'}

View file

@ -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<LogLineProps>) => {
const r = data[index];
return (
<div style={style}>
<LogLine {...r} />
</div>
);
},
areEqual
);
const Row = memo(({ index, style, data }: ListChildComponentProps<LogLineProps>) => {
const r = data[index];
return (
<div style={style}>
<LogLine {...r} />
</div>
);
}, 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 }) {
<LogSearch />
<div ref={refLogsContainer} style={{ paddingBottom }}>
{logs.length === 0 ? (
<div
className={s.logPlaceholder}
style={{ height: containerHeight - paddingBottom }}
>
<div className={s.logPlaceholder} style={{ height: containerHeight - paddingBottom }}>
<div className={s.logPlaceholderIcon}>
<SvgYacd width={200} height={200} />
</div>
@ -101,6 +95,14 @@ function Logs({ dispatch, logLevel, apiConfig, logs }) {
>
{Row}
</List>
<Fab
icon={isRefreshPaused ? <Play size={16} /> : <Pause size={16} />}
mainButtonStyles={isRefreshPaused ? { background: '#e74c3c' } : {}}
style={fabPosition}
text={isRefreshPaused ? 'Resume Refresh' : 'Pause Refresh'}
onClick={toggleIsRefreshPaused}
></Fab>
</div>
)}
</div>

View file

@ -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 {