Skip to content

Commit 41042f3

Browse files
authored
Preparing ops logs for public release (#983)
* Adding back in setting a variable for where the logs should go * Starting to wire back up the AddingLogTypes * Cleaning up the store a bit as we no longer need to care what fetching is happening * Some more cleaning up of state in store * Starting to make the refresh call to be directly passing props to reduce chance that states can start clashing when called in quick succession * Passing around the start and end of parsing logs * Getting the loading of new logs working * Prevent duplicate fetching * Cleaning up logging * No longer need the add type as we can just pass the parsed values * Removing the add type Starting to pass the offset when fetching newer logs * Removing the add type Starting to pass the offset when fetching newer logs * Clean up logging Do not override the start if provided Remove the adjusted bytes for now * Do not think this applies anymore * Passing the range with the docs * Updating types a bit to plan for future typing * logging and comment cleanup * Handling intervals * Cleaning up the debounce * commenting * cleaning up logging * Renaming and typing work * Refactoring and moving stuff around to prepare for it to be shared Trying to get the logs to show open/closed/height properly * Adding a bit more padding to help with showing on scroll * Trying to make sure the waiting rows show up all the time * Trying to get the waiting rows to show more consistently * typing log levels * commenting
1 parent a5be683 commit 41042f3

File tree

19 files changed

+498
-480
lines changed

19 files changed

+498
-480
lines changed

src/components/collection/DataPreview/ListView.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ import {
88
jsonViewTheme,
99
semiTransparentBackground,
1010
} from 'context/Theme';
11-
import { JournalRecord, useJournalData } from 'hooks/journals/useJournalData';
11+
import { JournalRecord } from 'hooks/journals/types';
12+
import { useJournalData } from 'hooks/journals/useJournalData';
1213
import { LiveSpecsQuery_spec } from 'hooks/useLiveSpecs';
1314
import { JsonPointer } from 'json-ptr';
1415
import { isEmpty } from 'lodash';

src/components/tables/Logs/Body.tsx

+40-55
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ import {
1818
DEFAULT_ROW_HEIGHT_WITHOUT_FIELDS,
1919
UUID_NEWEST_LOG,
2020
UUID_OLDEST_LOG,
21+
VIRTUAL_TABLE_BODY_PADDING,
22+
WAITING_ROW_HEIGHT,
2123
} from './shared';
2224
import useLogColumns from './useLogColumns';
2325
import { LogsTableRow } from './Row';
@@ -52,7 +54,7 @@ function LogsTableBody({ outerRef, tableScroller, virtualRows }: Props) {
5254
_meta: {
5355
uuid: UUID_OLDEST_LOG,
5456
},
55-
level: 'waiting',
57+
level: 'ui_waiting',
5658
message: '',
5759
ts: '',
5860
},
@@ -61,7 +63,7 @@ function LogsTableBody({ outerRef, tableScroller, virtualRows }: Props) {
6163
_meta: {
6264
uuid: UUID_NEWEST_LOG,
6365
},
64-
level: 'waiting',
66+
level: 'ui_waiting',
6567
message: '',
6668
ts: '',
6769
},
@@ -71,68 +73,25 @@ function LogsTableBody({ outerRef, tableScroller, virtualRows }: Props) {
7173
return null;
7274
}, [documents]);
7375

