@@ -104,6 +104,10 @@ export function UserWalletsTable(
104104 completed : 0 ,
105105 total : 0 ,
106106 } ) ;
107+ const [ csvExportProgress , setCsvExportProgress ] = useState < {
108+ fetchedWallets : number ;
109+ totalWallets ?: number ;
110+ } > ( ) ;
107111
108112 const walletsQuery = useEmbeddedWallets ( {
109113 authToken : props . authToken ,
@@ -113,10 +117,18 @@ export function UserWalletsTable(
113117 page : activePage ,
114118 } ) ;
115119 const wallets = walletsQuery ?. data ?. users || [ ] ;
116- const { mutateAsync : getAllEmbeddedWallets , isPending : isLoadingAllWallets } =
117- useAllEmbeddedWallets ( {
118- authToken : props . authToken ,
119- } ) ;
120+ const {
121+ mutateAsync : getAllEmbeddedWalletsForBalances ,
122+ isPending : isLoadingWalletsForBalances ,
123+ } = useAllEmbeddedWallets ( {
124+ authToken : props . authToken ,
125+ } ) ;
126+ const {
127+ mutateAsync : getAllEmbeddedWalletsForCsv ,
128+ isPending : isLoadingWalletsForCsv ,
129+ } = useAllEmbeddedWallets ( {
130+ authToken : props . authToken ,
131+ } ) ;
120132
121133 const fetchPortfoliosMutation = useFetchAllPortfolios ( ) ;
122134
@@ -125,7 +137,7 @@ export function UserWalletsTable(
125137
126138 try {
127139 // First get all wallets
128- const allWallets = await getAllEmbeddedWallets ( {
140+ const allWallets = await getAllEmbeddedWalletsForBalances ( {
129141 clientId : props . projectClientId ,
130142 ecosystemSlug : props . ecosystemSlug ,
131143 teamId : props . teamId ,
@@ -161,7 +173,7 @@ export function UserWalletsTable(
161173 }
162174 } , [
163175 selectedChains ,
164- getAllEmbeddedWallets ,
176+ getAllEmbeddedWalletsForBalances ,
165177 props . projectClientId ,
166178 props . ecosystemSlug ,
167179 props . teamId ,
@@ -170,7 +182,32 @@ export function UserWalletsTable(
170182 ] ) ;
171183
172184 const isFetchingBalances =
173- isLoadingAllWallets || fetchPortfoliosMutation . isPending ;
185+ isLoadingWalletsForBalances || fetchPortfoliosMutation . isPending ;
186+
187+ const csvExportLabel = useMemo ( ( ) => {
188+ if ( ! isLoadingWalletsForCsv ) {
189+ return "Download as .csv" ;
190+ }
191+
192+ const fetchedWallets = csvExportProgress ?. fetchedWallets ?? 0 ;
193+ const totalWallets = csvExportProgress ?. totalWallets ;
194+
195+ if ( fetchedWallets === 0 ) {
196+ return "Preparing export..." ;
197+ }
198+
199+ if ( totalWallets && totalWallets > 0 ) {
200+ const cappedFetchedWallets = Math . min ( fetchedWallets , totalWallets ) ;
201+ const percentage = Math . min (
202+ 100 ,
203+ Math . round ( ( cappedFetchedWallets / totalWallets ) * 100 ) ,
204+ ) ;
205+
206+ return `Exporting ${ percentage } % (${ cappedFetchedWallets . toLocaleString ( ) } /${ totalWallets . toLocaleString ( ) } )` ;
207+ }
208+
209+ return `Exporting ${ fetchedWallets . toLocaleString ( ) } users...` ;
210+ } , [ csvExportProgress , isLoadingWalletsForCsv ] ) ;
174211
175212 const aggregatedStats = useMemo ( ( ) => {
176213 let fundedWallets = 0 ;
@@ -414,41 +451,48 @@ export function UserWalletsTable(
414451 } ;
415452
416453 const downloadCSV = useCallback ( async ( ) => {
417- if ( wallets . length === 0 || ! getAllEmbeddedWallets ) {
454+ if ( wallets . length === 0 ) {
418455 return ;
419456 }
420- const usersWallets = await getAllEmbeddedWallets ( {
421- clientId : props . projectClientId ,
422- ecosystemSlug : props . ecosystemSlug ,
423- teamId : props . teamId ,
424- } ) ;
425- const csv = Papa . unparse (
426- usersWallets . map ( ( row ) => {
427- const email = getPrimaryEmail ( row . linkedAccounts ) ;
428-
429- return {
430- address : row . wallets [ 0 ] ?. address || "Uninitialized" ,
431- created : row . wallets [ 0 ] ?. createdAt
432- ? new Date ( row . wallets [ 0 ] . createdAt ) . toISOString ( )
433- : "Wallet not created yet" ,
434- email : email || "N/A" ,
435- login_methods : row . linkedAccounts . map ( ( acc ) => acc . type ) . join ( ", " ) ,
436- auth_identifier : getAuthIdentifier ( row . linkedAccounts ) || "N/A" ,
437- user_identifier : row . id || "N/A" ,
438- } ;
439- } ) ,
440- ) ;
441- const csvUrl = URL . createObjectURL (
442- new Blob ( [ csv ] , { type : "text/csv;charset=utf-8;" } ) ,
443- ) ;
444- const tempLink = document . createElement ( "a" ) ;
445- tempLink . href = csvUrl ;
446- tempLink . setAttribute ( "download" , "download.csv" ) ;
447- tempLink . click ( ) ;
457+ setCsvExportProgress ( { fetchedWallets : 0 } ) ;
458+
459+ try {
460+ const usersWallets = await getAllEmbeddedWalletsForCsv ( {
461+ clientId : props . projectClientId ,
462+ ecosystemSlug : props . ecosystemSlug ,
463+ teamId : props . teamId ,
464+ onProgress : setCsvExportProgress ,
465+ } ) ;
466+ const csv = Papa . unparse (
467+ usersWallets . map ( ( row ) => {
468+ const email = getPrimaryEmail ( row . linkedAccounts ) ;
469+
470+ return {
471+ address : row . wallets [ 0 ] ?. address || "Uninitialized" ,
472+ created : row . wallets [ 0 ] ?. createdAt
473+ ? new Date ( row . wallets [ 0 ] . createdAt ) . toISOString ( )
474+ : "Wallet not created yet" ,
475+ email : email || "N/A" ,
476+ login_methods : row . linkedAccounts . map ( ( acc ) => acc . type ) . join ( ", " ) ,
477+ auth_identifier : getAuthIdentifier ( row . linkedAccounts ) || "N/A" ,
478+ user_identifier : row . id || "N/A" ,
479+ } ;
480+ } ) ,
481+ ) ;
482+ const csvUrl = URL . createObjectURL (
483+ new Blob ( [ csv ] , { type : "text/csv;charset=utf-8;" } ) ,
484+ ) ;
485+ const tempLink = document . createElement ( "a" ) ;
486+ tempLink . href = csvUrl ;
487+ tempLink . setAttribute ( "download" , "download.csv" ) ;
488+ tempLink . click ( ) ;
489+ } finally {
490+ setCsvExportProgress ( undefined ) ;
491+ }
448492 } , [
449493 wallets ,
450494 props . projectClientId ,
451- getAllEmbeddedWallets ,
495+ getAllEmbeddedWalletsForCsv ,
452496 props . teamId ,
453497 props . ecosystemSlug ,
454498 ] ) ;
@@ -481,12 +525,12 @@ export function UserWalletsTable(
481525 </ div >
482526 < Button
483527 className = "gap-2 bg-background rounded-full"
484- disabled = { wallets . length === 0 || isLoadingAllWallets }
528+ disabled = { wallets . length === 0 || isLoadingWalletsForCsv }
485529 onClick = { downloadCSV }
486530 variant = "outline"
487531 >
488- { isLoadingAllWallets && < Spinner className = "size-4" /> }
489- Download as .csv
532+ { isLoadingWalletsForCsv && < Spinner className = "size-4" /> }
533+ { csvExportLabel }
490534 </ Button >
491535 </ div >
492536 </ div >
0 commit comments