From d391e9efbf332559aa065f2a535f84f5e3c71183 Mon Sep 17 00:00:00 2001 From: Haishan Date: Sat, 13 Nov 2021 13:06:41 +0800 Subject: [PATCH] Make log stream pause global state --- src/components/Connections.tsx | 41 +++++----------------- src/components/Logs.module.scss | 9 ++--- src/components/Logs.tsx | 33 ++++++++++-------- src/components/shared/Basic.module.scss | 46 ++++++++++++------------- src/components/shared/rtf.css | 4 +-- src/i18n/en.ts | 2 ++ src/i18n/zh.ts | 2 ++ src/store/app.ts | 27 +++++---------- src/store/types.ts | 1 + 9 files changed, 67 insertions(+), 98 deletions(-) diff --git a/src/components/Connections.tsx b/src/components/Connections.tsx index 4c8bcff..8357018 100644 --- a/src/components/Connections.tsx +++ b/src/components/Connections.tsx @@ -77,15 +77,7 @@ function formatConnectionDataItem( now: number ): FormattedConn { const { id, metadata, upload, download, start, chains, rule } = i; - const { - host, - destinationPort, - destinationIP, - network, - type, - sourceIP, - sourcePort, - } = metadata; + const { host, destinationPort, destinationIP, network, type, sourceIP, sourcePort } = metadata; // host could be an empty string if it's direct IP connection let host2 = host; if (host2 === '') host2 = destinationIP; @@ -130,10 +122,7 @@ function Conn({ apiConfig }) { const filteredClosedConns = filterConns(closedConns, filterKeyword); const [isCloseAllModalOpen, setIsCloseAllModalOpen] = useState(false); const openCloseAllModal = useCallback(() => setIsCloseAllModalOpen(true), []); - const closeCloseAllModal = useCallback( - () => setIsCloseAllModalOpen(false), - [] - ); + const closeCloseAllModal = useCallback(() => setIsCloseAllModalOpen(false), []); const [isRefreshPaused, setIsRefreshPaused] = useState(false); const toggleIsRefreshPaused = useCallback(() => { setIsRefreshPaused((x) => !x); @@ -161,11 +150,7 @@ function Conn({ apiConfig }) { }); // if previous connections and current connections are both empty // arrays, we wont update state to avaoid rerender - if ( - x && - (x.length !== 0 || prevConnsRef.current.length !== 0) && - !isRefreshPaused - ) { + if (x && (x.length !== 0 || prevConnsRef.current.length !== 0) && !isRefreshPaused) { prevConnsRef.current = x; setConns(x); } else { @@ -218,10 +203,7 @@ function Conn({ apiConfig }) { /> -
+
<>{renderTableOrPlaceholder(filteredConns)} : - } - mainButtonStyles={ - isRefreshPaused ? { background: '#e74c3c' } : {} - } + icon={isRefreshPaused ? : } + mainButtonStyles={isRefreshPaused ? { background: '#e74c3c' } : {}} style={fabPosition} - text={isRefreshPaused ? 'Resume Refresh' : 'Pause Refresh'} + text={isRefreshPaused ? t('Resume Refresh') : t('Pause Refresh')} onClick={toggleIsRefreshPaused} > - + diff --git a/src/components/Logs.module.scss b/src/components/Logs.module.scss index 508e9c6..16ecb7f 100644 --- a/src/components/Logs.module.scss +++ b/src/components/Logs.module.scss @@ -41,19 +41,16 @@ color: var(--color-text); :global { - li { + .log { + padding: 10px 40px; background: var(--color-background); } - li.even { + .log.even { background: var(--color-background); } } } -.log { - padding: 10px 40px; -} - /*******************/ .logPlaceholder { diff --git a/src/components/Logs.tsx b/src/components/Logs.tsx index 8f4ed1e..d9c53d7 100644 --- a/src/components/Logs.tsx +++ b/src/components/Logs.tsx @@ -1,23 +1,23 @@ import cx from 'clsx'; import * as React from 'react'; +import { Pause, Play } from 'react-feather'; import { useTranslation } from 'react-i18next'; import { areEqual, FixedSizeList as List, ListChildComponentProps } from 'react-window'; -import { fetchLogs, stop as stopLogs, reconnect as reconnectLogs } from 'src/api/logs'; +import { fetchLogs, reconnect as reconnectLogs,stop as stopLogs } from 'src/api/logs'; import ContentHeader from 'src/components/ContentHeader'; import LogSearch from 'src/components/LogSearch'; -import { connect } from 'src/components/StateProvider'; +import { connect, useStoreActions } from 'src/components/StateProvider'; import SvgYacd from 'src/components/SvgYacd'; import useRemainingViewPortHeight from 'src/hooks/useRemainingViewPortHeight'; -import { getClashAPIConfig } from 'src/store/app'; +import { getClashAPIConfig, getLogStreamingPaused } 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'; +import { Fab, position as fabPosition } from './shared/Fab'; -const { useState, useCallback, memo, useEffect } = React; +const { useCallback, memo, useEffect } = React; const paddingBottom = 30; const colors = { @@ -30,7 +30,7 @@ const colors = { type LogLineProps = Partial; function LogLine({ time, even, payload, type }: LogLineProps) { - const className = cx({ even }, s.log); + const className = cx({ even }, 'log'); return (
@@ -58,12 +58,14 @@ const Row = memo(({ index, style, data }: ListChildComponentProps) ); }, areEqual); -function Logs({ dispatch, logLevel, apiConfig, logs }) { - const [isRefreshPaused, setIsRefreshPaused] = useState(false); +function Logs({ dispatch, logLevel, apiConfig, logs, logStreamingPaused }) { + const actions = useStoreActions(); const toggleIsRefreshPaused = useCallback(() => { - isRefreshPaused ? reconnectLogs({ ...apiConfig, logLevel }) : stopLogs(); - setIsRefreshPaused((x) => !x); - }, [isRefreshPaused, apiConfig, logLevel]); + logStreamingPaused ? reconnectLogs({ ...apiConfig, logLevel }) : stopLogs(); + // being lazy here + // ideally we should check the result of previous operation before updating this + actions.app.updateAppConfig('logStreamingPaused', !logStreamingPaused); + }, [apiConfig, logLevel, logStreamingPaused, actions.app]); const appendLogInternal = useCallback((log) => dispatch(appendLog(log)), [dispatch]); useEffect(() => { fetchLogs({ ...apiConfig, logLevel }, appendLogInternal); @@ -97,10 +99,10 @@ function Logs({ dispatch, logLevel, apiConfig, logs }) { : } - mainButtonStyles={isRefreshPaused ? { background: '#e74c3c' } : {}} + icon={logStreamingPaused ? : } + mainButtonStyles={logStreamingPaused ? { background: '#e74c3c' } : {}} style={fabPosition} - text={isRefreshPaused ? 'Resume Refresh' : 'Pause Refresh'} + text={logStreamingPaused ? t('Resume Refresh') : t('Pause Refresh')} onClick={toggleIsRefreshPaused} >
@@ -114,6 +116,7 @@ const mapState = (s: State) => ({ logs: getLogsForDisplay(s), logLevel: getLogLevel(s), apiConfig: getClashAPIConfig(s), + logStreamingPaused: getLogStreamingPaused(s), }); export default connect(mapState)(Logs); diff --git a/src/components/shared/Basic.module.scss b/src/components/shared/Basic.module.scss index 8e5a113..b8e0068 100644 --- a/src/components/shared/Basic.module.scss +++ b/src/components/shared/Basic.module.scss @@ -36,32 +36,30 @@ h2.sectionNameType { * */ -:global { - body.light { - /* +:root[data-theme='light'] { + /* * --loading-dot-{dot-index}-{dot-keyframe-phase} */ - --loading-dot-1-1: rgba(0, 0, 0, 0.1); - --loading-dot-1-2: rgba(0, 0, 0, 0.5); - --loading-dot-1-3: rgba(0, 0, 0, 0.3); - --loading-dot-2-1: rgba(0, 0, 0, 0.3); - --loading-dot-2-2: rgba(0, 0, 0, 0.1); - --loading-dot-2-3: rgba(0, 0, 0, 0.5); - --loading-dot-3-1: rgba(0, 0, 0, 0.5); - --loading-dot-3-2: rgba(0, 0, 0, 0.3); - --loading-dot-3-3: rgba(0, 0, 0, 0.1); - } - body.dark { - --loading-dot-1-1: rgba(255, 255, 255, 0.5); - --loading-dot-1-2: rgba(255, 255, 255, 0.1); - --loading-dot-1-3: rgba(255, 255, 255, 0.3); - --loading-dot-2-1: rgba(255, 255, 255, 0.3); - --loading-dot-2-2: rgba(255, 255, 255, 0.5); - --loading-dot-2-3: rgba(255, 255, 255, 0.1); - --loading-dot-3-1: rgba(255, 255, 255, 0.1); - --loading-dot-3-2: rgba(255, 255, 255, 0.3); - --loading-dot-3-3: rgba(255, 255, 255, 0.5); - } + --loading-dot-1-1: rgba(0, 0, 0, 0.1); + --loading-dot-1-2: rgba(0, 0, 0, 0.5); + --loading-dot-1-3: rgba(0, 0, 0, 0.3); + --loading-dot-2-1: rgba(0, 0, 0, 0.3); + --loading-dot-2-2: rgba(0, 0, 0, 0.1); + --loading-dot-2-3: rgba(0, 0, 0, 0.5); + --loading-dot-3-1: rgba(0, 0, 0, 0.5); + --loading-dot-3-2: rgba(0, 0, 0, 0.3); + --loading-dot-3-3: rgba(0, 0, 0, 0.1); +} +:root[data-theme='dark'] { + --loading-dot-1-1: rgba(255, 255, 255, 0.5); + --loading-dot-1-2: rgba(255, 255, 255, 0.1); + --loading-dot-1-3: rgba(255, 255, 255, 0.3); + --loading-dot-2-1: rgba(255, 255, 255, 0.3); + --loading-dot-2-2: rgba(255, 255, 255, 0.5); + --loading-dot-2-3: rgba(255, 255, 255, 0.1); + --loading-dot-3-1: rgba(255, 255, 255, 0.1); + --loading-dot-3-2: rgba(255, 255, 255, 0.3); + --loading-dot-3-3: rgba(255, 255, 255, 0.5); } .loadingDot, diff --git a/src/components/shared/rtf.css b/src/components/shared/rtf.css index da439ee..574aad1 100644 --- a/src/components/shared/rtf.css +++ b/src/components/shared/rtf.css @@ -12,8 +12,8 @@ list-style: none; } .rtf.open .rtf--mb { - box-shadow: 0px 5px 5px -3px rgba(0, 0, 0, 0.2), - 0px 8px 10px 1px rgba(0, 0, 0, 0.14), 0px 3px 14px 2px rgba(0, 0, 0, 0.12); + box-shadow: 0px 5px 5px -3px rgba(0, 0, 0, 0.2), 0px 8px 10px 1px rgba(0, 0, 0, 0.14), + 0px 3px 14px 2px rgba(0, 0, 0, 0.12); } .rtf.open .rtf--mb > ul { diff --git a/src/i18n/en.ts b/src/i18n/en.ts index ac0aa6c..1991284 100644 --- a/src/i18n/en.ts +++ b/src/i18n/en.ts @@ -10,6 +10,8 @@ export const data = { 'Upload Total': 'Upload Total', 'Download Total': 'Download Total', 'Active Connections': 'Active Connections', + 'Pause Refresh': 'Pause Refresh', + 'Resume Refresh': 'Resume Refresh', Up: 'Up', Down: 'Down', 'Test Latency': 'Test Latency', diff --git a/src/i18n/zh.ts b/src/i18n/zh.ts index a8f4c05..bd035ef 100644 --- a/src/i18n/zh.ts +++ b/src/i18n/zh.ts @@ -10,6 +10,8 @@ export const data = { 'Upload Total': '上传总量', 'Download Total': '下载总量', 'Active Connections': '活动连接', + 'Pause Refresh': '暂停刷新', + 'Resume Refresh': '继续刷新', Up: '上传', Down: '下载', 'Test Latency': '延迟测速', diff --git a/src/store/app.ts b/src/store/app.ts index c6a455e..ea14e84 100644 --- a/src/store/app.ts +++ b/src/store/app.ts @@ -9,18 +9,16 @@ export const getClashAPIConfig = (s: State) => { const idx = s.app.selectedClashAPIConfigIndex; return s.app.clashAPIConfigs[idx]; }; -export const getSelectedClashAPIConfigIndex = (s: State) => - s.app.selectedClashAPIConfigIndex; +export const getSelectedClashAPIConfigIndex = (s: State) => s.app.selectedClashAPIConfigIndex; 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 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 getHideUnavailableProxies = (s: State) => s.app.hideUnavailableProxies; export const getAutoCloseOldConns = (s: State) => s.app.autoCloseOldConns; +export const getLogStreamingPaused = (s: State) => s.app.logStreamingPaused; const saveStateDebounced = debounce(saveState, 600); @@ -103,7 +101,7 @@ function setTheme(theme = 'dark') { themeColorMeta.setAttribute('content', '#202020'); } else { rootEl.setAttribute('data-theme', 'light'); - themeColorMeta.setAttribute('content', '#eeeeee'); + themeColorMeta.setAttribute('content', '#f7f7f7'); } } @@ -121,9 +119,7 @@ export function switchTheme() { }; } -export function selectChartStyleIndex( - selectedChartStyleIndex: number | string -) { +export function selectChartStyleIndex(selectedChartStyleIndex: number | string) { return (dispatch: DispatchFn, getState: GetStateFn) => { dispatch('appSelectChartStyleIndex', (s) => { s.app.selectedChartStyleIndex = Number(selectedChartStyleIndex); @@ -143,11 +139,7 @@ export function updateAppConfig(name: string, value: unknown) { }; } -export function updateCollapsibleIsOpen( - prefix: string, - name: string, - v: boolean -) { +export function updateCollapsibleIsOpen(prefix: string, name: string, v: boolean) { return (dispatch: DispatchFn, getState: GetStateFn) => { dispatch('updateCollapsibleIsOpen', (s: State) => { s.app.collapsibleIsOpen[`${prefix}:${name}`] = v; @@ -158,9 +150,7 @@ export function updateCollapsibleIsOpen( } const defaultClashAPIConfig = { - baseURL: - document.getElementById('app')?.getAttribute('data-base-url') ?? - 'http://127.0.0.1:9090', + baseURL: document.getElementById('app')?.getAttribute('data-base-url') ?? 'http://127.0.0.1:9090', secret: '', addedAt: 0, }; @@ -179,6 +169,7 @@ const defaultState: StateApp = { proxySortBy: 'Natural', hideUnavailableProxies: false, autoCloseOldConns: false, + logStreamingPaused: false, }; function parseConfigQueryString() { diff --git a/src/store/types.ts b/src/store/types.ts index 7e6a39d..b9141ac 100644 --- a/src/store/types.ts +++ b/src/store/types.ts @@ -13,6 +13,7 @@ export type StateApp = { proxySortBy: string; hideUnavailableProxies: boolean; autoCloseOldConns: boolean; + logStreamingPaused: boolean; }; export type ClashGeneralConfig = {