Skip to content

Commit

Permalink
fix(archive): all-archive should properly display when refreshing
Browse files Browse the repository at this point in the history
  • Loading branch information
tthvo committed Oct 12, 2022
1 parent c8dbc3d commit 591a498
Show file tree
Hide file tree
Showing 6 changed files with 96 additions and 94 deletions.
134 changes: 66 additions & 68 deletions src/app/Archives/AllTargetsArchivedRecordingsTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
*/
import * as React from 'react';
import { ServiceContext } from '@app/Shared/Services/Services';
import { Target } from '@app/Shared/Services/Target.service';
import { includesTarget, indexOfTarget, isEqualTarget, Target } from '@app/Shared/Services/Target.service';
import { NotificationCategory } from '@app/Shared/Services/NotificationChannel.service';
import { useSubscriptions } from '@app/utils/useSubscriptions';
import {
Expand All @@ -56,10 +56,9 @@ import { TableComposable, Th, Thead, Tbody, Tr, Td, ExpandableRowContent } from
import SearchIcon from '@patternfly/react-icons/dist/esm/icons/search-icon';
import { ArchivedRecordingsTable } from '@app/Recordings/ArchivedRecordingsTable';
import { of } from 'rxjs';
import { concatMap, map } from 'rxjs/operators';
import { map } from 'rxjs/operators';
import { TargetDiscoveryEvent } from '@app/Shared/Services/Targets.service';
import { LoadingView } from '@app/LoadingView/LoadingView';
import _ from 'lodash';

export interface AllTargetsArchivedRecordingsTableProps {}

Expand All @@ -68,45 +67,39 @@ export const AllTargetsArchivedRecordingsTable: React.FunctionComponent<AllTarge
const context = React.useContext(ServiceContext);

const [targets, setTargets] = React.useState([] as Target[]);
const [counts, setCounts] = React.useState([] as number[]);
const [search, setSearch] = React.useState('');
const [counts, setCounts] = React.useState(new Map<string, number>());
const [searchText, setSearchText] = React.useState('');
const [searchedTargets, setSearchedTargets] = React.useState([] as Target[]);
const [expandedTargets, setExpandedTargets] = React.useState([] as Target[]);
const [hideEmptyTargets, setHideEmptyTargets] = React.useState(true);
const [isLoading, setIsLoading] = React.useState(false);
const addSubscription = useSubscriptions();

const searchedTargetsRef = React.useRef(searchedTargets);

const tableColumns: string[] = ['Target', 'Count'];
const tableColumns: string[] = React.useMemo(() => ['Target', 'Count'], []);

const updateCount = React.useCallback(
(connectUrl: string, delta: number) => {
for (let i = 0; i < targets.length; i++) {
if (targets[i].connectUrl === connectUrl) {
setCounts((old) => {
let updated = [...old];
updated[i] += delta;
return updated;
});
break;
}
}
setCounts((old) => {
const newMap = new Map<string, number>(old);
const curr = newMap.get(connectUrl) || 0;
newMap.set(connectUrl, curr + delta);
return newMap;
});
},
[targets, setCounts]
[setCounts]
);

const handleTargetsAndCounts = React.useCallback(
(targetNodes) => {
let updatedTargets: Target[] = [];
let updatedCounts: number[] = [];
(targetNodes: any) => {
const updatedTargets: Target[] = [];
const updatedCounts = new Map<string, number>();
for (const node of targetNodes) {
const target: Target = {
connectUrl: node.target.serviceUri,
alias: node.target.alias,
};
updatedTargets.push(target);
updatedCounts.push(node.recordings.archived.aggregate.count as number);
updatedCounts.set(target.connectUrl, node.recordings.archived.aggregate.count as number);
}
setTargets(updatedTargets);
setCounts(updatedCounts);
Expand Down Expand Up @@ -161,7 +154,11 @@ export const AllTargetsArchivedRecordingsTable: React.FunctionComponent<AllTarge
}`
)
.subscribe((v) =>
setCounts((old) => old.concat(v.data.targetNodes[0].recordings.archived.aggregate.count as number))
setCounts((old) => {
const newMap = new Map<string, number>(old);
newMap.set(target.connectUrl, v.data.targetNodes[0].recordings.archived.aggregate.count as number);
return newMap;
})
)
);
},
Expand All @@ -170,18 +167,14 @@ export const AllTargetsArchivedRecordingsTable: React.FunctionComponent<AllTarge

const handleLostTarget = React.useCallback(
(target: Target) => {
let idx;
setTargets((old) => {
for (idx = 0; idx < old.length; idx++) {
if (_.isEqual(target, old[idx])) break;
}
return old.filter((o) => !_.isEqual(o, target));
return old.filter((t) => !isEqualTarget(t, target));
});
setExpandedTargets((old) => old.filter((o) => !_.isEqual(o, target)));
setExpandedTargets((old) => old.filter((t) => !isEqualTarget(t, target)));
setCounts((old) => {
let updated = [...old];
updated.splice(idx, 1);
return updated;
const newMap = new Map<string, number>(old);
newMap.delete(target.connectUrl);
return newMap;
});
},
[setTargets, setExpandedTargets, setCounts]
Expand All @@ -203,13 +196,35 @@ export const AllTargetsArchivedRecordingsTable: React.FunctionComponent<AllTarge
[setTargets, getCountForNewTarget, handleLostTarget]
);

const handleSearchInput = React.useCallback(
(searchInput) => {
setSearchText(searchInput);
},
[setSearchText]
);

const handleSearchInputClear = React.useCallback(() => {
handleSearchInput('');
}, [handleSearchInput]);

React.useEffect(() => {
refreshTargetsAndCounts();
}, []);
}, [refreshTargetsAndCounts]);

React.useEffect(() => {
searchedTargetsRef.current = searchedTargets;
});
let updatedSearchedTargets: Target[];
if (!searchText) {
updatedSearchedTargets = targets;
} else {
const formattedSearchText = searchText.trim().toLowerCase();
updatedSearchedTargets = targets.filter(
(t: Target) =>
t.alias.toLowerCase().includes(formattedSearchText) ||
t.connectUrl.toLowerCase().includes(formattedSearchText)
);
}
setSearchedTargets(updatedSearchedTargets);
}, [searchText, targets, setSearchedTargets]);

React.useEffect(() => {
if (!context.settings.autoRefreshEnabled()) {
Expand All @@ -222,28 +237,11 @@ export const AllTargetsArchivedRecordingsTable: React.FunctionComponent<AllTarge
return () => window.clearInterval(id);
}, [context.target, context.settings, refreshTargetsAndCounts]);

React.useEffect(() => {
let updatedSearchedTargets;
if (!search) {
updatedSearchedTargets = targets;
} else {
const searchText = search.trim().toLowerCase();
updatedSearchedTargets = targets.filter(
(t: Target) => t.alias.toLowerCase().includes(searchText) || t.connectUrl.toLowerCase().includes(searchText)
);
}

if (!_.isEqual(searchedTargetsRef.current, updatedSearchedTargets)) {
setSearchedTargets(updatedSearchedTargets);
}
}, [search, targets]);

React.useEffect(() => {
addSubscription(
context.notificationChannel
.messages(NotificationCategory.TargetJvmDiscovery)
.pipe(concatMap((v) => of(handleTargetNotification(v.message.event))))
.subscribe(() => {} /* do nothing - callback will have already handled updating state */)
.subscribe((v) => handleTargetNotification(v.message.event))
);
}, [addSubscription, context, context.notificationChannel, handleTargetNotification]);

Expand All @@ -265,30 +263,30 @@ export const AllTargetsArchivedRecordingsTable: React.FunctionComponent<AllTarge

const toggleExpanded = React.useCallback(
(target) => {
const idx = expandedTargets.indexOf(target);
const idx = indexOfTarget(expandedTargets, target);
setExpandedTargets((expandedTargets) =>
idx >= 0
? [...expandedTargets.slice(0, idx), ...expandedTargets.slice(idx + 1, expandedTargets.length)]
: [...expandedTargets, target]
);
},
[expandedTargets]
[expandedTargets, setExpandedTargets]
);

const isHidden = React.useMemo(() => {
let isHidden: boolean[] = [];
targets.map((target, idx) => {
isHidden.push(!searchedTargets.includes(target) || (hideEmptyTargets && counts[idx] === 0));
return targets.map((target) => {
return (
!includesTarget(searchedTargets, target) || (hideEmptyTargets && (counts.get(target.connectUrl) || 0) === 0)
);
});
return isHidden;
}, [targets, searchedTargets, hideEmptyTargets, counts]);

const targetRows = React.useMemo(() => {
return targets.map((target, idx) => {
let isExpanded: boolean = expandedTargets.includes(target);
let isExpanded: boolean = includesTarget(expandedTargets, target);

const handleToggle = () => {
if (counts[idx] !== 0 || isExpanded) {
if ((counts.get(target.connectUrl) || 0) !== 0 || isExpanded) {
toggleExpanded(target);
}
};
Expand All @@ -311,7 +309,7 @@ export const AllTargetsArchivedRecordingsTable: React.FunctionComponent<AllTarge
: `${target.alias} (${target.connectUrl})`}
</Td>
<Td key={`target-table-row-${idx}_3`}>
<Badge key={`${idx}_count`}>{counts[idx]}</Badge>
<Badge key={`${idx}_count`}>{counts.get(target.connectUrl) || 0}</Badge>
</Td>
</Tr>
);
Expand All @@ -320,7 +318,7 @@ export const AllTargetsArchivedRecordingsTable: React.FunctionComponent<AllTarge

const recordingRows = React.useMemo(() => {
return targets.map((target, idx) => {
let isExpanded: boolean = expandedTargets.includes(target);
let isExpanded: boolean = includesTarget(expandedTargets, target);

return (
<Tr key={`${idx}_child`} isExpanded={isExpanded} isHidden={isHidden[idx]}>
Expand Down Expand Up @@ -391,9 +389,9 @@ export const AllTargetsArchivedRecordingsTable: React.FunctionComponent<AllTarge
<ToolbarItem>
<SearchInput
placeholder="Search"
value={search}
onChange={setSearch}
onClear={(evt) => setSearch('')}
value={searchText}
onChange={handleSearchInput}
onClear={handleSearchInputClear}
/>
</ToolbarItem>
</ToolbarGroup>
Expand All @@ -402,7 +400,7 @@ export const AllTargetsArchivedRecordingsTable: React.FunctionComponent<AllTarge
<Checkbox
name={`all-archives-hide-check`}
label="Hide targets with zero recordings"
onChange={(v) => setHideEmptyTargets((old) => !old)}
onChange={setHideEmptyTargets}
isChecked={hideEmptyTargets}
id={`all-archives-hide-check`}
aria-label={`all-archives-hide-check`}
Expand Down
9 changes: 6 additions & 3 deletions src/app/Archives/Archives.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ import { ArchivedRecordingsTable } from '@app/Recordings/ArchivedRecordingsTable
import { Target } from '@app/Shared/Services/Target.service';
import { of } from 'rxjs';
import { UPLOADS_SUBDIRECTORY } from '@app/Shared/Services/Api.service';
import { useSubscriptions } from '@app/utils/useSubscriptions';

/*
This specific target is used as the "source" for the Uploads version of the ArchivedRecordingsTable.
Expand All @@ -58,14 +59,16 @@ export const uploadAsTarget: Target = {
alias: '',
};

export const Archives = () => {
export interface ArchivesProps {}

export const Archives: React.FunctionComponent<ArchivesProps> = () => {
const context = React.useContext(ServiceContext);
const addSubscription = useSubscriptions();
const [activeTab, setActiveTab] = React.useState(0);
const [archiveEnabled, setArchiveEnabled] = React.useState(false);

React.useEffect(() => {
const sub = context.api.isArchiveEnabled().subscribe(setArchiveEnabled);
return () => sub.unsubscribe();
addSubscription(context.api.isArchiveEnabled().subscribe(setArchiveEnabled));
}, [context.api]);

const cardBody = React.useMemo(() => {
Expand Down
3 changes: 1 addition & 2 deletions src/app/Recordings/ActiveRecordingsTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -500,9 +500,8 @@ export const ActiveRecordingsTable: React.FunctionComponent<ActiveRecordingsTabl
props.recording.maxAge,
props.recording.maxSize,
]);

return (
<Tbody key={props.index} isExpanded={isExpanded[props.index]}>
<Tbody key={props.index} isExpanded={isExpanded}>
{parentRow}
{childRow}
</Tbody>
Expand Down
2 changes: 1 addition & 1 deletion src/app/Recordings/ArchivedRecordingsTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -441,7 +441,7 @@ export const ArchivedRecordingsTable: React.FunctionComponent<ArchivedRecordings
}, [props.recording, props.recording.name, props.index, isExpanded, tableColumns]);

return (
<Tbody key={props.index} isExpanded={isExpanded[props.index]}>
<Tbody key={props.index} isExpanded={isExpanded}>
{parentRow}
{childRow}
</Tbody>
Expand Down
18 changes: 18 additions & 0 deletions src/app/Shared/Services/Target.service.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,24 @@ import { Observable, Subject, BehaviorSubject } from 'rxjs';

export const NO_TARGET = {} as Target;

export const includesTarget = (arr: Target[], target: Target): boolean => {
return arr.some((t) => t.connectUrl === target.connectUrl);
};

export const isEqualTarget = (a: Target, b: Target): boolean => {
return a.connectUrl === b.connectUrl;
};

export const indexOfTarget = (arr: Target[], target: Target): number => {
let index = -1;
arr.forEach((t, idx) => {
if (t.connectUrl === target.connectUrl) {
index = idx;
}
});
return index;
};

export interface Target {
connectUrl: string;
alias: string;
Expand Down
Loading

0 comments on commit 591a498

Please sign in to comment.