Skip to content

Commit 85ba12b

Browse files
committed
feat serilog-contrib#162: implement live-feed button
1 parent 4d56c7a commit 85ba12b

File tree

7 files changed

+182
-22
lines changed

7 files changed

+182
-22
lines changed

src/Serilog.Ui.Web/.prettierrc.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,4 @@ trailingComma: all
66
printWidth: 90
77

88
plugins:
9-
- "prettier-plugin-organize-imports"
9+
- 'prettier-plugin-organize-imports'

src/Serilog.Ui.Web/src/app/components/Authorization/AuthorizeButton.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { useDisclosure } from '@mantine/hooks';
33
import { IconLockCheck, IconLockOpen } from '@tabler/icons-react';
44
import { useSerilogUiProps } from 'app/hooks/useSerilogUiProps';
55
import { lazy, memo, Suspense } from 'react';
6+
import { theme } from 'style/theme';
67
import { AuthType } from 'types/types';
78
import { useAuthProperties } from '../../hooks/useAuthProperties';
89

@@ -18,7 +19,7 @@ const AuthorizeButton = () => {
1819

1920
return (
2021
<>
21-
<Button color="green" size="compact-md" onClick={open}>
22+
<Button color={theme.colors?.green?.[7]} size="compact-md" onClick={open}>
2223
{isHeaderReady ? <IconClose /> : <IconOpen />}
2324
Authorize
2425
</Button>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import { Button, Popover, Tooltip } from '@mantine/core';
2+
import { IconRefresh } from '@tabler/icons-react';
3+
import { liveRefreshOptions } from 'app/hooks/useLiveRefresh';
4+
import useQueryLogs from 'app/hooks/useQueryLogs';
5+
import classes from 'style/search.module.css';
6+
import { theme } from 'style/theme';
7+
8+
export const RefreshButton = () => {
9+
const { isLiveRefreshRunning, liveRefreshLabel, startLiveRefresh, stopLiveRefresh } =
10+
useQueryLogs();
11+
12+
if (isLiveRefreshRunning)
13+
return (
14+
<Tooltip label="Stop live refresh">
15+
<Button
16+
fz={9}
17+
size="compact-md"
18+
color={theme.colors?.green?.[7]}
19+
onClick={stopLiveRefresh}
20+
className={classes.refreshButton}
21+
>
22+
{liveRefreshLabel}
23+
</Button>
24+
</Tooltip>
25+
);
26+
27+
return (
28+
<Popover width={105} trapFocus position="bottom" withArrow shadow="md">
29+
<Popover.Target>
30+
<Tooltip label="Start live refresh">
31+
<Button
32+
fz={9}
33+
size="compact-md"
34+
color={theme.colors?.gray?.[7]}
35+
className={classes.activateRefreshButton}
36+
>
37+
<IconRefresh />
38+
</Button>
39+
</Tooltip>
40+
</Popover.Target>
41+
<Popover.Dropdown>
42+
<Button.Group orientation="vertical">
43+
{liveRefreshOptions.map((p) => (
44+
<Button
45+
key={p.value}
46+
onClick={() => {
47+
startLiveRefresh(p.value);
48+
}}
49+
variant="default"
50+
>
51+
{p.label}
52+
</Button>
53+
))}
54+
</Button.Group>
55+
</Popover.Dropdown>
56+
</Popover>
57+
);
58+
};

src/Serilog.Ui.Web/src/app/components/ShellStructure/Header.tsx

+3
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { useSerilogUiProps } from 'app/hooks/useSerilogUiProps';
1111
import { isStringGuard } from 'app/util/guards';
1212
import { Suspense, lazy } from 'react';
1313
import classes from 'style/header.module.css';
14+
import { RefreshButton } from '../Refresh/RefreshButton';
1415
import BrandBadge from './BrandBadge';
1516

1617
const HeaderActivity = lazy(() => import('./HeaderActivity'));
@@ -69,6 +70,8 @@ const Head = ({ isMobileOpen, toggleMobile }: IProps) => {
6970
<IconMoonStars size="1rem" stroke="3" />
7071
)}
7172
</ActionIcon>
73+
74+
<RefreshButton />
7275
<BrandBadge size={isMobileSize ? 'sm' : 'lg'} />
7376
</Group>
7477
</Group>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import { useState } from 'react';
2+
3+
enum options {
4+
'zero' = 0,
5+
'five' = 5,
6+
'fifteen' = 15,
7+
'thirty' = 30,
8+
'sixty' = 60,
9+
'onehundredtwenty' = 120,
10+
'threehundred' = 300,
11+
'ninehundred' = 900,
12+
}
13+
14+
export const liveRefreshOptions = [
15+
{ label: '5s', liveLabel: '5s', value: 'five' },
16+
{ label: '15s', liveLabel: '15s', value: 'fifteen' },
17+
{ label: '30s', liveLabel: '30s', value: 'thirty' },
18+
{ label: '1m', liveLabel: '1m', value: 'sixty' },
19+
{ label: '2m', liveLabel: '2m', value: 'onehundredtwenty' },
20+
{ label: '5m', liveLabel: '5m', value: 'threehundred' },
21+
{ label: '15m', liveLabel: '15m', value: 'ninehundred' },
22+
];
23+
24+
export const useLiveRefresh = () => {
25+
const [refetchInterval, setRefetchInterval] = useState(0);
26+
27+
const isLiveRefreshRunning = refetchInterval > 0;
28+
const liveRefreshLabel = !isLiveRefreshRunning
29+
? ''
30+
: liveRefreshOptions.find((lr) => lr.value === options[refetchInterval / 1000])
31+
?.liveLabel;
32+
33+
const startLiveRefresh = (v: string | null) => {
34+
if (v === null) {
35+
return setRefetchInterval(0);
36+
}
37+
38+
const eachSecond = options[v];
39+
setRefetchInterval(eachSecond * 1000);
40+
};
41+
42+
const stopLiveRefresh = () => {
43+
setRefetchInterval(0);
44+
};
45+
46+
return {
47+
isLiveRefreshRunning,
48+
liveRefreshLabel,
49+
refetchInterval,
50+
startLiveRefresh,
51+
stopLiveRefresh,
52+
};
53+
};
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,50 @@
11
import { keepPreviousData, useQuery } from '@tanstack/react-query';
2+
import { useWatch } from 'react-hook-form';
23
import { fetchLogs } from '../queries/logs';
34
import { useAuthProperties } from './useAuthProperties';
5+
import { useLiveRefresh } from './useLiveRefresh';
46
import { useSearchForm } from './useSearchForm';
5-
import { useWatch } from 'react-hook-form';
67

78
const useQueryLogs = () => {
89
const { fetchInfo, isHeaderReady } = useAuthProperties();
910
const { getValues } = useSearchForm();
11+
const {
12+
isLiveRefreshRunning,
13+
liveRefreshLabel,
14+
refetchInterval,
15+
startLiveRefresh,
16+
stopLiveRefresh,
17+
} = useLiveRefresh();
1018

11-
const currentDbKey = useWatch({ name: 'table' })
12-
const entriesPerPage = useWatch({ name: 'entriesPerPage' })
13-
const page = useWatch({ name: 'page' })
14-
const sortBy = useWatch({ name: 'sortBy' })
15-
const sortOn = useWatch({ name: 'sortOn' })
19+
const currentDbKey = useWatch({ name: 'table' });
20+
const entriesPerPage = useWatch({ name: 'entriesPerPage' });
21+
const page = useWatch({ name: 'page' });
22+
const sortBy = useWatch({ name: 'sortBy' });
23+
const sortOn = useWatch({ name: 'sortOn' });
1624

17-
return useQuery({
18-
enabled: true,
19-
queryKey: ['get-logs', entriesPerPage, page, sortBy, sortOn, currentDbKey],
20-
queryFn: async () => {
21-
if (!isHeaderReady) return null;
25+
return {
26+
...useQuery({
27+
enabled: true,
28+
queryKey: ['get-logs', entriesPerPage, page, sortBy, sortOn, currentDbKey],
29+
queryFn: async () => {
30+
if (!isHeaderReady) return null;
31+
const values = getValues();
2232

23-
return currentDbKey
24-
? await fetchLogs(getValues(), fetchInfo.headers, fetchInfo.routePrefix)
25-
: null;
26-
},
27-
placeholderData: keepPreviousData,
28-
refetchOnMount: false,
29-
refetchOnWindowFocus: false,
30-
retry: false,
31-
});
33+
return currentDbKey
34+
? await fetchLogs(values, fetchInfo.headers, fetchInfo.routePrefix)
35+
: null;
36+
},
37+
placeholderData: keepPreviousData,
38+
refetchOnMount: false,
39+
refetchOnWindowFocus: false,
40+
retry: false,
41+
refetchInterval,
42+
}),
43+
isLiveRefreshRunning,
44+
liveRefreshLabel,
45+
startLiveRefresh,
46+
stopLiveRefresh,
47+
};
3248
};
3349

3450
export default useQueryLogs;

src/Serilog.Ui.Web/src/style/search.module.css

+29
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,32 @@
3030
justify-self: end;
3131
}
3232
}
33+
34+
.refreshButton {
35+
@keyframes blink {
36+
0% {
37+
box-shadow: 'transparent';
38+
}
39+
50% {
40+
box-shadow: 0 0 0.7em #efeff0;
41+
}
42+
100% {
43+
box-shadow: 'transparent';
44+
}
45+
}
46+
47+
border: 4px solid transparent;
48+
animation: blink 2.5s ease-in infinite;
49+
border-radius: 50%;
50+
height: 30px;
51+
padding: 2px;
52+
width: 30px;
53+
}
54+
55+
.activateRefreshButton {
56+
border: 4px solid transparent;
57+
border-radius: 50%;
58+
height: 30px;
59+
padding: 2px;
60+
width: 30px;
61+
}

0 commit comments

Comments
 (0)