74-
const getItemSize = useCallback(
75-
(rowIndex: number) => {
76-
const row = itemData?.[rowIndex];
77-
78-
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
79-
if (!row) {
80-
return 0;
81-
}
82-
83-
const customHeight = expandedHeights.current.get(row._meta.uuid);
84-
85-
return customHeight && customHeight > 0
86-
? customHeight
87-
: isEmpty(row.fields)
88-
? DEFAULT_ROW_HEIGHT_WITHOUT_FIELDS
89-
: DEFAULT_ROW_HEIGHT;
90-
},
91-
[itemData]
92-
);
93-
94-
const openRow = useCallback((uuid: string, isOpen: boolean) => {
95-
if (isOpen) {
96-
openRows.current.set(uuid, isOpen);
97-
} else {
98-
openRows.current.delete(uuid);
99-
}
100-
}, []);
101-
102-
const updateRowHeight = useCallback(
103-
(index: number, uuid: string, height: number) => {
104-
if (
105-
height > 0 ||
106-
height === DEFAULT_ROW_HEIGHT_WITHOUT_FIELDS ||
107-
height === DEFAULT_ROW_HEIGHT
108-
) {
109-
expandedHeights.current.set(uuid, height);
110-
} else {
111-
expandedHeights.current.delete(uuid);
112-
}
113-
114-
tableScroller()?.resetAfterIndex(index);
115-
},
116-
[tableScroller]
117-
);
118-
11976
const renderRow = useCallback(
12077
({ data, index, style }: ListChildComponentProps) => {
12178
const row = data[index];
122-
const uuid = row._meta.uuid;
12379
return (
12480
<LogsTableRow
12581
row={row}
12682
style={style}
127-
rowExpanded={(height) =>
128-
updateRowHeight(index, uuid, height)
83+
rowExpanded={(uuid, height) => {
84+
expandedHeights.current.set(uuid, height);
85+
tableScroller()?.resetAfterIndex(index);
86+
}}
87+
rowOpened={(uuid, isOpen) =>
88+
openRows.current.set(uuid, isOpen)
12989
}
130-
rowOpened={(isOpen) => openRow(uuid, isOpen)}
131-
renderOpen={Boolean(openRows.current.get(uuid))}
90+
renderOpen={Boolean(openRows.current.get(row._meta.uuid))}
13291
/>
13392
);
13493
},
135-
[openRow, updateRowHeight]
94+
[tableScroller]
13695
);
13796

13897
if (itemData && itemData.length > 0) {
@@ -152,9 +111,35 @@ function LogsTableBody({ outerRef, tableScroller, virtualRows }: Props) {
152111
itemKey={(index, data) => {
153112
return data[index]._meta.uuid;
154113
}}
155-
itemSize={getItemSize}
114+
itemSize={(index) => {
115+
const row = itemData[index];
116+
117+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
118+
if (!row) {
119+
return 0;
120+
}
121+
122+
// Due to the intersection observer we need to force a specific height
123+
if (row.level === 'ui_waiting') {
124+
return WAITING_ROW_HEIGHT;
125+
}
126+
127+
const customHeight =
128+
expandedHeights.current.get(
129+
row._meta.uuid
130+
);
131+
132+
return customHeight && customHeight > 0
133+
? customHeight
134+
: isEmpty(row.fields)
135+
? DEFAULT_ROW_HEIGHT_WITHOUT_FIELDS
136+
: DEFAULT_ROW_HEIGHT;
137+
}}
156138
overscanCount={10}
157-
style={{ paddingBottom: 10, paddingTop: 10 }}
139+
style={{
140+
paddingBottom: VIRTUAL_TABLE_BODY_PADDING,
141+
paddingTop: VIRTUAL_TABLE_BODY_PADDING,
142+
}}
158143
width={width}
159144
>
160145
{renderRow}

src/components/tables/Logs/Row.tsx

+9-10
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ import WaitingForNewLogsRow from './WaitingForRow/NewLogs';
1010

1111
interface RowProps {
1212
row: OpsLogFlowDocument;
13-
rowExpanded: (height: number) => void;
14-
rowOpened: (isOpen: boolean) => void;
13+
rowExpanded: (uuid: string, height: number) => void;
14+
rowOpened: (uuid: string, isOpen: boolean) => void;
1515
style: CSSProperties;
1616
renderOpen?: boolean;
1717
}
@@ -24,9 +24,10 @@ export function LogsTableRow({
2424
style,
2525
}: RowProps) {
2626
const theme = useTheme();
27-
const renderedHeight = style.height;
2827

29-
const previousHeight = useRef(renderedHeight);
28+
const uuid = useRef(row._meta.uuid);
29+
const previousHeight = useRef(style.height);
30+
3031
const [open, setOpen] = useState(renderOpen);
3132
const [heightChanging, setHeightChanging] = useState(false);
3233

@@ -39,7 +40,7 @@ export function LogsTableRow({
3940
}
4041

4142
previousHeight.current = rowSizeHeight;
42-
rowExpanded(rowSizeHeight);
43+
rowExpanded(uuid.current, rowSizeHeight);
4344
setHeightChanging(false);
4445
}, [rowExpanded, rowSizeHeight]);
4546

@@ -48,19 +49,17 @@ export function LogsTableRow({
4849

4950
setHeightChanging(true);
5051
setOpen(newVal);
51-
rowOpened(newVal);
52+
rowOpened(uuid.current, newVal);
5253
};
5354

54-
const uuid = row._meta.uuid;
5555
const WaitingComponent =
56-
uuid === UUID_NEWEST_LOG
56+
uuid.current === UUID_NEWEST_LOG
5757
? WaitingForNewLogsRow
58-
: uuid === UUID_OLDEST_LOG
58+
: uuid.current === UUID_OLDEST_LOG
5959
? WaitingForOldLogsRow
6060
: null;
6161

6262
if (WaitingComponent) {
63-
// The fields thing is pretty janky and hacky but it worked and made this much easier
6463
return <WaitingComponent style={style} sizeRef={sizeRef} />;
6564
}
6665

src/components/tables/Logs/WaitingForRow/Base.tsx

+43-33
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { Box, TableCell, TableRow, Typography, useTheme } from '@mui/material';
22
import SpinnerIcon from 'components/logs/SpinnerIcon';
33
import { BaseTypographySx } from 'components/tables/cells/logs/shared';
4-
import { DEFAULT_POLLING } from 'context/SWR';
54
import {
65
errorOutlinedButtonBackground,
76
tableRowActive_Finished__Background,
@@ -17,74 +16,85 @@ import {
1716
useJournalDataLogsStore_fetchMoreLogs,
1817
useJournalDataLogsStore_lastFetchFailed,
1918
} from 'stores/JournalData/Logs/hooks';
19+
import { VIRTUAL_TABLE_BODY_PADDING } from '../shared';
2020
import { FetchMoreLogsOptions, WaitingForRowProps } from '../types';
2121

2222
interface Props extends WaitingForRowProps {
2323
fetchOption: FetchMoreLogsOptions;
2424
disabled?: boolean;
25+
interval?: number;
2526
}
2627

27-
function WaitingForRowBase({ disabled, fetchOption, sizeRef, style }: Props) {
28+
function WaitingForRowBase({
29+
disabled,
30+
interval = 500,
31+
fetchOption,
32+
sizeRef,
33+
style,
34+
}: Props) {
2835
const theme = useTheme();
2936

30-
const runFetch = useRef(true);
31-
32-
const [intervalLength, setIntervalLength] = useState(500);
37+
const [allowFetch, setAllowFetch] = useState(false);
3338

3439
const intersectionRef = useRef<HTMLElement>(null);
3540
const intersection = useIntersection(intersectionRef, {
3641
root: null,
37-
rootMargin: '0px',
42+
rootMargin: `${VIRTUAL_TABLE_BODY_PADDING}px`,
3843
threshold: 0.7,
3944
});
4045

4146
const messageKey = `ops.logsTable.waitingForLogs.${fetchOption}`;
4247

4348
const lastFetchFailed = useJournalDataLogsStore_lastFetchFailed();
4449
const fetchMoreLogs = useJournalDataLogsStore_fetchMoreLogs();
50+
51+
// Kinda hacky - but checking this flag here keeps the effect trigger
52+
// as it is flipped back and forth
4553
const fetchingMore = useJournalDataLogsStore_fetchingMore();
4654

4755
const fetchMore = useCallback(() => {
48-
if (lastFetchFailed) {
49-
return;
50-
}
51-
if (!fetchingMore && intersection?.isIntersecting) {
52-
runFetch.current = false;
53-
54-
// When checking for new ones fall back to give some time
55-
// for the entity to actually write logs
56-
if (fetchOption === 'new') {
57-
setIntervalLength(DEFAULT_POLLING);
58-
}
59-
fetchMoreLogs(fetchOption);
60-
} else {
61-
runFetch.current = true;
62-
}
63-
}, [
64-
fetchMoreLogs,
65-
fetchOption,
66-
fetchingMore,
67-
intersection?.isIntersecting,
68-
lastFetchFailed,
69-
]);
56+
setAllowFetch(false);
57+
fetchMoreLogs(fetchOption);
58+
}, [fetchMoreLogs, fetchOption]);
7059

60+
// Cannot figure out the deps
7161
// eslint-disable-next-line react-hooks/exhaustive-deps
72-
const debouncedFetch = useCallback(debounce(fetchMore, intervalLength), [
62+
const debouncedFetch = useCallback(debounce(fetchMore, interval), [
7363
fetchMore,
74-
intervalLength,
7564
]);
7665

66+
// If at anytime the row is not visible cancel any ongoing loading
7767
useEffect(() => {
78-
if (!lastFetchFailed && !disabled && intersection?.isIntersecting) {
79-
debouncedFetch();
68+
if (!intersection?.isIntersecting) {
69+
debouncedFetch.cancel();
8070
}
71+
}, [debouncedFetch, intersection?.isIntersecting]);
72+
73+
// Keeping all this logic in a stand alone effect/state because we might
74+
// need to expand this beyond just checking some simple booleans
75+
useEffect(() => {
76+
setAllowFetch(
77+
Boolean(
78+
!fetchingMore &&
79+
!lastFetchFailed &&
80+
!disabled &&
81+
intersection?.isIntersecting
82+
)
83+
);
8184
}, [
8285
debouncedFetch,
8386
disabled,
87+
fetchingMore,
8488
intersection?.isIntersecting,
8589
lastFetchFailed,
8690
]);
8791

92+
useEffect(() => {
93+
if (allowFetch) {
94+
debouncedFetch();
95+
}
96+
}, [allowFetch, debouncedFetch]);
97+
8898
return (
8999
<TableRow
90100
component={Box}
@@ -100,7 +110,7 @@ function WaitingForRowBase({ disabled, fetchOption, sizeRef, style }: Props) {
100110
lastFetchFailed || disabled || intersection?.isIntersecting
101111
? 1
102112
: 0,
103-
transition: 'all 50ms ease-in-out',
113+
transition: 'all 100ms ease-in-out',
104114
}}
105115
>
106116
<Box ref={intersectionRef}>
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,16 @@
1+
import { DEFAULT_POLLING } from 'context/SWR';
12
import { WaitingForRowProps } from '../types';
23
import WaitingForRowBase from './Base';
34

45
function WaitingForNewLogsRow(props: WaitingForRowProps) {
5-
return <WaitingForRowBase {...props} fetchOption="new" disabled={false} />;
6+
return (
7+
<WaitingForRowBase
8+
{...props}
9+
fetchOption="new"
10+
disabled={false}
11+
interval={DEFAULT_POLLING}
12+
/>
13+
);
614
}
715

816
export default WaitingForNewLogsRow;

src/components/tables/Logs/shared.ts

+3
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,12 @@ export const maxBytes = Math.round(MEGABYTE / 10);
88
// row should render expanded
99
export const DEFAULT_ROW_HEIGHT = 55;
1010
export const DEFAULT_ROW_HEIGHT_WITHOUT_FIELDS = 35;
11+
export const WAITING_ROW_HEIGHT = 37; // This is just a bit taller due to the spinner used
1112

1213
export const UUID_START_OF_LOGS = 'UI-start-of-logs';
1314
export const UUID_OLDEST_LOG = 'UI-oldest-log-line';
1415
export const UUID_NEWEST_LOG = 'UI-newest-log-line';
1516

1617
export const EXPAND_ROW_TRANSITION = 200;
18+
19+
export const VIRTUAL_TABLE_BODY_PADDING = 10;

src/components/tables/Logs/types.ts

+9-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { LoadDocumentsOffsets } from 'hooks/journals/shared';
1+
import { LoadDocumentsOffsets } from 'hooks/journals/types';
22
import { CSSProperties, RefCallback } from 'react';
33

44
export type FetchMoreLogsOptions = 'old' | 'new';
@@ -10,3 +10,11 @@ export interface WaitingForRowProps {
1010
}
1111

1212
export type RefreshLogsFunction = (newOffset?: LoadDocumentsOffsets) => void;
13+
14+
export type LogLevels =
15+
| 'error'
16+
| 'warn'
17+
| 'debug'
18+
| 'trace'
19+
| 'done'
20+
| 'ui_waiting';

0 commit comments

Comments
 (0)