From 53f57296742d0c719c9bf735b94af1ce6137c106 Mon Sep 17 00:00:00 2001 From: Faisal Amir Date: Mon, 19 May 2025 11:15:37 +0700 Subject: [PATCH] chore: update hub progress download, added toaster --- web-app/src/containers/DownloadManegement.tsx | 137 ++++---- web-app/src/routes/hub.tsx | 302 +++++++++++------- 2 files changed, 259 insertions(+), 180 deletions(-) diff --git a/web-app/src/containers/DownloadManegement.tsx b/web-app/src/containers/DownloadManegement.tsx index 89cd0ddfe..5a04d2c86 100644 --- a/web-app/src/containers/DownloadManegement.tsx +++ b/web-app/src/containers/DownloadManegement.tsx @@ -10,10 +10,12 @@ import { abortDownload } from '@/services/models' import { getProviders } from '@/services/providers' import { DownloadEvent, DownloadState, events } from '@janhq/core' import { IconX } from '@tabler/icons-react' -import { useCallback, useEffect, useMemo } from 'react' +import { useCallback, useEffect, useMemo, useState } from 'react' +import { toast } from 'sonner' export function DownloadManagement() { const { setProviders } = useModelProvider() + const [isPopoverOpen, setIsPopoverOpen] = useState(false) const { downloads, updateProgress, removeDownload } = useDownloadStore() const downloadCount = useMemo( () => Object.keys(downloads).length, @@ -22,7 +24,7 @@ export function DownloadManagement() { const downloadProcesses = useMemo( () => Object.values(downloads).map((download) => ({ - id: download.id, + id: download.name, name: download.name, progress: download.progress, current: download.current, @@ -76,6 +78,10 @@ export function DownloadManagement() { console.debug('onFileDownloadSuccess', state) removeDownload(state.modelId) getProviders().then(setProviders) + toast.success('Download Complete', { + id: 'download-complete', + description: `The model ${state.modelId} has been downloaded`, + }) }, [removeDownload, setProviders] ) @@ -107,64 +113,77 @@ export function DownloadManagement() { } return ( - + <> {downloadCount > 0 && ( - -
-
- {downloadCount} -
-

Downloads

-
- - - {overallProgress.toFixed(2)}% - -
-
-
- )} - -
-
-

Downloading

-
-
- {downloadProcesses.map((download) => ( -
-
-

- {download.name} -

-
- {/* */} - abortDownload(download.name)} - /> -
-
- -

- {`${renderGB(download.current)} / ${renderGB(download.total)}`}{' '} - GB ({download.progress.toFixed(2)}%) -

+ + +
+
+ {downloadCount}
- ))} -
-
- - +

Downloads

+
+ + + {overallProgress.toFixed(2)}% + +
+
+ + + e.preventDefault} + > +
+
+

Downloading

+
+
+ {downloadProcesses.map((download) => ( +
+
+

+ {download.name} +

+
+ { + abortDownload(download.name).then(() => { + toast.info('Download Cancelled', { + id: 'cancel-download', + description: + 'The download process was cancelled', + }) + if (downloadProcesses.length === 0) { + setIsPopoverOpen(false) + } + }) + }} + /> +
+
+ +

+ {`${renderGB(download.current)} / ${renderGB(download.total)}`}{' '} + GB ({download.progress.toFixed(2)}%) +

+
+ ))} +
+
+
+ + )} + ) } diff --git a/web-app/src/routes/hub.tsx b/web-app/src/routes/hub.tsx index b5b004fe1..f99bf988f 100644 --- a/web-app/src/routes/hub.tsx +++ b/web-app/src/routes/hub.tsx @@ -16,6 +16,8 @@ import { DropdownMenuTrigger, } from '@/components/ui/dropdown-menu' import { downloadModel } from '@/services/models' +import { useDownloadStore } from '@/hooks/useDownloadStore' +import { Progress } from '@/components/ui/progress' // eslint-disable-next-line @typescript-eslint/no-explicit-any export const Route = createFileRoute(route.hub as any)({ @@ -77,6 +79,53 @@ function Hub() { setSearchValue(e.target.value) } + const { downloads } = useDownloadStore() + + const downloadProcesses = useMemo( + () => + Object.values(downloads).map((download) => ({ + id: download.name, + name: download.name, + progress: download.progress, + current: download.current, + total: download.total, + })), + [downloads] + ) + + interface ModelProps { + model: { + id: string + models: { + id: string + }[] + } + } + + const DownloadButtonPlaceholder = useMemo(() => { + return ({ model }: ModelProps) => { + const modelId = model.models[0]?.id + const isDownloading = downloadProcesses.some((e) => e.id === modelId) + const downloadProgress = + downloadProcesses.find((e) => e.id === modelId)?.progress || 0 + + return ( + <> + {isDownloading ? ( +
+ + + {Math.round(downloadProgress * 100)}% + +
+ ) : ( + + )} + + ) + } + }, [downloadProcesses]) + return (
@@ -134,131 +183,142 @@ function Hub() {
) : (
- {filteredModels.map((model) => { - return ( -
- - -

- {extractModelName(model.id) || ''} -

- -
- - {toGigabytes(model.models?.[0]?.size)} - - -
-
- } - > - -
- - By {model?.author} - -
-
- - - {model.metadata?.downloads || 0} - -
-
- - - {model.models?.length || 0} - -
- {model.models.length > 1 && ( -
- - toggleModelExpansion(model.id) - } - /> -

- Show variants -

-
- )} -
-
- {expandedModels[model.id] && - model.models.length > 0 && ( -
- {model.models.map((variant) => { - return ( - - {/* {defaultVariant && <>test} */} -

- {toGigabytes(variant.size)} -

-
- downloadModel(variant.id) - } - > - -
-
- } - /> - ) - })} +

+ Show variants +

)} - -
- ) - })} +
+ + {expandedModels[model.id] && model.models.length > 0 && ( +
+ {model.models.slice(1).map((variant) => ( + + {/* {defaultVariant && <>test} */} +

+ {toGigabytes(variant.size)} +

+ {(() => { + const isDownloading = + downloadProcesses.some( + (e) => e.id === variant.id + ) + const downloadProgress = + downloadProcesses.find( + (e) => e.id === variant.id + )?.progress || 0 + + return isDownloading ? ( + <> +
+ + + {Math.round(downloadProgress * 100)} + % + +
+ + ) : ( +
+ downloadModel(variant.id) + } + > + +
+ ) + })()} +
+ } + /> + ))} + + )} + + + ))} )}