mirror of https://github.com/buster-so/buster.git
174 lines
4.9 KiB
TypeScript
174 lines
4.9 KiB
TypeScript
import React, { PropsWithChildren, useCallback, useRef, useTransition } from 'react';
|
|
import {
|
|
createContext,
|
|
ContextSelector,
|
|
useContextSelector
|
|
} from '@fluentui/react-context-selector';
|
|
import { useMemoizedFn, useMount } from 'ahooks';
|
|
import { useBusterWebSocket } from '../BusterWebSocket';
|
|
import type { BusterMetricData } from '../Metrics';
|
|
import { MetricEvent_fetchingData } from '@/api/buster_socket/metrics/eventsInterfaces';
|
|
import { DEFAULT_MESSAGE_DATA } from './config';
|
|
import { createMockData } from './MOCK_DATA';
|
|
|
|
const useMetricData = () => {
|
|
const busterSocket = useBusterWebSocket();
|
|
const [isPending, startTransition] = useTransition();
|
|
|
|
const metricDataRef = useRef<Record<string, BusterMetricData>>({});
|
|
|
|
const _setMetricData = useMemoizedFn(
|
|
(metricId: string, newMetricData: Partial<BusterMetricData>) => {
|
|
metricDataRef.current = {
|
|
...metricDataRef.current,
|
|
[metricId]: {
|
|
...DEFAULT_MESSAGE_DATA,
|
|
...metricDataRef.current[metricId],
|
|
...newMetricData,
|
|
metricId
|
|
}
|
|
};
|
|
startTransition(() => {
|
|
//trigger re-render
|
|
});
|
|
}
|
|
);
|
|
|
|
const _onGetFetchingData = useMemoizedFn((payload: MetricEvent_fetchingData) => {
|
|
const { data, data_metadata, code, progress, metric_id: metricId } = payload;
|
|
const currentMetric = getDataByMetricId(metricId);
|
|
const fallbackData = data || currentMetric?.data;
|
|
const fallbackDataMetadata = data_metadata || currentMetric?.data_metadata;
|
|
const isCompleted = progress === 'completed';
|
|
|
|
onSetDataForMetric({
|
|
metricId,
|
|
data: fallbackData,
|
|
data_metadata: fallbackDataMetadata,
|
|
isDataFromRerun: false,
|
|
fetchedData: isCompleted,
|
|
fetchingData: !isCompleted,
|
|
code
|
|
});
|
|
});
|
|
|
|
const onSetDataForMetric = useMemoizedFn(
|
|
({
|
|
metricId,
|
|
data,
|
|
data_metadata,
|
|
isDataFromRerun,
|
|
fetchedData,
|
|
fetchingData,
|
|
code
|
|
}: {
|
|
metricId: string;
|
|
data: BusterMetricData['data'];
|
|
data_metadata?: BusterMetricData['data_metadata'];
|
|
isDataFromRerun?: boolean;
|
|
fetchedData?: boolean;
|
|
fetchingData?: boolean;
|
|
code: string | null;
|
|
}) => {
|
|
const setKey = isDataFromRerun ? 'dataFromRerun' : 'data';
|
|
const prev = getDataByMetricId(metricId);
|
|
_setMetricData(metricId, {
|
|
[setKey]: data,
|
|
fetchedAt: Date.now(),
|
|
fetched: fetchedData ?? true,
|
|
fetching: fetchingData ?? false,
|
|
data_metadata: data_metadata ?? prev?.data_metadata,
|
|
code
|
|
});
|
|
}
|
|
);
|
|
|
|
const onSetMetricDataCode = useMemoizedFn(
|
|
({ messageId, code }: { messageId: string; code: string }) => {
|
|
_setMetricData(messageId, { code });
|
|
}
|
|
);
|
|
|
|
const fetchDataByMetricId = useMemoizedFn(async ({ metricId }: { metricId: string }) => {
|
|
const selectedMetricData = getDataByMetricId(metricId);
|
|
|
|
if (selectedMetricData?.fetching || selectedMetricData?.fetched) {
|
|
return;
|
|
}
|
|
|
|
_setMetricData(metricId, {
|
|
fetching: true
|
|
});
|
|
|
|
setTimeout(() => {
|
|
//TODO: remove mock data
|
|
// _setMetricData(metricId, { ...MOCK_DATA, fetched: true });
|
|
onSetDataForMetric(createMockData(metricId));
|
|
}, Math.random() * 5000);
|
|
|
|
return await busterSocket.emitAndOnce({
|
|
emitEvent: {
|
|
route: '/metrics/data',
|
|
payload: { id: metricId }
|
|
},
|
|
responseEvent: {
|
|
route: '/metrics/get:fetchingData',
|
|
callback: _onGetFetchingData
|
|
}
|
|
});
|
|
});
|
|
|
|
const getDataByMetricId = useCallback(
|
|
(metricId: string | undefined) => {
|
|
if (metricId && metricDataRef.current[metricId]) {
|
|
return metricDataRef.current[metricId];
|
|
}
|
|
return { ...DEFAULT_MESSAGE_DATA, metricId };
|
|
},
|
|
[isPending]
|
|
);
|
|
|
|
const getDataByMetricIdMemoized = useMemoizedFn(
|
|
(metricId: string): BusterMetricData | undefined => {
|
|
return metricDataRef.current[metricId];
|
|
}
|
|
);
|
|
|
|
useMount(() => {
|
|
busterSocket.on({
|
|
route: '/metrics/get:fetchingData',
|
|
callback: _onGetFetchingData
|
|
});
|
|
});
|
|
|
|
return {
|
|
metricData: metricDataRef.current,
|
|
onSetMetricDataCode,
|
|
onSetDataForMetric,
|
|
fetchDataByMetricId,
|
|
getDataByMetricId,
|
|
getDataByMetricIdMemoized
|
|
};
|
|
};
|
|
|
|
const BusterMetricDataContext = createContext<ReturnType<typeof useMetricData>>(
|
|
{} as ReturnType<typeof useMetricData>
|
|
);
|
|
|
|
export const BusterMetricDataProvider: React.FC<PropsWithChildren> = ({ children }) => {
|
|
const messageDataContext = useMetricData();
|
|
|
|
return (
|
|
<BusterMetricDataContext.Provider value={messageDataContext}>
|
|
{children}
|
|
</BusterMetricDataContext.Provider>
|
|
);
|
|
};
|
|
BusterMetricDataProvider.displayName = 'BusterMetricDataProvider';
|
|
|
|
export const useBusterMetricDataContextSelector = <T,>(
|
|
selector: ContextSelector<ReturnType<typeof useMetricData>, T>
|
|
) => {
|
|
return useContextSelector(BusterMetricDataContext, selector);
|
|
};
|