From 11ed86f2a8072ef4affdaefac8bccad10fa59f57 Mon Sep 17 00:00:00 2001 From: Joel Date: Thu, 21 Nov 2024 14:24:43 +0800 Subject: [PATCH] feat: download package --- .../market-place-plugin/action.tsx | 27 +++++++++++++++++-- .../market-place-plugin/item.tsx | 1 + web/service/fetch.ts | 3 ++- web/service/use-plugins.ts | 9 +++++++ web/utils/format.ts | 11 ++++++++ 5 files changed, 48 insertions(+), 3 deletions(-) diff --git a/web/app/components/workflow/block-selector/market-place-plugin/action.tsx b/web/app/components/workflow/block-selector/market-place-plugin/action.tsx index 6f0c08eeca..f4a9668c3e 100644 --- a/web/app/components/workflow/block-selector/market-place-plugin/action.tsx +++ b/web/app/components/workflow/block-selector/market-place-plugin/action.tsx @@ -1,6 +1,6 @@ 'use client' import type { FC } from 'react' -import React, { useCallback, useRef } from 'react' +import React, { useCallback, useEffect, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import { RiMoreFill } from '@remixicon/react' import ActionButton from '@/app/components/base/action-button' @@ -12,12 +12,15 @@ import { } from '@/app/components/base/portal-to-follow-elem' import cn from '@/utils/classnames' import { MARKETPLACE_URL_PREFIX } from '@/config' +import { useDownloadPlugin } from '@/service/use-plugins' +import { downloadFile } from '@/utils/format' type Props = { open: boolean onOpenChange: (v: boolean) => void author: string name: string + version: string } const OperationDropdown: FC = ({ @@ -25,6 +28,7 @@ const OperationDropdown: FC = ({ onOpenChange, author, name, + version, }) => { const { t } = useTranslation() const openRef = useRef(open) @@ -37,6 +41,25 @@ const OperationDropdown: FC = ({ setOpen(!openRef.current) }, [setOpen]) + const [needDownload, setNeedDownload] = useState(false) + const { data: blob, isLoading } = useDownloadPlugin({ + organization: author, + pluginName: name, + version, + }, needDownload) + const handleDownload = useCallback(() => { + if (isLoading) return + setNeedDownload(true) + }, [isLoading]) + + useEffect(() => { + if (blob) { + const fileName = `${author}-${name}_${version}.zip` + downloadFile({ data: blob, fileName }) + setNeedDownload(false) + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [blob]) return ( = ({
-
{t('common.operation.download')}
+
{t('common.operation.download')}
{t('common.operation.viewDetails')}
diff --git a/web/app/components/workflow/block-selector/market-place-plugin/item.tsx b/web/app/components/workflow/block-selector/market-place-plugin/item.tsx index 7f2ae34083..ebe4da73f8 100644 --- a/web/app/components/workflow/block-selector/market-place-plugin/item.tsx +++ b/web/app/components/workflow/block-selector/market-place-plugin/item.tsx @@ -59,6 +59,7 @@ const Item: FC = ({ onOpenChange={setOpen} author={payload.org} name={payload.name} + version={payload.latest_version} /> {isShowInstallModal && ( diff --git a/web/service/fetch.ts b/web/service/fetch.ts index 666a3e2336..39f91f903f 100644 --- a/web/service/fetch.ts +++ b/web/service/fetch.ts @@ -12,6 +12,7 @@ export const ContentType = { audio: 'audio/mpeg', form: 'application/x-www-form-urlencoded; charset=UTF-8', download: 'application/octet-stream', // for download + downloadZip: 'application/zip', // for download upload: 'multipart/form-data', // for upload } @@ -193,7 +194,7 @@ async function base(url: string, options: FetchOptionType = {}, otherOptions: const contentType = res.headers.get('content-type') if ( contentType - && [ContentType.download, ContentType.audio].includes(contentType) + && [ContentType.download, ContentType.audio, ContentType.downloadZip].includes(contentType) ) return await res.blob() as T diff --git a/web/service/use-plugins.ts b/web/service/use-plugins.ts index 0c05290e0f..78c4ff770f 100644 --- a/web/service/use-plugins.ts +++ b/web/service/use-plugins.ts @@ -344,3 +344,12 @@ export const useMutationCheckDependenciesBeforeImportDSL = () => { return mutation } + +export const useDownloadPlugin = (info: { organization: string; pluginName: string; version: string }, needDownload: boolean) => { + return useQuery({ + queryKey: [NAME_SPACE, 'downloadPlugin', info], + queryFn: () => getMarketplace(`/plugins/${info.organization}/${info.pluginName}/${info.version}/download`), + enabled: needDownload, + retry: 0, + }) +} diff --git a/web/utils/format.ts b/web/utils/format.ts index 1eeb6af807..45f0e51878 100644 --- a/web/utils/format.ts +++ b/web/utils/format.ts @@ -34,3 +34,14 @@ export const formatTime = (num: number) => { } return `${num.toFixed(2)} ${units[index]}` } + +export const downloadFile = ({ data, fileName }: { data: Blob; fileName: string }) => { + const url = window.URL.createObjectURL(data) + const a = document.createElement('a') + a.href = url + a.download = fileName + document.body.appendChild(a) + a.click() + a.remove() + window.URL.revokeObjectURL(url) +}