From 69d43b4d7e90ad8506403bdb03f4206229c9c01c Mon Sep 17 00:00:00 2001 From: David Echelberger Date: Sun, 27 Mar 2022 20:45:38 -0400 Subject: [PATCH 1/5] [bugfixes] slideover mounting Signed-off-by: David Echelberger --- src/components/Lists/TransferList.tsx | 16 ++++++++++++++-- src/components/Lists/TxList.tsx | 18 +++++++++++++++--- src/components/Slides/EventSlide.tsx | 4 ++-- src/components/Slides/InterfaceSlide.tsx | 2 +- src/components/Slides/MessageSlide.tsx | 2 +- src/components/Slides/TransactionSlide.tsx | 2 +- src/components/Slides/TransferSlide.tsx | 2 +- 7 files changed, 35 insertions(+), 11 deletions(-) diff --git a/src/components/Lists/TransferList.tsx b/src/components/Lists/TransferList.tsx index 937b88ee..eb484c5c 100644 --- a/src/components/Lists/TransferList.tsx +++ b/src/components/Lists/TransferList.tsx @@ -119,9 +119,21 @@ export const TransferList: React.FC = ({ { label: t('status'), value: txStatus && ( + // TODO: Fix when https://github.com/hyperledger/firefly/issues/628 is resolved ), }, diff --git a/src/components/Lists/TxList.tsx b/src/components/Lists/TxList.tsx index 26f60da1..b8f790a5 100644 --- a/src/components/Lists/TxList.tsx +++ b/src/components/Lists/TxList.tsx @@ -65,10 +65,22 @@ export const TxList: React.FC = ({ { label: t('status'), value: txStatus && ( + // TODO: Fix when https://github.com/hyperledger/firefly/issues/628 is resolved + label={ + txStatus.status?.toLocaleUpperCase() === 'PENDING' + ? 'SUCCEEDED' + : txStatus.status?.toLocaleUpperCase() + } + sx={{ + backgroundColor: + TxStatusColorMap[ + txStatus.status === 'Pending' + ? 'Succeeded' + : txStatus.status + ], + }} + > ), }, { diff --git a/src/components/Slides/EventSlide.tsx b/src/components/Slides/EventSlide.tsx index 6c1912e1..c6c7493a 100644 --- a/src/components/Slides/EventSlide.tsx +++ b/src/components/Slides/EventSlide.tsx @@ -73,7 +73,7 @@ export const EventSlide: React.FC = ({ event, open, onClose }) => { .catch((err) => { reportFetchError(err); }); - }, [event]); + }, [event, isMounted]); useEffect(() => { if (enrichedEvent && isMounted) { @@ -90,7 +90,7 @@ export const EventSlide: React.FC = ({ event, open, onClose }) => { reportFetchError(err); }); } - }, [enrichedEvent]); + }, [enrichedEvent, isMounted]); return ( <> diff --git a/src/components/Slides/InterfaceSlide.tsx b/src/components/Slides/InterfaceSlide.tsx index d9a77f6d..6bb0f3fb 100644 --- a/src/components/Slides/InterfaceSlide.tsx +++ b/src/components/Slides/InterfaceSlide.tsx @@ -88,7 +88,7 @@ export const InterfaceSlide: React.FC = ({ .catch((err) => { reportFetchError(err); }); - }, [cInterface]); + }, [cInterface, isMounted]); return ( <> diff --git a/src/components/Slides/MessageSlide.tsx b/src/components/Slides/MessageSlide.tsx index ea7bd119..fbebba24 100644 --- a/src/components/Slides/MessageSlide.tsx +++ b/src/components/Slides/MessageSlide.tsx @@ -85,7 +85,7 @@ export const MessageSlide: React.FC = ({ message, open, onClose }) => { .catch((err) => { reportFetchError(err); }); - }, [message]); + }, [message, isMounted]); return ( <> diff --git a/src/components/Slides/TransactionSlide.tsx b/src/components/Slides/TransactionSlide.tsx index f271ec40..9fbad8bf 100644 --- a/src/components/Slides/TransactionSlide.tsx +++ b/src/components/Slides/TransactionSlide.tsx @@ -109,7 +109,7 @@ export const TransactionSlide: React.FC = ({ .catch((err) => { reportFetchError(err); }); - }, [transaction]); + }, [transaction, isMounted]); return ( <> diff --git a/src/components/Slides/TransferSlide.tsx b/src/components/Slides/TransferSlide.tsx index cad097cf..ad4731e9 100644 --- a/src/components/Slides/TransferSlide.tsx +++ b/src/components/Slides/TransferSlide.tsx @@ -110,7 +110,7 @@ export const TransferSlide: React.FC = ({ transfer, open, onClose }) => { .catch((err) => { reportFetchError(err); }); - }, [transfer]); + }, [transfer, isMounted]); return ( <> From 550dfadca50a9f672e2f97e7846da7ba77139d27 Mon Sep 17 00:00:00 2001 From: David Echelberger Date: Mon, 28 Mar 2022 13:33:02 -0400 Subject: [PATCH 2/5] [bugfixes] histogram bugfix and newEvents refreshing Signed-off-by: David Echelberger --- src/App.tsx | 23 +++- src/components/Cards/EventCards/BaseCard.tsx | 4 +- .../Cards/EventCards/EventCardWrapper.tsx | 2 - src/components/Charts/Histogram.tsx | 4 +- src/components/Header.tsx | 12 +- src/components/NetworkMap/NetworkMap.tsx | 19 ++- src/components/Pickers/DatePicker.tsx | 6 +- src/components/Timeline/FFTimeline.tsx | 10 +- src/contexts/ApplicationContext.tsx | 7 +- src/contexts/DateFilterContext.tsx | 6 +- src/contexts/SlideContext.tsx | 4 +- src/interfaces/filters.ts | 13 +- src/pages/Activity/views/Events.tsx | 51 +++----- src/pages/Activity/views/Operations.tsx | 48 +++----- .../Activity/views/TransactionDetails.tsx | 18 +-- src/pages/Activity/views/Transactions.tsx | 75 ++++++------ src/pages/Blockchain/views/Apis.tsx | 31 ++--- src/pages/Blockchain/views/Dashboard.tsx | 39 +++--- src/pages/Blockchain/views/Events.tsx | 36 ++---- src/pages/Blockchain/views/Interfaces.tsx | 43 ++----- src/pages/Blockchain/views/Listeners.tsx | 17 ++- src/pages/Home/views/Dashboard.tsx | 74 ++++++------ src/pages/Off-Chain/views/Dashboard.tsx | 56 ++++----- src/pages/Off-Chain/views/Data.tsx | 46 ++----- src/pages/Off-Chain/views/DataTypes.tsx | 43 ++----- src/pages/Off-Chain/views/Messages.tsx | 49 +++----- src/pages/Tokens/views/Dashboard.tsx | 55 ++++----- src/pages/Tokens/views/PoolDetails.tsx | 13 +- src/pages/Tokens/views/Pools.tsx | 32 ++--- src/pages/Tokens/views/Transfers.tsx | 49 +++----- src/utils/filters.ts | 8 +- src/utils/wsEvents.tsx | 112 +++++++++--------- 32 files changed, 412 insertions(+), 593 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 32539d0e..73801657 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -31,7 +31,7 @@ import { } from './components/Snackbar/MessageSnackbar'; import { ApplicationContext } from './contexts/ApplicationContext'; import { SnackbarContext } from './contexts/SnackbarContext'; -import { INamespace, IStatus, NAMESPACES_PATH } from './interfaces'; +import { FF_EVENTS, INamespace, IStatus, NAMESPACES_PATH } from './interfaces'; import { FF_Paths } from './interfaces/constants'; import { themeOptions } from './theme'; import { fetchWithCredentials, summarizeFetchError } from './utils'; @@ -50,7 +50,6 @@ const App: React.FC = () => { const [selectedNamespace, setSelectedNamespace] = useState(''); const ws = useRef(null); const [identity, setIdentity] = useState(''); - const [lastEvent, setLastEvent] = useState(); const [message, setMessage] = useState(''); const [messageType, setMessageType] = useState('error'); const [orgID, setOrgID] = useState(''); @@ -58,6 +57,11 @@ const App: React.FC = () => { const [nodeID, setNodeID] = useState(''); const [nodeName, setNodeName] = useState(''); const protocol = window.location.protocol === 'https:' ? 'wss' : 'ws'; + // Event Context + const [newEvents, setNewEvents] = useState([]); + const [lastRefreshTime, setLastRefresh] = useState( + new Date().toISOString() + ); const { pathname: currentPath } = useMemo(() => { return window.location; @@ -116,7 +120,10 @@ const App: React.FC = () => { ); ws.current.onmessage = (event: any) => { const eventData = JSON.parse(event.data); - setLastEvent(eventData); + const eventType: FF_EVENTS = eventData.type; + if (Object.values(FF_EVENTS).includes(eventType)) { + setNewEvents((existing) => [eventType, ...existing]); + } }; return () => { @@ -134,6 +141,11 @@ const App: React.FC = () => { }); }; + const clearNewEvents = () => { + setNewEvents([]); + setLastRefresh(new Date().toISOString()); + }; + if (initialized) { if (initError) { // figure out what to display @@ -158,8 +170,9 @@ const App: React.FC = () => { nodeID, nodeName, identity, - lastEvent, - setLastEvent, + newEvents, + clearNewEvents, + lastRefreshTime, }} > void; link?: string; - linkState?: any; } export const BaseCard: React.FC = ({ @@ -44,7 +43,6 @@ export const BaseCard: React.FC = ({ color, onClick, link, - linkState, }) => { const navigate = useNavigate(); @@ -106,7 +104,7 @@ export const BaseCard: React.FC = ({ {link && ( navigate(link, linkState ?? undefined)} + onClick={() => navigate(link)} sx={{ elevation: 0, backgroundColor: 'background.paper', diff --git a/src/components/Cards/EventCards/EventCardWrapper.tsx b/src/components/Cards/EventCards/EventCardWrapper.tsx index 8eb78df7..2e472e4a 100644 --- a/src/components/Cards/EventCards/EventCardWrapper.tsx +++ b/src/components/Cards/EventCards/EventCardWrapper.tsx @@ -23,7 +23,6 @@ export const EventCardWrapper = ({ onHandleViewEvent, onHandleViewTx, link, - linkState, }: Props) => { const { t } = useTranslation(); @@ -45,7 +44,6 @@ export const EventCardWrapper = ({ status={} color={FF_EVENTS_CATEGORY_MAP[event.type]?.color} link={link} - linkState={linkState} /> ); diff --git a/src/components/Charts/Histogram.tsx b/src/components/Charts/Histogram.tsx index 608c4b42..d459db65 100644 --- a/src/components/Charts/Histogram.tsx +++ b/src/components/Charts/Histogram.tsx @@ -21,6 +21,7 @@ interface Props { indexBy: string; isEmpty: boolean; keys: string[]; + isLoading: boolean; } export const Histogram: React.FC = ({ @@ -32,6 +33,7 @@ export const Histogram: React.FC = ({ indexBy, isEmpty, keys, + isLoading, }) => { const [xAxisValues, setXAxisValues] = useState<(string | number)[]>([]); @@ -51,7 +53,7 @@ export const Histogram: React.FC = ({ backgroundColor: 'background.paper', }} > - {!data ? ( + {!data || isLoading ? ( ) : isEmpty ? ( diff --git a/src/components/Header.tsx b/src/components/Header.tsx index 6c5509ef..b6a7e48a 100644 --- a/src/components/Header.tsx +++ b/src/components/Header.tsx @@ -33,8 +33,7 @@ interface Props { title: string | JSX.Element; subtitle: string; onRefresh?: any; - numNewEvents?: number; - showNumNewEvents?: boolean; + showRefreshBtn?: boolean; noDateFilter?: boolean; noNsFilter?: boolean; } @@ -43,8 +42,7 @@ export const Header: React.FC = ({ title, subtitle, onRefresh, - numNewEvents = 0, - showNumNewEvents = false, + showRefreshBtn = false, noDateFilter = false, noNsFilter = false, }) => { @@ -88,7 +86,7 @@ export const Header: React.FC = ({ alignItems="center" > - {numNewEvents > 0 && ( + {showRefreshBtn && ( )} diff --git a/src/components/NetworkMap/NetworkMap.tsx b/src/components/NetworkMap/NetworkMap.tsx index 4a49e1a2..332d9a0a 100644 --- a/src/components/NetworkMap/NetworkMap.tsx +++ b/src/components/NetworkMap/NetworkMap.tsx @@ -171,7 +171,14 @@ export const NetworkMap: React.FC = ({ size }) => { ?.split(NODE_STRING_DELIM) .map((key: any, idx: number) => { return ( - + = ({ size }) => { return 1 + 1 * n.target.data.height; }} linkColor={'#9BA7B0'} - motionConfig={{ - mass: 1, - tension: 1, - friction: 1, - clamp: false, - precision: 0.01, - velocity: 0, - }} animate onClick={handleClick} nodeTooltip={({ node }) => { @@ -296,7 +295,7 @@ export const NetworkMap: React.FC = ({ size }) => { {anchorPosition && ( { const { t } = useTranslation(); - const { addDateToParams, searchParams } = useContext(DateFilterContext); + const { setTimeSearchParam, searchParams } = useContext(DateFilterContext); const createdQueryOptions = useMemo( () => [ @@ -55,7 +55,7 @@ export const DatePicker: React.FC = () => { variant="outlined" value={searchParams.get(TIME_QUERY_KEY) ?? ''} onChange={(event) => { - addDateToParams(event.target.value as CreatedFilterOptions); + setTimeSearchParam(event.target.value as TimeFilterEnum); }} sx={{ pr: 2 }} > diff --git a/src/components/Timeline/FFTimeline.tsx b/src/components/Timeline/FFTimeline.tsx index 51b6ca9a..06ab129a 100644 --- a/src/components/Timeline/FFTimeline.tsx +++ b/src/components/Timeline/FFTimeline.tsx @@ -33,7 +33,7 @@ interface Props { hasMoreData: boolean | undefined; height?: string | number; fetchNewData: any; - numNewEvents: number; + hasNewEvents: boolean; } export const FFTimeline: React.FC = ({ @@ -43,16 +43,16 @@ export const FFTimeline: React.FC = ({ hasMoreData = false, height, fetchNewData, - numNewEvents, + hasNewEvents, }) => { const { t } = useTranslation(); const myRef: any = useRef(null); useEffect(() => { - if (numNewEvents === 0 && myRef?.current) { + if (!hasNewEvents && myRef?.current) { myRef.current.scrollIntoView({ behavior: 'smooth' }); } - }, [numNewEvents]); + }, [hasNewEvents]); return ( = ({ position: 'relative', }} > - {numNewEvents > 0 && ( + {hasNewEvents && ( >; selectedNamespace: string; setSelectedNamespace: Dispatch>; namespaces: INamespace[]; + newEvents: FF_EVENTS[]; + clearNewEvents: () => void; + lastRefreshTime: string; } export const ApplicationContext = createContext({} as IApplicationContext); diff --git a/src/contexts/DateFilterContext.tsx b/src/contexts/DateFilterContext.tsx index aad3907a..c1bf1f96 100644 --- a/src/contexts/DateFilterContext.tsx +++ b/src/contexts/DateFilterContext.tsx @@ -15,12 +15,12 @@ // limitations under the License. import { createContext } from 'react'; -import { CreatedFilterOptions, ICreatedTimeFilter } from '../interfaces'; +import { ITimeFilterObject, TimeFilterEnum } from '../interfaces'; export interface IDateFilterContext { searchParams: URLSearchParams; - dateFilter: ICreatedTimeFilter; - addDateToParams: (timeFilterString: CreatedFilterOptions) => void; + dateFilter: ITimeFilterObject | undefined; + setTimeSearchParam: (timeFilter: TimeFilterEnum) => void; } export const DateFilterContext = createContext({} as IDateFilterContext); diff --git a/src/contexts/SlideContext.tsx b/src/contexts/SlideContext.tsx index 473deb9c..3fc07c11 100644 --- a/src/contexts/SlideContext.tsx +++ b/src/contexts/SlideContext.tsx @@ -17,8 +17,8 @@ import { createContext } from 'react'; export interface ISlideContext { - slideQuery: string | null; - addSlideToParams: (slideID: string | undefined) => void; + slideID: string | null; + setSlideSearchParam: (slideID: string | null) => void; } export const SlideContext = createContext({} as ISlideContext); diff --git a/src/interfaces/filters.ts b/src/interfaces/filters.ts index 0f344a3a..0c6f5c83 100644 --- a/src/interfaces/filters.ts +++ b/src/interfaces/filters.ts @@ -14,13 +14,20 @@ // See the License for the specific language governing permissions and // limitations under the License. -export interface ICreatedTimeFilter { +export interface ITimeFilterObject { filterString: string; - filterShortString: CreatedFilterOptions; + filterShortString: any; filterTime: number; } -export type CreatedFilterOptions = '1hour' | '24hours' | '7days' | '30days'; +export const times = ['1hour', '24hours', '7days', '30days']; + +export enum TimeFilterEnum { + '1hour' = '1hour', + '24hours' = '24hours', + '7days' = '7days', + '30days' = '30days', +} export const ApiFilters = ['id', 'name', 'interface']; diff --git a/src/pages/Activity/views/Events.tsx b/src/pages/Activity/views/Events.tsx index 147f128a..7c970a4c 100644 --- a/src/pages/Activity/views/Events.tsx +++ b/src/pages/Activity/views/Events.tsx @@ -56,14 +56,14 @@ import { makeColorArray, makeKeyArray, } from '../../../utils/charts'; -import { isEventType, WsEventTypes } from '../../../utils/wsEvents'; export const ActivityEvents: () => JSX.Element = () => { - const { lastEvent, selectedNamespace } = useContext(ApplicationContext); + const { newEvents, lastRefreshTime, clearNewEvents, selectedNamespace } = + useContext(ApplicationContext); const { dateFilter } = useContext(DateFilterContext); const { filterAnchor, setFilterAnchor, filterString } = useContext(FilterContext); - const { slideQuery, addSlideToParams } = useContext(SlideContext); + const { slideID, setSlideSearchParam } = useContext(SlideContext); const { reportFetchError } = useContext(SnackbarContext); const { t } = useTranslation(); const [isMounted, setIsMounted] = useState(false); @@ -77,26 +77,10 @@ export const ActivityEvents: () => JSX.Element = () => { const [viewEvent, setViewEvent] = useState(); const [currentPage, setCurrentPage] = useState(0); const [rowsPerPage, setRowsPerPage] = useState(DEFAULT_PAGE_LIMITS[1]); - // Last event tracking - const [numNewEvents, setNumNewEvents] = useState(0); - const [lastRefreshTime, setLastRefresh] = useState( - new Date().toISOString() - ); - - useEffect(() => { - isMounted && - isEventType(lastEvent, WsEventTypes.EVENT) && - setNumNewEvents(numNewEvents + 1); - }, [lastEvent]); - - const refreshData = () => { - setNumNewEvents(0); - setLastRefresh(new Date().toISOString()); - }; + const [isHistLoading, setIsHistLoading] = useState(false); useEffect(() => { setIsMounted(true); - setNumNewEvents(0); return () => { setIsMounted(false); }; @@ -104,10 +88,10 @@ export const ActivityEvents: () => JSX.Element = () => { useEffect(() => { isMounted && - slideQuery && + slideID && fetchCatcher( `${FF_Paths.nsPrefix}/${selectedNamespace}${FF_Paths.eventsById( - slideQuery + slideID )}` ) .then((eventRes: IEvent) => { @@ -116,11 +100,12 @@ export const ActivityEvents: () => JSX.Element = () => { .catch((err) => { reportFetchError(err); }); - }, [slideQuery, isMounted]); + }, [slideID, isMounted]); // Events list useEffect(() => { isMounted && + dateFilter && fetchCatcher( `${FF_Paths.nsPrefix}/${selectedNamespace}${ FF_Paths.events @@ -132,7 +117,6 @@ export const ActivityEvents: () => JSX.Element = () => { if (isMounted) { setEvents(eventRes.items); setEventTotal(eventRes.total); - numNewEvents !== 0 && setNumNewEvents(0); } }) .catch((err) => { @@ -144,15 +128,16 @@ export const ActivityEvents: () => JSX.Element = () => { selectedNamespace, dateFilter, filterString, - lastRefreshTime, isMounted, + lastRefreshTime, ]); // Histogram useEffect(() => { + setIsHistLoading(true); const currentTime = dayjs().unix(); - isMounted && + dateFilter && fetchCatcher( `${FF_Paths.nsPrefix}/${selectedNamespace}${FF_Paths.chartsHistogram( BucketCollectionEnum.Events, @@ -167,8 +152,9 @@ export const ActivityEvents: () => JSX.Element = () => { .catch((err) => { setEventHistData([]); reportFetchError(err); - }); - }, [selectedNamespace, dateFilter, lastRefreshTime, isMounted]); + }) + .finally(() => setIsHistLoading(false)); + }, [selectedNamespace, dateFilter, isMounted, lastRefreshTime]); const eventsColumnHeaders = [ t('type'), @@ -222,7 +208,7 @@ export const ActivityEvents: () => JSX.Element = () => { ], onClick: () => { setViewEvent(event); - addSlideToParams(event.id); + setSlideSearchParam(event.id); }, leftBorderColor: FF_EVENTS_CATEGORY_MAP[event.type]?.color, }) @@ -233,8 +219,8 @@ export const ActivityEvents: () => JSX.Element = () => {
0} + onRefresh={clearNewEvents} >
@@ -256,6 +242,7 @@ export const ActivityEvents: () => JSX.Element = () => { keys={makeKeyArray(FF_EVENTS_CATEGORY_MAP)} includeLegend={true} emptyText={t('noEvents')} + isLoading={isHistLoading} isEmpty={isHistogramEmpty(eventHistData ?? [])} /> @@ -294,7 +281,7 @@ export const ActivityEvents: () => JSX.Element = () => { open={!!viewEvent} onClose={() => { setViewEvent(undefined); - addSlideToParams(undefined); + setSlideSearchParam(null); }} /> )} diff --git a/src/pages/Activity/views/Operations.tsx b/src/pages/Activity/views/Operations.tsx index a6bb04dd..3ff45615 100644 --- a/src/pages/Activity/views/Operations.tsx +++ b/src/pages/Activity/views/Operations.tsx @@ -62,14 +62,14 @@ import { makeColorArray, makeKeyArray, } from '../../../utils/charts'; -import { isEventType, WsEventTypes } from '../../../utils/wsEvents'; export const ActivityOperations: () => JSX.Element = () => { - const { lastEvent, selectedNamespace } = useContext(ApplicationContext); + const { newEvents, lastRefreshTime, clearNewEvents, selectedNamespace } = + useContext(ApplicationContext); const { dateFilter } = useContext(DateFilterContext); const { filterAnchor, setFilterAnchor, filterString } = useContext(FilterContext); - const { slideQuery, addSlideToParams } = useContext(SlideContext); + const { slideID, setSlideSearchParam } = useContext(SlideContext); const { reportFetchError } = useContext(SnackbarContext); const { t } = useTranslation(); const [isMounted, setIsMounted] = useState(false); @@ -83,27 +83,11 @@ export const ActivityOperations: () => JSX.Element = () => { const [opHistData, setOpHistData] = useState(); const [currentPage, setCurrentPage] = useState(0); const [rowsPerPage, setRowsPerPage] = useState(DEFAULT_PAGE_LIMITS[1]); - // Last event tracking - const [numNewEvents, setNumNewEvents] = useState(0); - const [lastRefreshTime, setLastRefresh] = useState( - new Date().toISOString() - ); - - useEffect(() => { - // TODO: Figure out best type to filter by - isMounted && - isEventType(lastEvent, WsEventTypes.EVENT) && - setNumNewEvents(numNewEvents + 1); - }, [lastEvent]); - const refreshData = () => { - setNumNewEvents(0); - setLastRefresh(new Date().toString()); - }; + const [isHistLoading, setIsHistLoading] = useState(false); useEffect(() => { setIsMounted(true); - setNumNewEvents(0); return () => { setIsMounted(false); }; @@ -111,10 +95,10 @@ export const ActivityOperations: () => JSX.Element = () => { useEffect(() => { isMounted && - slideQuery && + slideID && fetchCatcher( `${FF_Paths.nsPrefix}/${selectedNamespace}${FF_Paths.operationsById( - slideQuery + slideID )}` ) .then((opRes: IOperation) => { @@ -123,11 +107,12 @@ export const ActivityOperations: () => JSX.Element = () => { .catch((err) => { reportFetchError(err); }); - }, [slideQuery, isMounted]); + }, [slideID, isMounted]); // Operations useEffect(() => { isMounted && + dateFilter && fetchCatcher( `${FF_Paths.nsPrefix}/${selectedNamespace}${ FF_Paths.operations @@ -143,8 +128,7 @@ export const ActivityOperations: () => JSX.Element = () => { }) .catch((err) => { reportFetchError(err); - }) - .finally(() => numNewEvents !== 0 && setNumNewEvents(0)); + }); }, [ rowsPerPage, currentPage, @@ -157,9 +141,11 @@ export const ActivityOperations: () => JSX.Element = () => { // Histogram useEffect(() => { + setIsHistLoading(true); const currentTime = dayjs().unix(); isMounted && + dateFilter && fetchCatcher( `${FF_Paths.nsPrefix}/${selectedNamespace}${FF_Paths.chartsHistogram( BucketCollectionEnum.Operations, @@ -173,7 +159,8 @@ export const ActivityOperations: () => JSX.Element = () => { }) .catch((err) => { reportFetchError(err); - }); + }) + .finally(() => setIsHistLoading(false)); }, [selectedNamespace, dateFilter, lastRefreshTime, isMounted]); const opsColumnHeaders = [ @@ -227,7 +214,7 @@ export const ActivityOperations: () => JSX.Element = () => { ], onClick: () => { setViewOp(op); - addSlideToParams(op.id); + setSlideSearchParam(op.id); }, leftBorderColor: FF_OP_CATEGORY_MAP[op.type]?.color, })); @@ -237,8 +224,8 @@ export const ActivityOperations: () => JSX.Element = () => {
0} + onRefresh={clearNewEvents} >
@@ -260,6 +247,7 @@ export const ActivityOperations: () => JSX.Element = () => { keys={makeKeyArray(FF_OP_CATEGORY_MAP)} includeLegend={true} emptyText={t('noOperations')} + isLoading={isHistLoading} isEmpty={isHistogramEmpty(opHistData ?? [])} /> @@ -298,7 +286,7 @@ export const ActivityOperations: () => JSX.Element = () => { open={!!viewOp} onClose={() => { setViewOp(undefined); - addSlideToParams(undefined); + setSlideSearchParam(null); }} /> )} diff --git a/src/pages/Activity/views/TransactionDetails.tsx b/src/pages/Activity/views/TransactionDetails.tsx index 631f78d2..e506eee6 100644 --- a/src/pages/Activity/views/TransactionDetails.tsx +++ b/src/pages/Activity/views/TransactionDetails.tsx @@ -48,7 +48,7 @@ import { fetchCatcher, getShortHash } from '../../../utils'; export const TransactionDetails: () => JSX.Element = () => { const { selectedNamespace } = useContext(ApplicationContext); - const { slideQuery, addSlideToParams } = useContext(SlideContext); + const { slideID, setSlideSearchParam } = useContext(SlideContext); const { reportFetchError } = useContext(SnackbarContext); const { t } = useTranslation(); const navigate = useNavigate(); @@ -75,19 +75,19 @@ export const TransactionDetails: () => JSX.Element = () => { }, []); useEffect(() => { - if (isMounted && slideQuery) { + if (isMounted && slideID) { fetchCatcher( - `${FF_Paths.nsPrefix}/${selectedNamespace}${FF_Paths.events}?id=${slideQuery}` + `${FF_Paths.nsPrefix}/${selectedNamespace}${FF_Paths.events}?id=${slideID}` ).then((eventRes: IEvent[]) => { isMounted && eventRes.length > 0 && setViewEvent(eventRes[0]); }); fetchCatcher( - `${FF_Paths.nsPrefix}/${selectedNamespace}${FF_Paths.operations}?id=${slideQuery}` + `${FF_Paths.nsPrefix}/${selectedNamespace}${FF_Paths.operations}?id=${slideID}` ).then((opRes: IOperation[]) => { isMounted && opRes.length > 0 && setViewOp(opRes[0]); }); } - }, [slideQuery, isMounted]); + }, [slideID, isMounted]); // Transaction details useEffect(() => { @@ -176,7 +176,7 @@ export const TransactionDetails: () => JSX.Element = () => { { setViewOp(op); - addSlideToParams(op.id); + setSlideSearchParam(op.id); }} {...{ op }} /> @@ -209,7 +209,7 @@ export const TransactionDetails: () => JSX.Element = () => { { setViewEvent(event); - addSlideToParams(event.id); + setSlideSearchParam(event.id); }} {...{ event }} /> @@ -315,7 +315,7 @@ export const TransactionDetails: () => JSX.Element = () => { open={!!viewEvent} onClose={() => { setViewEvent(undefined); - addSlideToParams(undefined); + setSlideSearchParam(null); }} /> )} @@ -325,7 +325,7 @@ export const TransactionDetails: () => JSX.Element = () => { open={!!viewOp} onClose={() => { setViewOp(undefined); - addSlideToParams(undefined); + setSlideSearchParam(null); }} /> )} diff --git a/src/pages/Activity/views/Transactions.tsx b/src/pages/Activity/views/Transactions.tsx index 45286e27..18788901 100644 --- a/src/pages/Activity/views/Transactions.tsx +++ b/src/pages/Activity/views/Transactions.tsx @@ -57,14 +57,15 @@ import { makeKeyArray, } from '../../../utils/charts'; import { makeTxHistogram } from '../../../utils/histograms/transactionHistogram'; -import { isEventType, WsEventTypes } from '../../../utils/wsEvents'; +import { hasTxEvent } from '../../../utils/wsEvents'; export const ActivityTransactions: () => JSX.Element = () => { - const { lastEvent, selectedNamespace } = useContext(ApplicationContext); + const { newEvents, lastRefreshTime, clearNewEvents, selectedNamespace } = + useContext(ApplicationContext); const { dateFilter } = useContext(DateFilterContext); const { filterAnchor, setFilterAnchor, filterString } = useContext(FilterContext); - const { slideQuery, addSlideToParams } = useContext(SlideContext); + const { slideID, setSlideSearchParam } = useContext(SlideContext); const { reportFetchError } = useContext(SnackbarContext); const { t } = useTranslation(); const [isMounted, setIsMounted] = useState(false); @@ -79,26 +80,11 @@ export const ActivityTransactions: () => JSX.Element = () => { const [currentPage, setCurrentPage] = useState(0); const [rowsPerPage, setRowsPerPage] = useState(DEFAULT_PAGE_LIMITS[1]); - // Last event tracking - const [numNewEvents, setNumNewEvents] = useState(0); - const [lastRefreshTime, setLastRefresh] = useState( - new Date().toISOString() - ); - - useEffect(() => { - isMounted && - isEventType(lastEvent, WsEventTypes.TRANSACTION) && - setNumNewEvents(numNewEvents + 1); - }, [lastEvent]); - const refreshData = () => { - setNumNewEvents(0); - setLastRefresh(new Date().toString()); - }; + const [isHistLoading, setIsHistLoading] = useState(false); useEffect(() => { setIsMounted(true); - setNumNewEvents(0); return () => { setIsMounted(false); }; @@ -106,10 +92,10 @@ export const ActivityTransactions: () => JSX.Element = () => { useEffect(() => { isMounted && - slideQuery && + slideID && fetchCatcher( `${FF_Paths.nsPrefix}/${selectedNamespace}${FF_Paths.transactionById( - slideQuery + slideID )}` ) .then((txRes: ITransaction) => { @@ -118,11 +104,12 @@ export const ActivityTransactions: () => JSX.Element = () => { .catch((err) => { reportFetchError(err); }); - }, [slideQuery, isMounted]); + }, [slideID, isMounted]); // Transactions useEffect(() => { isMounted && + dateFilter && fetchCatcher( `${FF_Paths.nsPrefix}/${selectedNamespace}${ FF_Paths.transactions @@ -138,8 +125,7 @@ export const ActivityTransactions: () => JSX.Element = () => { }) .catch((err) => { reportFetchError(err); - }) - .finally(() => numNewEvents !== 0 && setNumNewEvents(0)); + }); }, [ rowsPerPage, currentPage, @@ -152,22 +138,26 @@ export const ActivityTransactions: () => JSX.Element = () => { // Histogram; useEffect(() => { + setIsHistLoading(true); const currentTime = dayjs().unix(); - fetchCatcher( - `${FF_Paths.nsPrefix}/${selectedNamespace}${FF_Paths.chartsHistogram( - BucketCollectionEnum.Transactions, - dateFilter.filterTime, - currentTime, - BucketCountEnum.Large - )}` - ) - .then((histTypes: IMetric[]) => { - isMounted && setTxHistData(makeTxHistogram(histTypes)); - }) - .catch((err) => { - reportFetchError(err); - }); + isMounted && + dateFilter && + fetchCatcher( + `${FF_Paths.nsPrefix}/${selectedNamespace}${FF_Paths.chartsHistogram( + BucketCollectionEnum.Transactions, + dateFilter.filterTime, + currentTime, + BucketCountEnum.Large + )}` + ) + .then((histTypes: IMetric[]) => { + isMounted && setTxHistData(makeTxHistogram(histTypes)); + }) + .catch((err) => { + reportFetchError(err); + }) + .finally(() => setIsHistLoading(false)); }, [selectedNamespace, dateFilter, lastRefreshTime, isMounted]); const txColumnHeaders = [ @@ -217,7 +207,7 @@ export const ActivityTransactions: () => JSX.Element = () => { ], onClick: () => { setViewTx(tx); - addSlideToParams(tx.id); + setSlideSearchParam(tx.id); }, leftBorderColor: FF_TX_CATEGORY_MAP[tx.type]?.color, })); @@ -227,8 +217,8 @@ export const ActivityTransactions: () => JSX.Element = () => {
@@ -250,6 +240,7 @@ export const ActivityTransactions: () => JSX.Element = () => { keys={makeKeyArray(FF_TX_CATEGORY_MAP)} includeLegend={true} emptyText={t('noTransactions')} + isLoading={isHistLoading} isEmpty={isHistogramEmpty(txHistData ?? [])} /> @@ -288,7 +279,7 @@ export const ActivityTransactions: () => JSX.Element = () => { open={!!viewTx} onClose={() => { setViewTx(undefined); - addSlideToParams(undefined); + setSlideSearchParam(null); }} /> )} diff --git a/src/pages/Blockchain/views/Apis.tsx b/src/pages/Blockchain/views/Apis.tsx index b96af365..c2ac0d74 100644 --- a/src/pages/Blockchain/views/Apis.tsx +++ b/src/pages/Blockchain/views/Apis.tsx @@ -31,7 +31,6 @@ import { FilterContext } from '../../../contexts/FilterContext'; import { SnackbarContext } from '../../../contexts/SnackbarContext'; import { ApiFilters, - FF_EVENTS, FF_Paths, IDataTableRecord, IFireflyApi, @@ -39,10 +38,11 @@ import { } from '../../../interfaces'; import { DEFAULT_PADDING, DEFAULT_PAGE_LIMITS } from '../../../theme'; import { fetchCatcher } from '../../../utils'; -import { isEventType } from '../../../utils/wsEvents'; +import { hasApiEvent } from '../../../utils/wsEvents'; export const BlockchainApis: () => JSX.Element = () => { - const { lastEvent, selectedNamespace } = useContext(ApplicationContext); + const { newEvents, lastRefreshTime, clearNewEvents, selectedNamespace } = + useContext(ApplicationContext); const { dateFilter } = useContext(DateFilterContext); const { filterAnchor, setFilterAnchor, filterString } = useContext(FilterContext); @@ -56,26 +56,9 @@ export const BlockchainApis: () => JSX.Element = () => { const [currentPage, setCurrentPage] = useState(0); const [rowsPerPage, setRowsPerPage] = useState(DEFAULT_PAGE_LIMITS[1]); - // Last event tracking - const [numNewEvents, setNumNewEvents] = useState(0); - const [lastRefreshTime, setLastRefresh] = useState( - new Date().toISOString() - ); - - useEffect(() => { - isMounted && - isEventType(lastEvent, FF_EVENTS.CONTRACT_API_CONFIRMED) && - setNumNewEvents(numNewEvents + 1); - }, [lastEvent]); - - const refreshData = () => { - setNumNewEvents(0); - setLastRefresh(new Date().toString()); - }; useEffect(() => { setIsMounted(true); - setNumNewEvents(0); return () => { setIsMounted(false); }; @@ -84,6 +67,7 @@ export const BlockchainApis: () => JSX.Element = () => { // APIs useEffect(() => { isMounted && + dateFilter && fetchCatcher( `${FF_Paths.nsPrefix}/${selectedNamespace}${ FF_Paths.apis @@ -99,8 +83,7 @@ export const BlockchainApis: () => JSX.Element = () => { }) .catch((err) => { reportFetchError(err); - }) - .finally(() => numNewEvents !== 0 && setNumNewEvents(0)); + }); }, [ rowsPerPage, currentPage, @@ -171,8 +154,8 @@ export const BlockchainApis: () => JSX.Element = () => {
diff --git a/src/pages/Blockchain/views/Dashboard.tsx b/src/pages/Blockchain/views/Dashboard.tsx index 5c7c4ced..93f0af2c 100644 --- a/src/pages/Blockchain/views/Dashboard.tsx +++ b/src/pages/Blockchain/views/Dashboard.tsx @@ -64,11 +64,12 @@ import { makeKeyArray, } from '../../../utils/charts'; import { makeBlockchainEventHistogram } from '../../../utils/histograms/blockchainEventHistogram'; -import { isEventType, WsEventTypes } from '../../../utils/wsEvents'; +import { hasBlockchainEvent } from '../../../utils/wsEvents'; export const BlockchainDashboard: () => JSX.Element = () => { const { t } = useTranslation(); - const { lastEvent, selectedNamespace } = useContext(ApplicationContext); + const { newEvents, lastRefreshTime, clearNewEvents, selectedNamespace } = + useContext(ApplicationContext); const { dateFilter } = useContext(DateFilterContext); const { reportFetchError } = useContext(SnackbarContext); const navigate = useNavigate(); @@ -102,26 +103,11 @@ export const BlockchainDashboard: () => JSX.Element = () => { const [currentPage, setCurrentPage] = useState(0); const [rowsPerPage, setRowsPerPage] = useState(DEFAULT_PAGE_LIMITS[0]); - // Last event tracking - const [numNewEvents, setNumNewEvents] = useState(0); - const [lastRefreshTime, setLastRefresh] = useState( - new Date().toISOString() - ); - - useEffect(() => { - isMounted && - isEventType(lastEvent, WsEventTypes.BLOCKCHAIN_EVENT) && - setNumNewEvents(numNewEvents + 1); - }, [lastEvent]); - const refreshData = () => { - setNumNewEvents(0); - setLastRefresh(new Date().toString()); - }; + const [isHistLoading, setIsHistLoading] = useState(false); useEffect(() => { setIsMounted(true); - setNumNewEvents(0); return () => { setIsMounted(false); }; @@ -153,10 +139,11 @@ export const BlockchainDashboard: () => JSX.Element = () => { // Small Card UseEffect useEffect(() => { - const qParams = `?count=true&limit=1${dateFilter.filterString}`; + const qParams = `?count=true&limit=1${dateFilter?.filterString ?? ''}`; const qParamsNoRange = `?count=true&limit=1`; isMounted && + dateFilter && Promise.all([ // Blockchain Operations fetchCatcher( @@ -205,8 +192,7 @@ export const BlockchainDashboard: () => JSX.Element = () => { ) .catch((err) => { reportFetchError(err); - }) - .finally(() => numNewEvents !== 0 && setNumNewEvents(0)); + }); }, [selectedNamespace, lastRefreshTime, dateFilter, isMounted]); const ciColHeaders = [t('name'), t('version'), t('interfaceID')]; @@ -263,6 +249,7 @@ export const BlockchainDashboard: () => JSX.Element = () => { keys={makeKeyArray(FF_BE_CATEGORY_MAP)} includeLegend={true} emptyText={t('noBlockchainEvents')} + isLoading={isHistLoading} isEmpty={isHistogramEmpty(beHistData ?? [])} /> ), @@ -327,9 +314,11 @@ export const BlockchainDashboard: () => JSX.Element = () => { // Histogram useEffect(() => { + setIsHistLoading(true); const currentTime = dayjs().unix(); isMounted && + dateFilter && fetchCatcher( `${FF_Paths.nsPrefix}/${selectedNamespace}${FF_Paths.chartsHistogram( BucketCollectionEnum.BlockchainEvents, @@ -343,7 +332,8 @@ export const BlockchainDashboard: () => JSX.Element = () => { }) .catch((err) => { reportFetchError(err); - }); + }) + .finally(() => setIsHistLoading(false)); }, [selectedNamespace, lastRefreshTime, dateFilter, isMounted]); const beColHeaders = [ @@ -382,6 +372,7 @@ export const BlockchainDashboard: () => JSX.Element = () => { // Recent blockchain events useEffect(() => { isMounted && + dateFilter && fetchCatcher( `${FF_Paths.nsPrefix}/${selectedNamespace}${ FF_Paths.blockchainEvents @@ -412,8 +403,8 @@ export const BlockchainDashboard: () => JSX.Element = () => {
diff --git a/src/pages/Blockchain/views/Events.tsx b/src/pages/Blockchain/views/Events.tsx index 8b7222d2..b36d955b 100644 --- a/src/pages/Blockchain/views/Events.tsx +++ b/src/pages/Blockchain/views/Events.tsx @@ -55,10 +55,11 @@ import { makeKeyArray, } from '../../../utils/charts'; import { makeBlockchainEventHistogram } from '../../../utils/histograms/blockchainEventHistogram'; -import { isEventType, WsEventTypes } from '../../../utils/wsEvents'; +import { hasBlockchainEvent } from '../../../utils/wsEvents'; export const BlockchainEvents: () => JSX.Element = () => { - const { lastEvent, selectedNamespace } = useContext(ApplicationContext); + const { newEvents, lastRefreshTime, clearNewEvents, selectedNamespace } = + useContext(ApplicationContext); const { dateFilter } = useContext(DateFilterContext); const { filterAnchor, setFilterAnchor, filterString } = useContext(FilterContext); @@ -74,26 +75,11 @@ export const BlockchainEvents: () => JSX.Element = () => { const [beHistData, setBeHistData] = useState(); const [currentPage, setCurrentPage] = useState(0); const [rowsPerPage, setRowsPerPage] = useState(DEFAULT_PAGE_LIMITS[1]); - // Last event tracking - const [numNewEvents, setNumNewEvents] = useState(0); - const [lastRefreshTime, setLastRefresh] = useState( - new Date().toISOString() - ); - - useEffect(() => { - isMounted && - isEventType(lastEvent, WsEventTypes.BLOCKCHAIN_EVENT) && - setNumNewEvents(numNewEvents + 1); - }, [lastEvent]); - const refreshData = () => { - setNumNewEvents(0); - setLastRefresh(new Date().toString()); - }; + const [isHistLoading, setIsHistLoading] = useState(false); useEffect(() => { setIsMounted(true); - setNumNewEvents(0); return () => { setIsMounted(false); }; @@ -102,6 +88,7 @@ export const BlockchainEvents: () => JSX.Element = () => { // Blockchain events useEffect(() => { isMounted && + dateFilter && fetchCatcher( `${FF_Paths.nsPrefix}/${selectedNamespace}${ FF_Paths.blockchainEvents @@ -117,8 +104,7 @@ export const BlockchainEvents: () => JSX.Element = () => { }) .catch((err) => { reportFetchError(err); - }) - .finally(() => numNewEvents !== 0 && setNumNewEvents(0)); + }); }, [ rowsPerPage, currentPage, @@ -131,9 +117,11 @@ export const BlockchainEvents: () => JSX.Element = () => { // Histogram useEffect(() => { + setIsHistLoading(true); const currentTime = dayjs().unix(); isMounted && + dateFilter && fetchCatcher( `${FF_Paths.nsPrefix}/${selectedNamespace}${FF_Paths.chartsHistogram( BucketCollectionEnum.BlockchainEvents, @@ -147,7 +135,8 @@ export const BlockchainEvents: () => JSX.Element = () => { }) .catch((err) => { reportFetchError(err); - }); + }) + .finally(() => setIsHistLoading(false)); }, [selectedNamespace, dateFilter, lastRefreshTime, isMounted]); const beColHeaders = [ @@ -188,8 +177,8 @@ export const BlockchainEvents: () => JSX.Element = () => {
@@ -210,6 +199,7 @@ export const BlockchainEvents: () => JSX.Element = () => { indexBy="timestamp" keys={makeKeyArray(FF_BE_CATEGORY_MAP)} includeLegend={true} + isLoading={isHistLoading} emptyText={t('noBlockchainEvents')} isEmpty={isHistogramEmpty(beHistData ?? [])} /> diff --git a/src/pages/Blockchain/views/Interfaces.tsx b/src/pages/Blockchain/views/Interfaces.tsx index 7d4d516a..6bfb47bf 100644 --- a/src/pages/Blockchain/views/Interfaces.tsx +++ b/src/pages/Blockchain/views/Interfaces.tsx @@ -28,7 +28,6 @@ import { DateFilterContext } from '../../../contexts/DateFilterContext'; import { SlideContext } from '../../../contexts/SlideContext'; import { SnackbarContext } from '../../../contexts/SnackbarContext'; import { - FF_EVENTS, FF_Paths, IContractInterface, IDataTableRecord, @@ -36,12 +35,13 @@ import { } from '../../../interfaces'; import { DEFAULT_PADDING, DEFAULT_PAGE_LIMITS } from '../../../theme'; import { fetchCatcher } from '../../../utils'; -import { isEventType } from '../../../utils/wsEvents'; +import { hasInterfaceEvent } from '../../../utils/wsEvents'; export const BlockchainInterfaces: () => JSX.Element = () => { - const { lastEvent, selectedNamespace } = useContext(ApplicationContext); + const { newEvents, lastRefreshTime, clearNewEvents, selectedNamespace } = + useContext(ApplicationContext); const { dateFilter } = useContext(DateFilterContext); - const { slideQuery, addSlideToParams } = useContext(SlideContext); + const { slideID, setSlideSearchParam } = useContext(SlideContext); const { reportFetchError } = useContext(SnackbarContext); const { t } = useTranslation(); const [isMounted, setIsMounted] = useState(false); @@ -55,26 +55,9 @@ export const BlockchainInterfaces: () => JSX.Element = () => { >(); const [currentPage, setCurrentPage] = useState(0); const [rowsPerPage, setRowsPerPage] = useState(DEFAULT_PAGE_LIMITS[1]); - // Last event tracking - const [numNewEvents, setNumNewEvents] = useState(0); - const [lastRefreshTime, setLastRefresh] = useState( - new Date().toISOString() - ); - - useEffect(() => { - isMounted && - isEventType(lastEvent, FF_EVENTS.CONTRACT_INTERFACE_CONFIRMED) && - setNumNewEvents(numNewEvents + 1); - }, [lastEvent]); - - const refreshData = () => { - setNumNewEvents(0); - setLastRefresh(new Date().toString()); - }; useEffect(() => { setIsMounted(true); - setNumNewEvents(0); return () => { setIsMounted(false); }; @@ -82,11 +65,11 @@ export const BlockchainInterfaces: () => JSX.Element = () => { useEffect(() => { isMounted && - slideQuery && + slideID && fetchCatcher( `${ FF_Paths.nsPrefix - }/${selectedNamespace}${FF_Paths.contractInterfacesById(slideQuery)}` + }/${selectedNamespace}${FF_Paths.contractInterfacesById(slideID)}` ) .then((contractRes: IContractInterface) => { setViewInterface(contractRes); @@ -94,11 +77,12 @@ export const BlockchainInterfaces: () => JSX.Element = () => { .catch((err) => { reportFetchError(err); }); - }, [slideQuery, isMounted]); + }, [slideID, isMounted]); // Interfaces useEffect(() => { isMounted && + dateFilter && fetchCatcher( `${FF_Paths.nsPrefix}/${selectedNamespace}${ FF_Paths.contractInterfaces @@ -114,8 +98,7 @@ export const BlockchainInterfaces: () => JSX.Element = () => { }) .catch((err) => { reportFetchError(err); - }) - .finally(() => numNewEvents !== 0 && setNumNewEvents(0)); + }); }, [ rowsPerPage, currentPage, @@ -165,7 +148,7 @@ export const BlockchainInterfaces: () => JSX.Element = () => { ], onClick: () => { setViewInterface(int); - addSlideToParams(int.id); + setSlideSearchParam(int.id); }, }) ); @@ -175,8 +158,8 @@ export const BlockchainInterfaces: () => JSX.Element = () => {
@@ -214,7 +197,7 @@ export const BlockchainInterfaces: () => JSX.Element = () => { open={!!viewInterface} onClose={() => { setViewInterface(undefined); - addSlideToParams(undefined); + setSlideSearchParam(null); }} /> )} diff --git a/src/pages/Blockchain/views/Listeners.tsx b/src/pages/Blockchain/views/Listeners.tsx index ca7110f6..e46acee4 100644 --- a/src/pages/Blockchain/views/Listeners.tsx +++ b/src/pages/Blockchain/views/Listeners.tsx @@ -41,11 +41,11 @@ import { DEFAULT_PADDING, DEFAULT_PAGE_LIMITS } from '../../../theme'; import { fetchCatcher, getFFTime } from '../../../utils'; export const BlockchainListeners: () => JSX.Element = () => { - const { lastEvent, selectedNamespace } = useContext(ApplicationContext); + const { selectedNamespace } = useContext(ApplicationContext); const { dateFilter } = useContext(DateFilterContext); const { filterAnchor, setFilterAnchor, filterString } = useContext(FilterContext); - const { slideQuery, addSlideToParams } = useContext(SlideContext); + const { slideID, setSlideSearchParam } = useContext(SlideContext); const { reportFetchError } = useContext(SnackbarContext); const { t } = useTranslation(); const [isMounted, setIsMounted] = useState(false); @@ -69,11 +69,11 @@ export const BlockchainListeners: () => JSX.Element = () => { useEffect(() => { isMounted && - slideQuery && + slideID && fetchCatcher( `${ FF_Paths.nsPrefix - }/${selectedNamespace}${FF_Paths.contractListenersByNameId(slideQuery)}` + }/${selectedNamespace}${FF_Paths.contractListenersByNameId(slideID)}` ) .then((listenerRes: IContractListener) => { setViewListener(listenerRes); @@ -81,11 +81,12 @@ export const BlockchainListeners: () => JSX.Element = () => { .catch((err) => { reportFetchError(err); }); - }, [slideQuery, isMounted]); + }, [slideID, isMounted]); // Listeners useEffect(() => { isMounted && + dateFilter && fetchCatcher( `${FF_Paths.nsPrefix}/${selectedNamespace}${ FF_Paths.contractListeners @@ -107,9 +108,7 @@ export const BlockchainListeners: () => JSX.Element = () => { currentPage, selectedNamespace, dateFilter, - lastEvent, filterString, - reportFetchError, isMounted, ]); @@ -159,7 +158,7 @@ export const BlockchainListeners: () => JSX.Element = () => { ], onClick: () => { setViewListener(l); - addSlideToParams(l.id); + setSlideSearchParam(l.id); }, }) ); @@ -214,7 +213,7 @@ export const BlockchainListeners: () => JSX.Element = () => { open={!!viewListener} onClose={() => { setViewListener(undefined); - addSlideToParams(undefined); + setSlideSearchParam(null); }} /> )} diff --git a/src/pages/Home/views/Dashboard.tsx b/src/pages/Home/views/Dashboard.tsx index 55c69ad1..5d5d7757 100644 --- a/src/pages/Home/views/Dashboard.tsx +++ b/src/pages/Home/views/Dashboard.tsx @@ -48,14 +48,21 @@ import { makeColorArray, makeKeyArray, } from '../../../utils/charts'; -import { isEventType, WsEventTypes } from '../../../utils/wsEvents'; export const HomeDashboard: () => JSX.Element = () => { const { t } = useTranslation(); - const { lastEvent, nodeID, nodeName, orgID, orgName, selectedNamespace } = - useContext(ApplicationContext); + const { + newEvents, + lastRefreshTime, + clearNewEvents, + nodeID, + nodeName, + orgID, + orgName, + selectedNamespace, + } = useContext(ApplicationContext); const { dateFilter } = useContext(DateFilterContext); - const { slideQuery, addSlideToParams } = useContext(SlideContext); + const { slideID, setSlideSearchParam } = useContext(SlideContext); const { reportFetchError } = useContext(SnackbarContext); const navigate = useNavigate(); const [isMounted, setIsMounted] = useState(false); @@ -88,45 +95,29 @@ export const HomeDashboard: () => JSX.Element = () => { // Table cards const [recentEventTxs, setRecentEventTxs] = useState(); const [recentEvents, setRecentEvents] = useState(); - // Last event tracking - const [numNewEvents, setNumNewEvents] = useState(0); - const [lastRefreshTime, setLastRefresh] = useState( - new Date().toISOString() - ); - - useEffect(() => { - isMounted && - isEventType(lastEvent, WsEventTypes.EVENT) && - setNumNewEvents(numNewEvents + 1); - }, [lastEvent]); - - const refreshData = () => { - setNumNewEvents(0); - setLastRefresh(new Date().toString()); - }; + const [isHistLoading, setIsHistLoading] = useState(false); useEffect(() => { setIsMounted(true); - setNumNewEvents(0); return () => { setIsMounted(false); }; }, []); useEffect(() => { - if (isMounted && slideQuery) { + if (isMounted && slideID) { fetchCatcher( - `${FF_Paths.nsPrefix}/${selectedNamespace}${FF_Paths.events}?id=${slideQuery}` + `${FF_Paths.nsPrefix}/${selectedNamespace}${FF_Paths.events}?id=${slideID}` ).then((eventRes: IEvent[]) => { isMounted && eventRes.length > 0 && setViewEvent(eventRes[0]); }); fetchCatcher( - `${FF_Paths.nsPrefix}/${selectedNamespace}${FF_Paths.transactions}?id=${slideQuery}` + `${FF_Paths.nsPrefix}/${selectedNamespace}${FF_Paths.transactions}?id=${slideID}` ).then((txRes: ITransaction[]) => { isMounted && txRes.length > 0 && setViewTx(txRes[0]); }); } - }, [slideQuery, isMounted]); + }, [slideID, isMounted]); const smallCards: ISmallCard[] = [ { @@ -169,9 +160,10 @@ export const HomeDashboard: () => JSX.Element = () => { // Small Card UseEffect useEffect(() => { - const qParams = `?count=true&limit=1${dateFilter.filterString}`; + const qParams = `?count=true&limit=1${dateFilter?.filterString ?? ''}`; isMounted && + dateFilter && Promise.all([ // Blockchain fetchCatcher( @@ -264,8 +256,7 @@ export const HomeDashboard: () => JSX.Element = () => { ) .catch((err) => { reportFetchError(err); - }) - .finally(() => numNewEvents !== 0 && setNumNewEvents(0)); + }); }, [selectedNamespace, dateFilter, lastRefreshTime, isMounted]); const myNodeDetailsList: IDataWithHeader[] = [ @@ -315,6 +306,7 @@ export const HomeDashboard: () => JSX.Element = () => { indexBy="timestamp" keys={makeKeyArray(FF_EVENTS_CATEGORY_MAP)} emptyText={t('noActivity')} + isLoading={isHistLoading} isEmpty={isHistogramEmpty(eventHistData ?? [])} includeLegend={true} /> @@ -359,9 +351,10 @@ export const HomeDashboard: () => JSX.Element = () => { // Medium Card UseEffect useEffect(() => { + setIsHistLoading(true); const currentTime = dayjs().unix(); - if (isMounted) { + if (isMounted && dateFilter) { fetchCatcher( `${FF_Paths.nsPrefix}/${selectedNamespace}${FF_Paths.chartsHistogram( BucketCollectionEnum.Events, @@ -376,7 +369,8 @@ export const HomeDashboard: () => JSX.Element = () => { .catch((err) => { setEventHistData([]); reportFetchError(err); - }); + }) + .finally(() => setIsHistLoading(false)); fetchCatcher(`${FF_Paths.apiPrefix}/${FF_Paths.networkNodeById(nodeID)}`) .then((nodeRes: INode) => { setMyNode(nodeRes); @@ -442,11 +436,11 @@ export const HomeDashboard: () => JSX.Element = () => { { setViewEvent(event); - addSlideToParams(event.id); + setSlideSearchParam(event.id); }} onHandleViewTx={(tx: ITransaction) => { setViewTx(tx); - addSlideToParams(tx.id); + setSlideSearchParam(tx.id); }} link={FF_NAV_PATHS.activityTxDetailPath( selectedNamespace, @@ -499,17 +493,16 @@ export const HomeDashboard: () => JSX.Element = () => { { setViewEvent(event); - addSlideToParams(event.id); + setSlideSearchParam(event.id); }} onHandleViewTx={(tx: ITransaction) => { setViewTx(tx); - addSlideToParams(tx.id); + setSlideSearchParam(tx.id); }} link={FF_NAV_PATHS.activityTxDetailPath( selectedNamespace, event.tx )} - linkState={{ state: event }} {...{ event }} /> @@ -523,8 +516,9 @@ export const HomeDashboard: () => JSX.Element = () => { ]; // Table Card UseEffect useEffect(() => { - const qParams = `?limit=25${dateFilter.filterString}`; + const qParams = `?limit=25${dateFilter?.filterString ?? ''}`; isMounted && + dateFilter && Promise.all([ fetchCatcher( `${FF_Paths.nsPrefix}/${selectedNamespace}${FF_Paths.events}${qParams}&type=transaction_submitted&fetchreferences=true` @@ -549,8 +543,8 @@ export const HomeDashboard: () => JSX.Element = () => {
0} + onRefresh={clearNewEvents} >
@@ -635,7 +629,7 @@ export const HomeDashboard: () => JSX.Element = () => { open={!!viewTx} onClose={() => { setViewTx(undefined); - addSlideToParams(undefined); + setSlideSearchParam(null); }} /> )} @@ -645,7 +639,7 @@ export const HomeDashboard: () => JSX.Element = () => { open={!!viewEvent} onClose={() => { setViewEvent(undefined); - addSlideToParams(undefined); + setSlideSearchParam(null); }} /> )} diff --git a/src/pages/Off-Chain/views/Dashboard.tsx b/src/pages/Off-Chain/views/Dashboard.tsx index 5a7f39ab..3fd39f6a 100644 --- a/src/pages/Off-Chain/views/Dashboard.tsx +++ b/src/pages/Off-Chain/views/Dashboard.tsx @@ -40,7 +40,6 @@ import { BucketCountEnum, DATATYPES_PATH, DATA_PATH, - FF_EVENTS, FF_MESSAGES_CATEGORY_MAP, FF_NAV_PATHS, FF_Paths, @@ -73,13 +72,14 @@ import { makeColorArray, makeKeyArray, } from '../../../utils/charts'; -import { isEventType, WsEventTypes } from '../../../utils/wsEvents'; +import { hasOffchainEvent } from '../../../utils/wsEvents'; export const OffChainDashboard: () => JSX.Element = () => { const { t } = useTranslation(); - const { lastEvent, selectedNamespace } = useContext(ApplicationContext); + const { newEvents, lastRefreshTime, clearNewEvents, selectedNamespace } = + useContext(ApplicationContext); const { dateFilter } = useContext(DateFilterContext); - const { slideQuery, addSlideToParams } = useContext(SlideContext); + const { slideID, setSlideSearchParam } = useContext(SlideContext); const { reportFetchError } = useContext(SnackbarContext); const navigate = useNavigate(); const [isMounted, setIsMounted] = useState(false); @@ -107,27 +107,11 @@ export const OffChainDashboard: () => JSX.Element = () => { const [viewMsg, setViewMsg] = useState(); const [currentPage, setCurrentPage] = useState(0); const [rowsPerPage, setRowsPerPage] = useState(DEFAULT_PAGE_LIMITS[0]); - // Last event tracking - const [numNewEvents, setNumNewEvents] = useState(0); - const [lastRefreshTime, setLastRefresh] = useState( - new Date().toISOString() - ); - - useEffect(() => { - isMounted && - (isEventType(lastEvent, WsEventTypes.MESSAGE) || - isEventType(lastEvent, FF_EVENTS.DATATYPE_CONFIRMED)) && - setNumNewEvents(numNewEvents + 1); - }, [lastEvent]); - const refreshData = () => { - setNumNewEvents(0); - setLastRefresh(new Date().toISOString()); - }; + const [isHistLoading, setIsHistLoading] = useState(false); useEffect(() => { setIsMounted(true); - setNumNewEvents(0); return () => { setIsMounted(false); }; @@ -135,10 +119,10 @@ export const OffChainDashboard: () => JSX.Element = () => { useEffect(() => { isMounted && - slideQuery && + slideID && fetchCatcher( `${FF_Paths.nsPrefix}/${selectedNamespace}${FF_Paths.messagesById( - slideQuery + slideID )}` ) .then((messageRes: IMessage) => { @@ -147,7 +131,7 @@ export const OffChainDashboard: () => JSX.Element = () => { .catch((err) => { reportFetchError(err); }); - }, [slideQuery, isMounted]); + }, [slideID, isMounted]); const smallCards: ISmallCard[] = [ { @@ -173,10 +157,11 @@ export const OffChainDashboard: () => JSX.Element = () => { // Small Card UseEffect useEffect(() => { - const qParams = `?count=true&limit=1${dateFilter.filterString}`; + const qParams = `?count=true&limit=1${dateFilter?.filterString ?? ''}`; const qParamsNoRange = `?count=true&limit=1`; isMounted && + dateFilter && Promise.all([ // Messages fetchCatcher( @@ -212,8 +197,7 @@ export const OffChainDashboard: () => JSX.Element = () => { ) .catch((err) => { reportFetchError(err); - }) - .finally(() => numNewEvents !== 0 && setNumNewEvents(0)); + }); }, [selectedNamespace, dateFilter, lastRefreshTime, isMounted]); const dataHeaders = [t('nameOrID'), t('created'), t('download')]; @@ -281,6 +265,7 @@ export const OffChainDashboard: () => JSX.Element = () => { isEmpty={isHistogramEmpty(messageHistData ?? [])} keys={makeKeyArray(FF_MESSAGES_CATEGORY_MAP)} includeLegend={true} + isLoading={isHistLoading} /> ), }, @@ -320,10 +305,11 @@ export const OffChainDashboard: () => JSX.Element = () => { // Medium Card UseEffect useEffect(() => { + setIsHistLoading(true); const currentTime = dayjs().unix(); - const qParams = `?limit=25${dateFilter.filterString}`; + const qParams = `?limit=25${dateFilter?.filterString ?? ''}`; const qParamsNoRange = `?limit=25`; - if (isMounted) { + if (isMounted && dateFilter) { fetchCatcher( `${FF_Paths.nsPrefix}/${selectedNamespace}${FF_Paths.chartsHistogram( BucketCollectionEnum.Messages, @@ -337,7 +323,8 @@ export const OffChainDashboard: () => JSX.Element = () => { }) .catch((err) => { reportFetchError(err); - }); + }) + .finally(() => setIsHistLoading(false)); // Data fetchCatcher( `${FF_Paths.nsPrefix}/${selectedNamespace}${FF_Paths.data}${qParams}` @@ -364,6 +351,7 @@ export const OffChainDashboard: () => JSX.Element = () => { // Messages useEffect(() => { isMounted && + dateFilter && fetchCatcher( `${FF_Paths.nsPrefix}/${selectedNamespace}${ FF_Paths.messages @@ -472,7 +460,7 @@ export const OffChainDashboard: () => JSX.Element = () => { ], onClick: () => { setViewMsg(msg); - addSlideToParams(msg.header.id); + setSlideSearchParam(msg.header.id); }, leftBorderColor: FF_MESSAGES_CATEGORY_MAP[msg.header.type]?.color, })); @@ -482,8 +470,8 @@ export const OffChainDashboard: () => JSX.Element = () => {
@@ -568,7 +556,7 @@ export const OffChainDashboard: () => JSX.Element = () => { open={!!viewMsg} onClose={() => { setViewMsg(undefined); - addSlideToParams(undefined); + setSlideSearchParam(null); }} /> )} diff --git a/src/pages/Off-Chain/views/Data.tsx b/src/pages/Off-Chain/views/Data.tsx index 681b8677..7ad5ea2a 100644 --- a/src/pages/Off-Chain/views/Data.tsx +++ b/src/pages/Off-Chain/views/Data.tsx @@ -33,7 +33,6 @@ import { SlideContext } from '../../../contexts/SlideContext'; import { SnackbarContext } from '../../../contexts/SnackbarContext'; import { DataFilters, - FF_EVENTS, FF_Paths, IData, IDataTableRecord, @@ -41,14 +40,15 @@ import { } from '../../../interfaces'; import { DEFAULT_PADDING, DEFAULT_PAGE_LIMITS } from '../../../theme'; import { downloadBlobFile, fetchCatcher, getFFTime } from '../../../utils'; -import { isEventType, WsEventTypes } from '../../../utils/wsEvents'; +import { hasDataEvent } from '../../../utils/wsEvents'; export const OffChainData: () => JSX.Element = () => { - const { lastEvent, selectedNamespace } = useContext(ApplicationContext); + const { newEvents, lastRefreshTime, clearNewEvents, selectedNamespace } = + useContext(ApplicationContext); const { dateFilter } = useContext(DateFilterContext); const { filterAnchor, setFilterAnchor, filterString } = useContext(FilterContext); - const { slideQuery, addSlideToParams } = useContext(SlideContext); + const { slideID, setSlideSearchParam } = useContext(SlideContext); const { reportFetchError } = useContext(SnackbarContext); const { t } = useTranslation(); const [isMounted, setIsMounted] = useState(false); @@ -60,27 +60,9 @@ export const OffChainData: () => JSX.Element = () => { const [viewData, setViewData] = useState(); const [currentPage, setCurrentPage] = useState(0); const [rowsPerPage, setRowsPerPage] = useState(DEFAULT_PAGE_LIMITS[1]); - // Last event tracking - const [numNewEvents, setNumNewEvents] = useState(0); - const [lastRefreshTime, setLastRefresh] = useState( - new Date().toISOString() - ); - - useEffect(() => { - isMounted && - (isEventType(lastEvent, WsEventTypes.MESSAGE) || - isEventType(lastEvent, FF_EVENTS.DATATYPE_CONFIRMED)) && - setNumNewEvents(numNewEvents + 1); - }, [lastEvent]); - - const refreshData = () => { - setNumNewEvents(0); - setLastRefresh(new Date().toString()); - }; useEffect(() => { setIsMounted(true); - setNumNewEvents(0); return () => { setIsMounted(false); }; @@ -88,11 +70,9 @@ export const OffChainData: () => JSX.Element = () => { useEffect(() => { isMounted && - slideQuery && + slideID && fetchCatcher( - `${FF_Paths.nsPrefix}/${selectedNamespace}${FF_Paths.dataById( - slideQuery - )}` + `${FF_Paths.nsPrefix}/${selectedNamespace}${FF_Paths.dataById(slideID)}` ) .then((dataRes: IData) => { setViewData(dataRes); @@ -100,11 +80,12 @@ export const OffChainData: () => JSX.Element = () => { .catch((err) => { reportFetchError(err); }); - }, [slideQuery, isMounted]); + }, [slideID, isMounted]); // Data useEffect(() => { isMounted && + dateFilter && fetchCatcher( `${FF_Paths.nsPrefix}/${selectedNamespace}${ FF_Paths.data @@ -120,8 +101,7 @@ export const OffChainData: () => JSX.Element = () => { }) .catch((err) => { reportFetchError(err); - }) - .finally(() => numNewEvents !== 0 && setNumNewEvents(0)); + }); }, [ rowsPerPage, currentPage, @@ -187,7 +167,7 @@ export const OffChainData: () => JSX.Element = () => { ], onClick: () => { setViewData(d); - addSlideToParams(d.id); + setSlideSearchParam(d.id); }, })); @@ -196,8 +176,8 @@ export const OffChainData: () => JSX.Element = () => {
@@ -246,7 +226,7 @@ export const OffChainData: () => JSX.Element = () => { open={!!viewData} onClose={() => { setViewData(undefined); - addSlideToParams(undefined); + setSlideSearchParam(null); }} /> )} diff --git a/src/pages/Off-Chain/views/DataTypes.tsx b/src/pages/Off-Chain/views/DataTypes.tsx index ccb08341..23641d58 100644 --- a/src/pages/Off-Chain/views/DataTypes.tsx +++ b/src/pages/Off-Chain/views/DataTypes.tsx @@ -32,7 +32,6 @@ import { SlideContext } from '../../../contexts/SlideContext'; import { SnackbarContext } from '../../../contexts/SnackbarContext'; import { DatatypesFilters, - FF_EVENTS, FF_Paths, IDataTableRecord, IDatatype, @@ -40,14 +39,15 @@ import { } from '../../../interfaces'; import { DEFAULT_PADDING, DEFAULT_PAGE_LIMITS } from '../../../theme'; import { fetchCatcher, getFFTime } from '../../../utils'; -import { isEventType } from '../../../utils/wsEvents'; +import { hasDatatypeEvent } from '../../../utils/wsEvents'; export const OffChainDataTypes: () => JSX.Element = () => { - const { lastEvent, selectedNamespace } = useContext(ApplicationContext); + const { newEvents, lastRefreshTime, clearNewEvents, selectedNamespace } = + useContext(ApplicationContext); const { dateFilter } = useContext(DateFilterContext); const { filterAnchor, setFilterAnchor, filterString } = useContext(FilterContext); - const { slideQuery, addSlideToParams } = useContext(SlideContext); + const { slideID, setSlideSearchParam } = useContext(SlideContext); const { reportFetchError } = useContext(SnackbarContext); const { t } = useTranslation(); const [isMounted, setIsMounted] = useState(false); @@ -58,26 +58,9 @@ export const OffChainDataTypes: () => JSX.Element = () => { const [viewDatatype, setViewDatatype] = useState(); const [currentPage, setCurrentPage] = useState(0); const [rowsPerPage, setRowsPerPage] = useState(DEFAULT_PAGE_LIMITS[1]); - // Last event tracking - const [numNewEvents, setNumNewEvents] = useState(0); - const [lastRefreshTime, setLastRefresh] = useState( - new Date().toISOString() - ); - - useEffect(() => { - isMounted && - isEventType(lastEvent, FF_EVENTS.DATATYPE_CONFIRMED) && - setNumNewEvents(numNewEvents + 1); - }, [lastEvent]); - - const refreshData = () => { - setNumNewEvents(0); - setLastRefresh(new Date().toString()); - }; useEffect(() => { setIsMounted(true); - setNumNewEvents(0); return () => { setIsMounted(false); }; @@ -85,9 +68,9 @@ export const OffChainDataTypes: () => JSX.Element = () => { useEffect(() => { isMounted && - slideQuery && + slideID && fetchCatcher( - `${FF_Paths.nsPrefix}/${selectedNamespace}${FF_Paths.datatypes}?id=${slideQuery}` + `${FF_Paths.nsPrefix}/${selectedNamespace}${FF_Paths.datatypes}?id=${slideID}` ) .then((dtRes: IDatatype[]) => { dtRes.length && setViewDatatype(dtRes[0]); @@ -95,11 +78,12 @@ export const OffChainDataTypes: () => JSX.Element = () => { .catch((err) => { reportFetchError(err); }); - }, [slideQuery, isMounted]); + }, [slideID, isMounted]); // Datatype useEffect(() => { isMounted && + dateFilter && fetchCatcher( `${FF_Paths.nsPrefix}/${selectedNamespace}${ FF_Paths.datatypes @@ -115,8 +99,7 @@ export const OffChainDataTypes: () => JSX.Element = () => { }) .catch((err) => { reportFetchError(err); - }) - .finally(() => numNewEvents !== 0 && setNumNewEvents(0)); + }); }, [ rowsPerPage, currentPage, @@ -167,7 +150,7 @@ export const OffChainDataTypes: () => JSX.Element = () => { ], onClick: () => { setViewDatatype(d); - addSlideToParams(d.id); + setSlideSearchParam(d.id); }, }) ); @@ -177,8 +160,8 @@ export const OffChainDataTypes: () => JSX.Element = () => {
@@ -227,7 +210,7 @@ export const OffChainDataTypes: () => JSX.Element = () => { open={!!viewDatatype} onClose={() => { setViewDatatype(undefined); - addSlideToParams(undefined); + setSlideSearchParam(null); }} /> )} diff --git a/src/pages/Off-Chain/views/Messages.tsx b/src/pages/Off-Chain/views/Messages.tsx index e2d07270..3d639d8c 100644 --- a/src/pages/Off-Chain/views/Messages.tsx +++ b/src/pages/Off-Chain/views/Messages.tsx @@ -59,14 +59,15 @@ import { makeColorArray, makeKeyArray, } from '../../../utils/charts'; -import { isEventType, WsEventTypes } from '../../../utils/wsEvents'; +import { hasDataEvent } from '../../../utils/wsEvents'; export const OffChainMessages: () => JSX.Element = () => { - const { lastEvent, selectedNamespace } = useContext(ApplicationContext); + const { newEvents, lastRefreshTime, clearNewEvents, selectedNamespace } = + useContext(ApplicationContext); const { dateFilter } = useContext(DateFilterContext); const { filterAnchor, setFilterAnchor, filterString } = useContext(FilterContext); - const { slideQuery, addSlideToParams } = useContext(SlideContext); + const { slideID, setSlideSearchParam } = useContext(SlideContext); const { reportFetchError } = useContext(SnackbarContext); const { t } = useTranslation(); const [isMounted, setIsMounted] = useState(false); @@ -81,26 +82,10 @@ export const OffChainMessages: () => JSX.Element = () => { const [currentPage, setCurrentPage] = useState(0); const [rowsPerPage, setRowsPerPage] = useState(DEFAULT_PAGE_LIMITS[1]); - // Last event tracking - const [numNewEvents, setNumNewEvents] = useState(0); - const [lastRefreshTime, setLastRefresh] = useState( - new Date().toISOString() - ); - - useEffect(() => { - isMounted && - isEventType(lastEvent, WsEventTypes.MESSAGE) && - setNumNewEvents(numNewEvents + 1); - }, [lastEvent]); - - const refreshData = () => { - setNumNewEvents(0); - setLastRefresh(new Date().toString()); - }; + const [isHistLoading, setIsHistLoading] = useState(false); useEffect(() => { setIsMounted(true); - setNumNewEvents(0); return () => { setIsMounted(false); }; @@ -108,10 +93,10 @@ export const OffChainMessages: () => JSX.Element = () => { useEffect(() => { isMounted && - slideQuery && + slideID && fetchCatcher( `${FF_Paths.nsPrefix}/${selectedNamespace}${FF_Paths.messagesById( - slideQuery + slideID )}` ) .then((messageRes: IMessage) => { @@ -120,11 +105,12 @@ export const OffChainMessages: () => JSX.Element = () => { .catch((err) => { reportFetchError(err); }); - }, [slideQuery, isMounted]); + }, [slideID, isMounted]); // Messages useEffect(() => { isMounted && + dateFilter && fetchCatcher( `${FF_Paths.nsPrefix}/${selectedNamespace}${ FF_Paths.messages @@ -140,8 +126,7 @@ export const OffChainMessages: () => JSX.Element = () => { }) .catch((err) => { reportFetchError(err); - }) - .finally(() => numNewEvents !== 0 && setNumNewEvents(0)); + }); }, [ rowsPerPage, currentPage, @@ -154,9 +139,11 @@ export const OffChainMessages: () => JSX.Element = () => { // Histogram useEffect(() => { + setIsHistLoading(true); const currentTime = dayjs().unix(); isMounted && + dateFilter && fetchCatcher( `${FF_Paths.nsPrefix}/${selectedNamespace}${FF_Paths.chartsHistogram( BucketCollectionEnum.Messages, @@ -170,7 +157,8 @@ export const OffChainMessages: () => JSX.Element = () => { }) .catch((err) => { reportFetchError(err); - }); + }) + .finally(() => setIsHistLoading(false)); }, [selectedNamespace, dateFilter, lastRefreshTime, isMounted]); const msgColumnHeaders = [ @@ -256,7 +244,7 @@ export const OffChainMessages: () => JSX.Element = () => { ], onClick: () => { setViewMsg(msg); - addSlideToParams(msg.header.id); + setSlideSearchParam(msg.header.id); }, leftBorderColor: FF_MESSAGES_CATEGORY_MAP[msg.header.type]?.color, })); @@ -266,8 +254,8 @@ export const OffChainMessages: () => JSX.Element = () => {
@@ -289,6 +277,7 @@ export const OffChainMessages: () => JSX.Element = () => { keys={makeKeyArray(FF_MESSAGES_CATEGORY_MAP)} includeLegend={true} emptyText={t('noMessages')} + isLoading={isHistLoading} isEmpty={isHistogramEmpty(messageHistData ?? [])} /> @@ -327,7 +316,7 @@ export const OffChainMessages: () => JSX.Element = () => { open={!!viewMsg} onClose={() => { setViewMsg(undefined); - addSlideToParams(undefined); + setSlideSearchParam(null); }} /> )} diff --git a/src/pages/Tokens/views/Dashboard.tsx b/src/pages/Tokens/views/Dashboard.tsx index 8f4989f0..18650fef 100644 --- a/src/pages/Tokens/views/Dashboard.tsx +++ b/src/pages/Tokens/views/Dashboard.tsx @@ -53,7 +53,6 @@ import { TRANSFERS_PATH, } from '../../../interfaces'; import { - FF_EVENTS, FF_TRANSFER_CATEGORY_MAP, PoolStateColorMap, TransferIconMap, @@ -70,13 +69,14 @@ import { makeKeyArray, } from '../../../utils/charts'; import { makeTransferHistogram } from '../../../utils/histograms/transferHistogram'; -import { isEventType } from '../../../utils/wsEvents'; +import { hasTransferEvent } from '../../../utils/wsEvents'; export const TokensDashboard: () => JSX.Element = () => { const { t } = useTranslation(); - const { lastEvent, selectedNamespace } = useContext(ApplicationContext); + const { newEvents, lastRefreshTime, clearNewEvents, selectedNamespace } = + useContext(ApplicationContext); const { dateFilter } = useContext(DateFilterContext); - const { slideQuery, addSlideToParams } = useContext(SlideContext); + const { slideID, setSlideSearchParam } = useContext(SlideContext); const { reportFetchError } = useContext(SnackbarContext); const navigate = useNavigate(); const [isMounted, setIsMounted] = useState(false); @@ -111,28 +111,11 @@ export const TokensDashboard: () => JSX.Element = () => { >(); const [currentPage, setCurrentPage] = useState(0); const [rowsPerPage, setRowsPerPage] = useState(DEFAULT_PAGE_LIMITS[0]); - // Last event tracking - const [numNewEvents, setNumNewEvents] = useState(0); - const [lastRefreshTime, setLastRefresh] = useState( - new Date().toISOString() - ); - - useEffect(() => { - isMounted && - (isEventType(lastEvent, FF_EVENTS.TOKEN_POOL_CONFIRMED) || - isEventType(lastEvent, FF_EVENTS.TOKEN_TRANSFER_CONFIRMED) || - isEventType(lastEvent, FF_EVENTS.TOKEN_TRANSFER_FAILED)) && - setNumNewEvents(numNewEvents + 1); - }, [lastEvent]); - const refreshData = () => { - setNumNewEvents(0); - setLastRefresh(new Date().toString()); - }; + const [isHistLoading, setIsHistLoading] = useState(false); useEffect(() => { setIsMounted(true); - setNumNewEvents(0); return () => { setIsMounted(false); }; @@ -140,10 +123,10 @@ export const TokensDashboard: () => JSX.Element = () => { useEffect(() => { isMounted && - slideQuery && + slideID && fetchCatcher( `${FF_Paths.nsPrefix}/${selectedNamespace}${FF_Paths.tokenTransferById( - slideQuery + slideID )}` ) .then((transferRes: ITokenTransfer) => { @@ -152,7 +135,7 @@ export const TokensDashboard: () => JSX.Element = () => { .catch((err) => { reportFetchError(err); }); - }, [slideQuery, isMounted]); + }, [slideID, isMounted]); const smallCards: ISmallCard[] = [ { @@ -186,9 +169,10 @@ export const TokensDashboard: () => JSX.Element = () => { // Small Card UseEffect useEffect(() => { - const qParams = `?count=true&limit=1${dateFilter.filterString}`; + const qParams = `?count=true&limit=1${dateFilter?.filterString ?? ''}`; isMounted && + dateFilter && Promise.all([ // Tokens fetchCatcher( @@ -252,8 +236,7 @@ export const TokensDashboard: () => JSX.Element = () => { ) .catch((err) => { reportFetchError(err); - }) - .finally(() => numNewEvents !== 0 && setNumNewEvents(0)); + }); }, [selectedNamespace, dateFilter, lastRefreshTime, isMounted]); const tokenAccountsColHeaders = [t('key'), t('poolID'), t('balance')]; @@ -330,6 +313,7 @@ export const TokensDashboard: () => JSX.Element = () => { component: ( JSX.Element = () => { // Medium Card UseEffect useEffect(() => { + setIsHistLoading(true); const currentTime = dayjs().unix(); - if (isMounted) { + if (isMounted && dateFilter) { fetchCatcher( `${FF_Paths.nsPrefix}/${selectedNamespace}${FF_Paths.chartsHistogram( BucketCollectionEnum.TokenTransfers, @@ -392,7 +377,8 @@ export const TokensDashboard: () => JSX.Element = () => { .catch((err) => { setTransferHistData([]); reportFetchError(err); - }); + }) + .finally(() => setIsHistLoading(false)); fetchCatcher( `${FF_Paths.nsPrefix}/${selectedNamespace}${FF_Paths.tokenPools}` ) @@ -476,7 +462,7 @@ export const TokensDashboard: () => JSX.Element = () => { ], onClick: () => { setViewTransfer(transfer); - addSlideToParams(transfer.localId); + setSlideSearchParam(transfer.localId); }, leftBorderColor: FF_TRANSFER_CATEGORY_MAP[transfer.type]?.color, })); @@ -484,6 +470,7 @@ export const TokensDashboard: () => JSX.Element = () => { // Recent token transfers useEffect(() => { isMounted && + dateFilter && fetchCatcher( `${FF_Paths.nsPrefix}/${selectedNamespace}${ FF_Paths.tokenTransfers @@ -514,8 +501,8 @@ export const TokensDashboard: () => JSX.Element = () => {
@@ -600,7 +587,7 @@ export const TokensDashboard: () => JSX.Element = () => { open={!!viewTransfer} onClose={() => { setViewTransfer(undefined); - addSlideToParams(undefined); + setSlideSearchParam(null); }} /> )} diff --git a/src/pages/Tokens/views/PoolDetails.tsx b/src/pages/Tokens/views/PoolDetails.tsx index e6300be9..624cfa7f 100644 --- a/src/pages/Tokens/views/PoolDetails.tsx +++ b/src/pages/Tokens/views/PoolDetails.tsx @@ -61,7 +61,7 @@ import { export const PoolDetails: () => JSX.Element = () => { const { selectedNamespace } = useContext(ApplicationContext); const { dateFilter } = useContext(DateFilterContext); - const { slideQuery, addSlideToParams } = useContext(SlideContext); + const { slideID, setSlideSearchParam } = useContext(SlideContext); const { reportFetchError } = useContext(SnackbarContext); const { t } = useTranslation(); const navigate = useNavigate(); @@ -89,10 +89,10 @@ export const PoolDetails: () => JSX.Element = () => { useEffect(() => { isMounted && - slideQuery && + slideID && fetchCatcher( `${FF_Paths.nsPrefix}/${selectedNamespace}${FF_Paths.tokenTransferById( - slideQuery + slideID )}` ) .then((transferRes: ITokenTransfer) => { @@ -101,7 +101,7 @@ export const PoolDetails: () => JSX.Element = () => { .catch((err) => { reportFetchError(err); }); - }, [slideQuery, isMounted]); + }, [slideID, isMounted]); useEffect(() => { if (poolID && isMounted) { @@ -137,6 +137,7 @@ export const PoolDetails: () => JSX.Element = () => { // Token transfers and accounts useEffect(() => { isMounted && + dateFilter && fetchCatcher( `${FF_Paths.nsPrefix}/${selectedNamespace}${ FF_Paths.tokenTransfers @@ -271,7 +272,7 @@ export const PoolDetails: () => JSX.Element = () => { ], onClick: () => { setViewTransfer(transfer); - addSlideToParams(transfer.localId); + setSlideSearchParam(transfer.localId); }, leftBorderColor: FF_TRANSFER_CATEGORY_MAP[transfer.type]?.color, })); @@ -396,7 +397,7 @@ export const PoolDetails: () => JSX.Element = () => { open={!!viewTransfer} onClose={() => { setViewTransfer(undefined); - addSlideToParams(undefined); + setSlideSearchParam(null); }} /> )} diff --git a/src/pages/Tokens/views/Pools.tsx b/src/pages/Tokens/views/Pools.tsx index b6efd19b..62ee0a2f 100644 --- a/src/pages/Tokens/views/Pools.tsx +++ b/src/pages/Tokens/views/Pools.tsx @@ -31,7 +31,6 @@ import { DateFilterContext } from '../../../contexts/DateFilterContext'; import { FilterContext } from '../../../contexts/FilterContext'; import { SnackbarContext } from '../../../contexts/SnackbarContext'; import { - FF_EVENTS, FF_NAV_PATHS, FF_Paths, IDataTableRecord, @@ -42,10 +41,11 @@ import { } from '../../../interfaces'; import { DEFAULT_PADDING, DEFAULT_PAGE_LIMITS } from '../../../theme'; import { fetchCatcher, getFFTime, jsNumberForAddress } from '../../../utils'; -import { isEventType } from '../../../utils/wsEvents'; +import { hasPoolEvent } from '../../../utils/wsEvents'; export const TokensPools: () => JSX.Element = () => { - const { lastEvent, selectedNamespace } = useContext(ApplicationContext); + const { newEvents, lastRefreshTime, clearNewEvents, selectedNamespace } = + useContext(ApplicationContext); const { dateFilter } = useContext(DateFilterContext); const { filterAnchor, setFilterAnchor, filterString } = useContext(FilterContext); @@ -60,26 +60,9 @@ export const TokensPools: () => JSX.Element = () => { const [currentPage, setCurrentPage] = useState(0); const [rowsPerPage, setRowsPerPage] = useState(DEFAULT_PAGE_LIMITS[1]); - // Last event tracking - const [numNewEvents, setNumNewEvents] = useState(0); - const [lastRefreshTime, setLastRefresh] = useState( - new Date().toISOString() - ); - - useEffect(() => { - isMounted && - isEventType(lastEvent, FF_EVENTS.TOKEN_POOL_CONFIRMED) && - setNumNewEvents(numNewEvents + 1); - }, [lastEvent]); - - const refreshData = () => { - setNumNewEvents(0); - setLastRefresh(new Date().toString()); - }; useEffect(() => { setIsMounted(true); - setNumNewEvents(0); return () => { setIsMounted(false); }; @@ -88,6 +71,7 @@ export const TokensPools: () => JSX.Element = () => { // Token pools useEffect(() => { isMounted && + dateFilter && fetchCatcher( `${FF_Paths.nsPrefix}/${selectedNamespace}${ FF_Paths.tokenPools @@ -101,9 +85,7 @@ export const TokensPools: () => JSX.Element = () => { }) .catch((err) => { reportFetchError(err); - }) - .finally(() => numNewEvents !== 0 && setNumNewEvents(0)); - numNewEvents !== 0 && setNumNewEvents(0); + }); }, [ rowsPerPage, currentPage, @@ -194,8 +176,8 @@ export const TokensPools: () => JSX.Element = () => {
diff --git a/src/pages/Tokens/views/Transfers.tsx b/src/pages/Tokens/views/Transfers.tsx index 7152823c..301f5042 100644 --- a/src/pages/Tokens/views/Transfers.tsx +++ b/src/pages/Tokens/views/Transfers.tsx @@ -45,7 +45,6 @@ import { TransferFilters, } from '../../../interfaces'; import { - FF_EVENTS, FF_TRANSFER_CATEGORY_MAP, TransferIconMap, } from '../../../interfaces/enums'; @@ -61,14 +60,15 @@ import { makeKeyArray, } from '../../../utils/charts'; import { makeTransferHistogram } from '../../../utils/histograms/transferHistogram'; -import { isEventType } from '../../../utils/wsEvents'; +import { hasTransferEvent } from '../../../utils/wsEvents'; export const TokensTransfers: () => JSX.Element = () => { - const { lastEvent, selectedNamespace } = useContext(ApplicationContext); + const { newEvents, lastRefreshTime, clearNewEvents, selectedNamespace } = + useContext(ApplicationContext); const { dateFilter } = useContext(DateFilterContext); const { filterAnchor, setFilterAnchor, filterString } = useContext(FilterContext); - const { slideQuery, addSlideToParams } = useContext(SlideContext); + const { slideID, setSlideSearchParam } = useContext(SlideContext); const { reportFetchError } = useContext(SnackbarContext); const { t } = useTranslation(); const [isMounted, setIsMounted] = useState(false); @@ -84,27 +84,10 @@ export const TokensTransfers: () => JSX.Element = () => { >(); const [currentPage, setCurrentPage] = useState(0); const [rowsPerPage, setRowsPerPage] = useState(DEFAULT_PAGE_LIMITS[1]); - // Last event tracking - const [numNewEvents, setNumNewEvents] = useState(0); - const [lastRefreshTime, setLastRefresh] = useState( - new Date().toISOString() - ); - - useEffect(() => { - isMounted && - (isEventType(lastEvent, FF_EVENTS.TOKEN_TRANSFER_CONFIRMED) || - isEventType(lastEvent, FF_EVENTS.TOKEN_TRANSFER_FAILED)) && - setNumNewEvents(numNewEvents + 1); - }, [lastEvent]); - - const refreshData = () => { - setNumNewEvents(0); - setLastRefresh(new Date().toString()); - }; + const [isHistLoading, setIsHistLoading] = useState(false); useEffect(() => { setIsMounted(true); - setNumNewEvents(0); return () => { setIsMounted(false); }; @@ -112,10 +95,10 @@ export const TokensTransfers: () => JSX.Element = () => { useEffect(() => { isMounted && - slideQuery && + slideID && fetchCatcher( `${FF_Paths.nsPrefix}/${selectedNamespace}${FF_Paths.tokenTransferById( - slideQuery + slideID )}` ) .then((transferRes: ITokenTransfer) => { @@ -124,11 +107,12 @@ export const TokensTransfers: () => JSX.Element = () => { .catch((err) => { reportFetchError(err); }); - }, [slideQuery, isMounted]); + }, [slideID, isMounted]); // Token transfers useEffect(() => { isMounted && + dateFilter && fetchCatcher( `${FF_Paths.nsPrefix}/${selectedNamespace}${ FF_Paths.tokenTransfers @@ -145,7 +129,6 @@ export const TokensTransfers: () => JSX.Element = () => { .catch((err) => { reportFetchError(err); }); - numNewEvents !== 0 && setNumNewEvents(0); }, [ rowsPerPage, currentPage, @@ -158,8 +141,10 @@ export const TokensTransfers: () => JSX.Element = () => { // Histogram useEffect(() => { + setIsHistLoading(true); const currentTime = dayjs().unix(); isMounted && + dateFilter && fetchCatcher( `${FF_Paths.nsPrefix}/${selectedNamespace}${FF_Paths.chartsHistogram( BucketCollectionEnum.TokenTransfers, @@ -173,7 +158,8 @@ export const TokensTransfers: () => JSX.Element = () => { }) .catch((err) => { reportFetchError(err); - }); + }) + .finally(() => setIsHistLoading(false)); }, [selectedNamespace, dateFilter, lastRefreshTime, isMounted]); const tokenTransferColHeaders = [ @@ -238,7 +224,7 @@ export const TokensTransfers: () => JSX.Element = () => { ], onClick: () => { setViewTransfer(transfer); - addSlideToParams(transfer.localId); + setSlideSearchParam(transfer.localId); }, leftBorderColor: FF_TRANSFER_CATEGORY_MAP[transfer.type]?.color, })); @@ -248,8 +234,8 @@ export const TokensTransfers: () => JSX.Element = () => {
@@ -271,6 +257,7 @@ export const TokensTransfers: () => JSX.Element = () => { keys={makeKeyArray(FF_TRANSFER_CATEGORY_MAP)} includeLegend={true} emptyText={t('noTransfers')} + isLoading={isHistLoading} isEmpty={isHistogramEmpty(transferHistData ?? [])} /> @@ -309,7 +296,7 @@ export const TokensTransfers: () => JSX.Element = () => { open={!!viewTransfer} onClose={() => { setViewTransfer(undefined); - addSlideToParams(undefined); + setSlideSearchParam(null); }} /> )} diff --git a/src/utils/filters.ts b/src/utils/filters.ts index f8114014..e53ea85b 100644 --- a/src/utils/filters.ts +++ b/src/utils/filters.ts @@ -1,10 +1,10 @@ import dayjs from 'dayjs'; -import { CreatedFilterOptions, ICreatedTimeFilter } from '../interfaces'; +import { ITimeFilterObject, TimeFilterEnum } from '../interfaces'; -export const getCreatedTimeFilter = ( - createdFilter: CreatedFilterOptions, +export const getTimeFilterObject = ( + createdFilter: TimeFilterEnum, timestampKey?: boolean -): ICreatedTimeFilter => { +): ITimeFilterObject => { let createdFilterTime: number; switch (createdFilter) { diff --git a/src/utils/wsEvents.tsx b/src/utils/wsEvents.tsx index 457e9628..bcbf3e85 100644 --- a/src/utils/wsEvents.tsx +++ b/src/utils/wsEvents.tsx @@ -1,56 +1,58 @@ -import { - FF_EVENTS, - FF_MESSAGES, - FF_MESSAGES_CATEGORY_MAP, - FF_TX, - FF_TX_CATEGORY_MAP, -} from '../interfaces'; - -export enum WsEventTypes { - BLOCKCHAIN_EVENT = 'blockchainevent', - CONTRACT_API = 'contract_api_confirmed', - // Denotes normal event type - EVENT = 'type', - MESSAGE = 'message', - TRANSACTION = 'transaction', -} - -export const isEventType = ( - event: any | undefined, - eventType: string -): boolean => { - if (!event) { - return false; - } - switch (eventType) { - case WsEventTypes.BLOCKCHAIN_EVENT: - return event[WsEventTypes.BLOCKCHAIN_EVENT] !== undefined; - case FF_EVENTS.CONTRACT_API_CONFIRMED: - return event.type === FF_EVENTS.CONTRACT_API_CONFIRMED; - case FF_EVENTS.DATATYPE_CONFIRMED: - return event.type === FF_EVENTS.DATATYPE_CONFIRMED; - case FF_EVENTS.CONTRACT_INTERFACE_CONFIRMED: - return event.type === FF_EVENTS.CONTRACT_INTERFACE_CONFIRMED; - case WsEventTypes.MESSAGE: - return ( - event?.message?.header?.type && - FF_MESSAGES_CATEGORY_MAP[event.message.header.type as FF_MESSAGES] !== - undefined - ); - case FF_EVENTS.TOKEN_POOL_CONFIRMED: - return event.type === FF_EVENTS.TOKEN_POOL_CONFIRMED; - case FF_EVENTS.TOKEN_TRANSFER_CONFIRMED: - return event.type === FF_EVENTS.TOKEN_TRANSFER_CONFIRMED; - case FF_EVENTS.TOKEN_TRANSFER_FAILED: - return event.type === FF_EVENTS.TOKEN_TRANSFER_FAILED; - case WsEventTypes.TRANSACTION: - return ( - event?.transaction?.type && - FF_TX_CATEGORY_MAP[event.transaction.type as FF_TX] !== undefined - ); - case WsEventTypes.EVENT: - return event?.type; - default: - return false; - } +import { FF_EVENTS } from '../interfaces'; + +export const hasApiEvent = (events: FF_EVENTS[]) => { + return events.includes(FF_EVENTS.CONTRACT_API_CONFIRMED); +}; + +export const hasBlockchainEvent = (events: FF_EVENTS[]) => { + return ( + events.includes(FF_EVENTS.BLOCKCHAIN_EVENT_RECEIVED) || + events.includes(FF_EVENTS.CONTRACT_API_CONFIRMED) || + events.includes(FF_EVENTS.CONTRACT_INTERFACE_CONFIRMED) || + events.includes(FF_EVENTS.DATATYPE_CONFIRMED) || + events.includes(FF_EVENTS.IDENTITY_CONFIRMED) || + events.includes(FF_EVENTS.IDENTITY_UPDATED) || + events.includes(FF_EVENTS.NS_CONFIRMED) + ); +}; + +export const hasDatatypeEvent = (events: FF_EVENTS[]) => { + return events.includes(FF_EVENTS.DATATYPE_CONFIRMED); +}; + +export const hasDataEvent = (events: FF_EVENTS[]) => { + return ( + events.includes(FF_EVENTS.MSG_CONFIRMED) || + events.includes(FF_EVENTS.MSG_REJECTED) + ); +}; + +export const hasInterfaceEvent = (events: FF_EVENTS[]) => { + return events.includes(FF_EVENTS.CONTRACT_INTERFACE_CONFIRMED); +}; + +export const hasOffchainEvent = (events: FF_EVENTS[]) => { + return ( + events.includes(FF_EVENTS.MSG_CONFIRMED) || + events.includes(FF_EVENTS.MSG_REJECTED) || + events.includes(FF_EVENTS.DATATYPE_CONFIRMED) + ); +}; + +export const hasPoolEvent = (events: FF_EVENTS[]) => { + return events.includes(FF_EVENTS.TOKEN_POOL_CONFIRMED); +}; + +export const hasTransferEvent = (events: FF_EVENTS[]) => { + return ( + events.includes(FF_EVENTS.TOKEN_POOL_CONFIRMED) || + events.includes(FF_EVENTS.TOKEN_APPROVAL_CONFIRMED) || + events.includes(FF_EVENTS.TOKEN_APPROVAL_OP_FAILED) || + events.includes(FF_EVENTS.TOKEN_TRANSFER_CONFIRMED) || + events.includes(FF_EVENTS.TOKEN_TRANSFER_FAILED) + ); +}; + +export const hasTxEvent = (events: FF_EVENTS[]) => { + return events.includes(FF_EVENTS.TX_SUBMITTED); }; From 76c0fafaf1384ac704dfb00ffcacfcb0c9172374 Mon Sep 17 00:00:00 2001 From: David Echelberger Date: Mon, 28 Mar 2022 13:56:47 -0400 Subject: [PATCH 3/5] [bugfixes] table filtering Signed-off-by: David Echelberger --- src/components/AppWrapper.tsx | 118 ++++++++++++++++-------- src/components/Filters/FilterButton.tsx | 9 +- src/contexts/FilterContext.tsx | 1 + 3 files changed, 82 insertions(+), 46 deletions(-) diff --git a/src/components/AppWrapper.tsx b/src/components/AppWrapper.tsx index 49ff7048..1c0ecc64 100644 --- a/src/components/AppWrapper.tsx +++ b/src/components/AppWrapper.tsx @@ -11,11 +11,11 @@ import { DateFilterContext } from '../contexts/DateFilterContext'; import { FilterContext } from '../contexts/FilterContext'; import { SlideContext } from '../contexts/SlideContext'; import { - CreatedFilterOptions, - ICreatedTimeFilter, + ITimeFilterObject, NAMESPACES_PATH, + TimeFilterEnum, } from '../interfaces'; -import { getCreatedTimeFilter, isValidUUID } from '../utils'; +import { getTimeFilterObject, isValidUUID } from '../utils'; import { Navigation, NAV_WIDTH } from './Navigation/Navigation'; const Main = styled('main')({ @@ -38,7 +38,7 @@ export const SLIDE_QUERY_KEY = 'slide'; export const TIME_QUERY_KEY = 'time'; export const AppWrapper: React.FC = () => { - const { pathname } = useLocation(); + const { pathname, search } = useLocation(); const { selectedNamespace } = useContext(ApplicationContext); const [filterAnchor, setFilterAnchor] = useState( null @@ -47,12 +47,9 @@ export const AppWrapper: React.FC = () => { // Table filters const [filterArray, setFilterArray] = useState([]); const [filterString, setFilterString] = useState(''); - // Slide filters - const [slideQuery, setSlideQuery] = useState(null); + const [slideID, setSlideID] = useState(null); // Date filter - const [dateFilter, setDateFilter] = useState( - getCreatedTimeFilter('24hours') - ); + const [dateFilter, setDateFilter] = useState(); if (pathname === '/') { return ( @@ -63,28 +60,68 @@ export const AppWrapper: React.FC = () => { ); } - // Table Filters useEffect(() => { - const filterArray = searchParams.getAll(FILTERS_QUERY_KEY); - setFilterArray(filterArray); - setFilterString(`&${filterArray.join('&')}`); - }, [pathname]); + initializeTimeSearchParams(); + initializeSlideSearchParams(); + initializeTableFilterSearchParams(); + }, [pathname, search]); - // Slide ID - useEffect(() => { - const slideQuery = searchParams.get(SLIDE_QUERY_KEY); - slideQuery !== null && setSlideQuery(slideQuery); - }, [pathname, setSearchParams]); + const initializeTimeSearchParams = () => { + // If date has already been set + if ( + dateFilter?.filterShortString && + dateFilter?.filterShortString in TimeFilterEnum + ) { + setTimeSearchParam(dateFilter.filterShortString); + return; + } + // If time param is invalid + const existingTimeParam = searchParams.get(TIME_QUERY_KEY); + if (existingTimeParam === null || !(existingTimeParam in TimeFilterEnum)) { + setTimeSearchParam(TimeFilterEnum['24hours']); + } else { + // Set filter string for components to consume + setDateFilter(getTimeFilterObject(existingTimeParam as TimeFilterEnum)); + } + }; - // Time string - useEffect(() => { - const timeString = searchParams.get(TIME_QUERY_KEY) as CreatedFilterOptions; - if (timeString === null) { - addDateToParams(dateFilter.filterShortString); + const setTimeSearchParam = (timeFilter: TimeFilterEnum) => { + searchParams.set(TIME_QUERY_KEY, timeFilter); + setSearchParams(searchParams); + if ( + getTimeFilterObject(timeFilter).filterShortString !== + dateFilter?.filterShortString + ) { + setDateFilter(getTimeFilterObject(timeFilter)); + } + }; + + const initializeSlideSearchParams = () => { + setSlideID(null); + const existingSlideParam = searchParams.get(SLIDE_QUERY_KEY); + if (existingSlideParam === null || !isValidUUID(existingSlideParam)) { + setSlideSearchParam(null); } else { - setDateFilter(getCreatedTimeFilter(timeString)); + setSlideSearchParam(existingSlideParam); } - }, [pathname]); + }; + + const setSlideSearchParam = (slideID: string | null) => { + if (slideID === null) { + searchParams.delete(SLIDE_QUERY_KEY); + setSearchParams(searchParams); + } else if (isValidUUID(slideID)) { + searchParams.set(SLIDE_QUERY_KEY, slideID); + setSearchParams(searchParams); + setSlideID(slideID); + } + }; + + const initializeTableFilterSearchParams = () => { + const existingFilterArray = searchParams.getAll(FILTERS_QUERY_KEY); + setFilterArray(existingFilterArray); + setFilterString(`&${existingFilterArray.join('&')}`); + }; const addFilterToParams = (filter: string) => { searchParams.append(FILTERS_QUERY_KEY, filter); @@ -101,21 +138,21 @@ export const AppWrapper: React.FC = () => { setFilterString(''); }; - const addSlideToParams = (slideID: string | undefined) => { - if (slideID === undefined) { - searchParams.delete(SLIDE_QUERY_KEY); - } else { - isValidUUID(slideID) && searchParams.set(SLIDE_QUERY_KEY, slideID); + const removeFilter = (filterToRemove: string) => { + const filters = searchParams.getAll(FILTERS_QUERY_KEY); + if (filters.length > 0) { + filters.forEach((f) => { + if (f !== filterToRemove) { + searchParams.append(FILTERS_QUERY_KEY, f); + } + }); setSearchParams(searchParams); + const filterArray = searchParams.getAll(FILTERS_QUERY_KEY); + setFilterArray(filterArray); + setFilterString(`&${filterArray.join('&')}`); } }; - const addDateToParams = (timeFilterString: CreatedFilterOptions) => { - searchParams.set(TIME_QUERY_KEY, timeFilterString); - setSearchParams(searchParams); - setDateFilter(getCreatedTimeFilter(timeFilterString)); - }; - return ( { filterString, filterArray, addFilterToParams, + removeFilter, clearAllFilters, }} > @@ -132,13 +170,13 @@ export const AppWrapper: React.FC = () => { value={{ searchParams, dateFilter, - addDateToParams, + setTimeSearchParam, }} >
diff --git a/src/components/Filters/FilterButton.tsx b/src/components/Filters/FilterButton.tsx index cca7db8d..f6c29018 100644 --- a/src/components/Filters/FilterButton.tsx +++ b/src/components/Filters/FilterButton.tsx @@ -26,7 +26,8 @@ interface Props { export const FilterButton: React.FC = ({ onSetFilterAnchor }) => { const { t } = useTranslation(); - const { clearAllFilters, filterArray } = useContext(FilterContext); + const { clearAllFilters, filterArray, removeFilter } = + useContext(FilterContext); const handleOpenFilter = (event: React.MouseEvent) => { onSetFilterAnchor(event); @@ -51,11 +52,7 @@ export const FilterButton: React.FC = ({ onSetFilterAnchor }) => { {filterArray.map((filter, index) => ( - handleRemoveFilter(filter)} - label={filter} - /> + removeFilter(filter)} label={filter} /> ))} diff --git a/src/contexts/FilterContext.tsx b/src/contexts/FilterContext.tsx index 2af5469d..d509cc64 100644 --- a/src/contexts/FilterContext.tsx +++ b/src/contexts/FilterContext.tsx @@ -22,6 +22,7 @@ export interface IFilterContext { filterString: string; filterArray: string[]; addFilterToParams: (filter: string) => void; + removeFilter: (filterToRemove: string) => void; clearAllFilters: () => void; } From 1447b1602a083456839cf6a8a816bbdad399a009 Mon Sep 17 00:00:00 2001 From: David Echelberger Date: Mon, 28 Mar 2022 14:00:40 -0400 Subject: [PATCH 4/5] [bugfixes] filter table fix Signed-off-by: David Echelberger --- src/components/AppWrapper.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/AppWrapper.tsx b/src/components/AppWrapper.tsx index 1c0ecc64..8222b556 100644 --- a/src/components/AppWrapper.tsx +++ b/src/components/AppWrapper.tsx @@ -140,6 +140,7 @@ export const AppWrapper: React.FC = () => { const removeFilter = (filterToRemove: string) => { const filters = searchParams.getAll(FILTERS_QUERY_KEY); + searchParams.delete(FILTERS_QUERY_KEY); if (filters.length > 0) { filters.forEach((f) => { if (f !== filterToRemove) { From 673838477048fddbff06585242aae48007d0be53 Mon Sep 17 00:00:00 2001 From: David Echelberger Date: Mon, 28 Mar 2022 14:14:45 -0400 Subject: [PATCH 5/5] [bugfixes] timeline Signed-off-by: David Echelberger --- src/pages/Activity/views/Timeline.tsx | 60 ++++++++++++--------------- 1 file changed, 26 insertions(+), 34 deletions(-) diff --git a/src/pages/Activity/views/Timeline.tsx b/src/pages/Activity/views/Timeline.tsx index 5d3e9993..31e8451a 100644 --- a/src/pages/Activity/views/Timeline.tsx +++ b/src/pages/Activity/views/Timeline.tsx @@ -55,16 +55,16 @@ import { } from '../../../utils'; import { isHistogramEmpty } from '../../../utils/charts'; import { isOppositeTimelineEvent } from '../../../utils/timeline'; -import { isEventType, WsEventTypes } from '../../../utils/wsEvents'; const ROWS_PER_PAGE = 25; export const ActivityTimeline: () => JSX.Element = () => { - const { lastEvent, selectedNamespace } = useContext(ApplicationContext); + const { newEvents, lastRefreshTime, clearNewEvents, selectedNamespace } = + useContext(ApplicationContext); const { dateFilter } = useContext(DateFilterContext); const { filterAnchor, setFilterAnchor, filterString } = useContext(FilterContext); - const { slideQuery, addSlideToParams } = useContext(SlideContext); + const { slideID, setSlideSearchParam } = useContext(SlideContext); const { reportFetchError } = useContext(SnackbarContext); const [isMounted, setIsMounted] = useState(false); const [eventHistData, setEventHistData] = useState(); @@ -74,44 +74,29 @@ export const ActivityTimeline: () => JSX.Element = () => { const queryClient = useQueryClient(); const [isVisible, setIsVisible] = useState(0); // Last event tracking - const [numNewEvents, setNumNewEvents] = useState(0); - const [lastRefreshTime, setLastRefresh] = useState( - new Date().toISOString() - ); - - useEffect(() => { - isMounted && - isEventType(lastEvent, WsEventTypes.EVENT) && - setNumNewEvents(numNewEvents + 1); - }, [lastEvent]); - - const refreshData = () => { - setNumNewEvents(0); - setLastRefresh(new Date().toString()); - }; + const [isHistLoading, setIsHistLoading] = useState(false); useEffect(() => { setIsMounted(true); - setNumNewEvents(0); return () => { setIsMounted(false); }; }, []); useEffect(() => { - if (isMounted && slideQuery) { + if (isMounted && slideID) { fetchCatcher( - `${FF_Paths.nsPrefix}/${selectedNamespace}${FF_Paths.events}?id=${slideQuery}` + `${FF_Paths.nsPrefix}/${selectedNamespace}${FF_Paths.events}?id=${slideID}` ).then((eventRes: IEvent[]) => { isMounted && eventRes.length > 0 && setViewEvent(eventRes[0]); }); fetchCatcher( - `${FF_Paths.nsPrefix}/${selectedNamespace}${FF_Paths.transactions}?id=${slideQuery}` + `${FF_Paths.nsPrefix}/${selectedNamespace}${FF_Paths.transactions}?id=${slideID}` ).then((txRes: ITransaction[]) => { isMounted && txRes.length > 0 && setViewTx(txRes[0]); }); } - }, [slideQuery, isMounted]); + }, [slideID, isMounted]); const { data, fetchNextPage, hasNextPage, refetch } = useInfiniteQuery( 'events', @@ -120,7 +105,7 @@ export const ActivityTimeline: () => JSX.Element = () => { `${FF_Paths.nsPrefix}/${selectedNamespace}${ FF_Paths.events }?count&limit=${ROWS_PER_PAGE}&skip=${ROWS_PER_PAGE * pageParam}${ - dateFilter.filterString + dateFilter?.filterString ?? '' }${filterString ?? ''}&fetchreferences` ); if (res.ok) { @@ -144,9 +129,11 @@ export const ActivityTimeline: () => JSX.Element = () => { // Events Histogram useEffect(() => { + setIsHistLoading(true); const currentTime = dayjs().unix(); isMounted && + dateFilter && fetchCatcher( `${FF_Paths.nsPrefix}/${selectedNamespace}${FF_Paths.chartsHistogram( BucketCollectionEnum.Events, @@ -160,7 +147,8 @@ export const ActivityTimeline: () => JSX.Element = () => { }) .catch((err) => { reportFetchError(err); - }); + }) + .finally(() => setIsHistLoading(false)); }, [selectedNamespace, dateFilter, lastRefreshTime, isMounted]); useEffect(() => { @@ -190,18 +178,17 @@ export const ActivityTimeline: () => JSX.Element = () => { { setViewEvent(event); - addSlideToParams(event.id); + setSlideSearchParam(event?.id); }} onHandleViewTx={(tx: ITransaction) => { setViewTx(tx); - addSlideToParams(tx.id); + setSlideSearchParam(tx?.id); }} link={FF_NAV_PATHS.activityTxDetailPath( selectedNamespace, - event.tx + event?.tx )} {...{ event }} - linkState={{ state: event }} /> ), opposite: isOppositeTimelineEvent(event.type), @@ -214,7 +201,11 @@ export const ActivityTimeline: () => JSX.Element = () => { return ( <> -
+
JSX.Element = () => { EventCategoryEnum.TOKENS, ]} includeLegend={true} + isLoading={isHistLoading} isEmpty={isHistogramEmpty(eventHistData ?? [])} emptyText={t('noActivity')} /> @@ -254,8 +246,8 @@ export const ActivityTimeline: () => JSX.Element = () => { height={'calc(100vh - 475px)'} fetchMoreData={() => setIsVisible(isVisible + 1)} hasMoreData={hasNextPage} - numNewEvents={numNewEvents} - fetchNewData={() => refreshData()} + hasNewEvents={newEvents.length > 0} + fetchNewData={clearNewEvents} /> @@ -274,7 +266,7 @@ export const ActivityTimeline: () => JSX.Element = () => { open={!!viewEvent} onClose={() => { setViewEvent(undefined); - addSlideToParams(undefined); + setSlideSearchParam(null); }} /> )} @@ -284,7 +276,7 @@ export const ActivityTimeline: () => JSX.Element = () => { open={!!viewTx} onClose={() => { setViewTx(undefined); - addSlideToParams(undefined); + setSlideSearchParam(null); }} /> )}