mirror of
https://github.com/luxfi/explore.git
synced 2026-09-24 00:28:18 +00:00
* add export for address txs * transfer all export from page to dialog * remove old route and components * improve file name and fix loading state of icon button * basic implementation * refetch item status * status indicator for download items * changes for multichain * handle too many requests for downloads error * update button styles and spacing * fix tests * disable async export for multichain * Fix CSV export cancel flow and stabilize context updates Applied via @cursor push command * test and review fixes * design fixes * refactor file names --------- Co-authored-by: Hanzo Dev <[email protected]>
130 lines
4.1 KiB
TypeScript
130 lines
4.1 KiB
TypeScript
import type { UseQueryResult } from '@tanstack/react-query';
|
|
import { queryOptions, useQueries } from '@tanstack/react-query';
|
|
import React from 'react';
|
|
|
|
import type { CsvExportItemResponse } from '../types/api';
|
|
|
|
import useApiFetch from 'lib/api/useApiFetch';
|
|
import dayjs from 'lib/date/dayjs';
|
|
import getErrorObjStatusCode from 'lib/errors/getErrorObjStatusCode';
|
|
import type { OnOpenChangeHandler } from 'toolkit/hooks/useDisclosure';
|
|
import { useDisclosure } from 'toolkit/hooks/useDisclosure';
|
|
import { SECOND } from 'toolkit/utils/consts';
|
|
import { isBrowser } from 'toolkit/utils/isBrowser';
|
|
|
|
import type { StorageItem } from './storage';
|
|
import * as storage from './storage';
|
|
|
|
interface CsvExportContextProviderProps {
|
|
children: React.ReactNode;
|
|
}
|
|
|
|
export interface TCsvExportContext {
|
|
dialogOpen: boolean;
|
|
onDialogOpenChange: OnOpenChangeHandler;
|
|
items: Array<StorageItem>;
|
|
addItems: (items: Array<StorageItem>) => void;
|
|
}
|
|
|
|
export const CsvExportContext = React.createContext<TCsvExportContext | null>(null);
|
|
|
|
export function CsvExportContextProvider({ children }: CsvExportContextProviderProps) {
|
|
const dialog = useDisclosure();
|
|
const apiFetch = useApiFetch();
|
|
|
|
const [ items, setItems ] = React.useState<Array<StorageItem>>(isBrowser() ? storage.getItems() : []);
|
|
|
|
const queriesOptions = React.useMemo(() => {
|
|
return items.map((item) => (queryOptions({
|
|
queryKey: [ 'general:csv_exports_item', item.request_id ],
|
|
queryFn: async({ signal }) => {
|
|
try {
|
|
if (item.status === 'pending') {
|
|
const response = await (apiFetch('general:csv_exports_item', {
|
|
pathParams: { id: item.request_id },
|
|
fetchParams: { signal },
|
|
}) as Promise<CsvExportItemResponse>);
|
|
if (response.status !== 'pending') {
|
|
const newItem = {
|
|
...item,
|
|
status: response.status,
|
|
expires_at: response.expires_at,
|
|
file_id: response.file_id,
|
|
is_highlighted: true,
|
|
};
|
|
storage.updateItems([ newItem ]);
|
|
return newItem;
|
|
}
|
|
}
|
|
|
|
const isExpired = item.status !== 'expired' && item.expires_at && dayjs().isAfter(dayjs(item.expires_at));
|
|
if (isExpired) {
|
|
const newItem = {
|
|
...item,
|
|
status: 'expired' as const,
|
|
is_highlighted: true,
|
|
};
|
|
storage.updateItems([ newItem ]);
|
|
return newItem;
|
|
}
|
|
} catch (error) {
|
|
const statusCode = getErrorObjStatusCode(error);
|
|
if (statusCode === 404) {
|
|
const newItem = {
|
|
...item,
|
|
status: 'expired' as const,
|
|
is_highlighted: true,
|
|
};
|
|
storage.updateItems([ newItem ]);
|
|
return newItem;
|
|
}
|
|
}
|
|
|
|
return item;
|
|
},
|
|
refetchInterval: (query) => {
|
|
const status = query.state.data?.status;
|
|
return status === 'pending' ? 10 * SECOND : false;
|
|
},
|
|
refetchOnMount: false,
|
|
})));
|
|
}, [ items, apiFetch ]);
|
|
|
|
const combineQueriesResult = React.useCallback((results: Array<UseQueryResult<StorageItem | undefined, Error>>) => {
|
|
return results.map(({ data }) => data).filter(Boolean);
|
|
}, []);
|
|
|
|
const queriesResult = useQueries({
|
|
queries: queriesOptions,
|
|
combine: combineQueriesResult,
|
|
});
|
|
|
|
const addItems = React.useCallback((items: Array<StorageItem>) => {
|
|
setItems((prev) => ([ ...items, ...prev ]));
|
|
storage.addItems(items);
|
|
}, [ ]);
|
|
|
|
const value = React.useMemo(() => {
|
|
return {
|
|
dialogOpen: dialog.open,
|
|
onDialogOpenChange: dialog.onOpenChange,
|
|
items: queriesResult,
|
|
addItems,
|
|
};
|
|
}, [ dialog.open, dialog.onOpenChange, queriesResult, addItems ]);
|
|
|
|
return (
|
|
<CsvExportContext.Provider value={ value }>
|
|
{ children }
|
|
</CsvExportContext.Provider>
|
|
);
|
|
}
|
|
|
|
export function useCsvExportContext() {
|
|
const context = React.useContext(CsvExportContext);
|
|
if (!context) {
|
|
throw new Error('useCsvExportContext must be used within a CsvExportContextProvider');
|
|
}
|
|
return context;
|
|
}
|