Make log stream pause global state

This commit is contained in:
Haishan 2021-11-13 13:06:41 +08:00
parent ab99bd3773
commit d391e9efbf
9 changed files with 67 additions and 98 deletions

View file

@ -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 }) {
/>
</div>
</div>
<div
ref={refContainer}
style={{ padding: 30, paddingBottom, paddingTop: 0 }}
>
<div ref={refContainer} style={{ padding: 30, paddingBottom, paddingTop: 0 }}>
<div
style={{
height: containerHeight - paddingBottom,
@ -231,20 +213,13 @@ function Conn({ apiConfig }) {
<TabPanel>
<>{renderTableOrPlaceholder(filteredConns)}</>
<Fab
icon={
isRefreshPaused ? <Play size={16} /> : <Pause size={16} />
}
mainButtonStyles={
isRefreshPaused ? { background: '#e74c3c' } : {}
}
icon={isRefreshPaused ? <Play size={16} /> : <Pause size={16} />}
mainButtonStyles={isRefreshPaused ? { background: '#e74c3c' } : {}}
style={fabPosition}
text={isRefreshPaused ? 'Resume Refresh' : 'Pause Refresh'}
text={isRefreshPaused ? t('Resume Refresh') : t('Pause Refresh')}
onClick={toggleIsRefreshPaused}
>
<Action
text="Close All Connections"
onClick={openCloseAllModal}
>
<Action text="Close All Connections" onClick={openCloseAllModal}>
<IconClose size={10} />
</Action>
</Fab>

View file

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

View file

@ -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<Log>;
function LogLine({ time, even, payload, type }: LogLineProps) {
const className = cx({ even }, s.log);
const className = cx({ even }, 'log');
return (
<div className={className}>
<div className={s.logMeta}>
@ -58,12 +58,14 @@ const Row = memo(({ index, style, data }: ListChildComponentProps<LogLineProps>)
);
}, 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 }) {
</List>
<Fab
icon={isRefreshPaused ? <Play size={16} /> : <Pause size={16} />}
mainButtonStyles={isRefreshPaused ? { background: '#e74c3c' } : {}}
icon={logStreamingPaused ? <Play size={16} /> : <Pause size={16} />}
mainButtonStyles={logStreamingPaused ? { background: '#e74c3c' } : {}}
style={fabPosition}
text={isRefreshPaused ? 'Resume Refresh' : 'Pause Refresh'}
text={logStreamingPaused ? t('Resume Refresh') : t('Pause Refresh')}
onClick={toggleIsRefreshPaused}
></Fab>
</div>
@ -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);

View file

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

View file

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

View file

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

View file

@ -10,6 +10,8 @@ export const data = {
'Upload Total': '上传总量',
'Download Total': '下载总量',
'Active Connections': '活动连接',
'Pause Refresh': '暂停刷新',
'Resume Refresh': '继续刷新',
Up: '上传',
Down: '下载',
'Test Latency': '延迟测速',

View file

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

View file

@ -13,6 +13,7 @@ export type StateApp = {
proxySortBy: string;
hideUnavailableProxies: boolean;
autoCloseOldConns: boolean;
logStreamingPaused: boolean;
};
export type ClashGeneralConfig = {