From bde1261b8cfb11812951814b5799cbb39b63ae63 Mon Sep 17 00:00:00 2001 From: Yi Date: Tue, 5 Nov 2024 17:52:47 +0800 Subject: [PATCH] chore: update installFromGitHub component --- .../plugins/install-plugin/hooks.ts | 4 +- .../install-from-github/index.tsx | 208 ++++++++---------- .../install-from-github/steps/installed.tsx | 54 ----- .../install-from-github/steps/loaded.tsx | 82 +++++-- .../steps/selectPackage.tsx | 49 ++++- .../steps/uploading.tsx | 2 +- web/app/components/plugins/types.ts | 6 +- web/service/plugins.ts | 9 +- 8 files changed, 220 insertions(+), 194 deletions(-) delete mode 100644 web/app/components/plugins/install-plugin/install-from-github/steps/installed.tsx diff --git a/web/app/components/plugins/install-plugin/hooks.ts b/web/app/components/plugins/install-plugin/hooks.ts index 30db71e0f6..90388a06bf 100644 --- a/web/app/components/plugins/install-plugin/hooks.ts +++ b/web/app/components/plugins/install-plugin/hooks.ts @@ -49,7 +49,7 @@ export const useGitHubUpload = () => { repoUrl: string, selectedVersion: string, selectedPackage: string, - onSuccess?: (GitHubPackage: { manifest: any; uniqueIdentifier: string }) => void, + onSuccess?: (GitHubPackage: { manifest: any; unique_identifier: string }) => void, ) => { setIsLoading(true) setError(null) @@ -58,7 +58,7 @@ export const useGitHubUpload = () => { const response = await uploadGitHub(repoUrl, selectedVersion, selectedPackage) const GitHubPackage = { manifest: response.manifest, - uniqueIdentifier: response.plugin_unique_identifier, + unique_identifier: response.unique_identifier, } if (onSuccess) onSuccess(GitHubPackage) return GitHubPackage diff --git a/web/app/components/plugins/install-plugin/install-from-github/index.tsx b/web/app/components/plugins/install-plugin/install-from-github/index.tsx index 16cd5bc2f8..15c0f1f800 100644 --- a/web/app/components/plugins/install-plugin/install-from-github/index.tsx +++ b/web/app/components/plugins/install-plugin/install-from-github/index.tsx @@ -4,21 +4,20 @@ import React, { useCallback, useState } from 'react' import Modal from '@/app/components/base/modal' import type { Item } from '@/app/components/base/select' import type { InstallState } from '@/app/components/plugins/types' -import { useGitHubReleases, useGitHubUpload } from '../hooks' +import { useGitHubReleases } from '../hooks' import { parseGitHubUrl } from '../utils' import type { PluginDeclaration, UpdateFromGitHubPayload } from '../../types' import { InstallStepFromGitHub } from '../../types' -import checkTaskStatus from '../base/check-task-status' -import { usePluginTasksStore } from '@/app/components/plugins/plugin-page/store' import Toast from '@/app/components/base/toast' import SetURL from './steps/setURL' import SelectPackage from './steps/selectPackage' -import Installed from './steps/installed' +import Installed from '../base/installed' import Loaded from './steps/loaded' import useGetIcon from '@/app/components/plugins/install-plugin/base/use-get-icon' import { useTranslation } from 'react-i18next' import { usePluginPageContext } from '../../plugin-page/context' -import { installPackageFromGitHub } from '@/service/plugins' + +const i18nPrefix = 'plugin.installModal' type InstallFromGitHubProps = { updatePayload?: UpdateFromGitHubPayload @@ -30,17 +29,15 @@ const InstallFromGitHub: React.FC = ({ updatePayload, on const { t } = useTranslation() const [state, setState] = useState({ step: updatePayload ? InstallStepFromGitHub.selectPackage : InstallStepFromGitHub.setUrl, - repoUrl: updatePayload?.url || '', - selectedVersion: updatePayload?.currVersion || '', - selectedPackage: updatePayload?.currPackage || '', + repoUrl: updatePayload?.originalPackageInfo.repo || '', + selectedVersion: updatePayload?.originalPackageInfo.version || '', + selectedPackage: updatePayload?.originalPackageInfo.package || '', releases: [], }) const { getIconUrl } = useGetIcon() const [uniqueIdentifier, setUniqueIdentifier] = useState(null) const [manifest, setManifest] = useState(null) const [errorMsg, setErrorMsg] = useState(null) - const setPluginTasksWithPolling = usePluginTasksStore(s => s.setPluginTasksWithPolling) - const { check } = checkTaskStatus() const mutateInstalledPluginList = usePluginPageContext(v => v.mutateInstalledPluginList) const versions: Item[] = state.releases.map(release => ({ @@ -58,13 +55,39 @@ const InstallFromGitHub: React.FC = ({ updatePayload, on })) || []) : [] - const { isLoading, handleUpload, error } = useGitHubUpload() const { fetchReleases } = useGitHubReleases() - const handleError = (e: any) => { - const message = e?.response?.message || t('plugin.error.installFailed') + const getTitle = useCallback(() => { + if (state.step === InstallStepFromGitHub.installed) + return t(`${i18nPrefix}.installedSuccessfully`) + if (state.step === InstallStepFromGitHub.installFailed) + return t(`${i18nPrefix}.installFailed`) + + return t(`${i18nPrefix}.installPlugin`) + }, [state.step]) + + const handleUrlSubmit = async () => { + const { isValid, owner, repo } = parseGitHubUrl(state.repoUrl) + if (!isValid || !owner || !repo) { + Toast.notify({ + type: 'error', + message: t('plugin.error.inValidGitHubUrl'), + }) + return + } + await fetchReleases(owner, repo).then((fetchedReleases) => { + setState(prevState => ({ + ...prevState, + releases: fetchedReleases, + step: InstallStepFromGitHub.selectPackage, + })) + }) + } + + const handleError = (e: any, isInstall: boolean) => { + const message = e?.response?.message || t('plugin.installModal.installFailedDesc') setErrorMsg(message) - setState(prevState => ({ ...prevState, step: InstallStepFromGitHub.failed })) + setState(prevState => ({ ...prevState, step: isInstall ? InstallStepFromGitHub.installFailed : InstallStepFromGitHub.uploadFailed })) } const handleUploaded = async (GitHubPackage: any) => { @@ -78,72 +101,25 @@ const InstallFromGitHub: React.FC = ({ updatePayload, on setState(prevState => ({ ...prevState, step: InstallStepFromGitHub.readyToInstall })) } catch (e) { - handleError(e) + handleError(e, false) } } - const handleInstall = async () => { - try { - const { all_installed: isInstalled, task_id: taskId } = await installPackageFromGitHub(uniqueIdentifier!) - - if (isInstalled) { - setState(prevState => ({ ...prevState, step: InstallStepFromGitHub.installed })) - return - } - - setPluginTasksWithPolling() - await check({ - taskId, - pluginUniqueIdentifier: uniqueIdentifier!, - }) - setState(prevState => ({ ...prevState, step: InstallStepFromGitHub.installed })) - } - catch (e) { - handleError(e) - } - } + const handleUploadFail = useCallback((errorMsg: string) => { + setErrorMsg(errorMsg) + setState(prevState => ({ ...prevState, step: InstallStepFromGitHub.uploadFailed })) + }, []) const handleInstalled = useCallback(() => { mutateInstalledPluginList() setState(prevState => ({ ...prevState, step: InstallStepFromGitHub.installed })) }, [mutateInstalledPluginList]) - const handleNext = async () => { - switch (state.step) { - case InstallStepFromGitHub.setUrl: { - const { isValid, owner, repo } = parseGitHubUrl(state.repoUrl) - if (!isValid || !owner || !repo) { - Toast.notify({ - type: 'error', - message: t('plugin.error.inValidGitHubUrl'), - }) - break - } - const fetchedReleases = await fetchReleases(owner, repo) - setState(prevState => ({ - ...prevState, - releases: fetchedReleases, - step: InstallStepFromGitHub.selectPackage, - })) - break - } - case InstallStepFromGitHub.selectPackage: { - const repo = state.repoUrl.replace('https://github.com/', '') - if (error) - handleError(error) - - else - await handleUpload(repo, state.selectedVersion, state.selectedPackage, handleUploaded) - - break - } - case InstallStepFromGitHub.readyToInstall: - await handleInstall() - break - case InstallStepFromGitHub.installed: - break - } - } + const handleFailed = useCallback((errorMsg?: string) => { + setState(prevState => ({ ...prevState, step: InstallStepFromGitHub.installFailed })) + if (errorMsg) + setErrorMsg(errorMsg) + }, []) const handleBack = () => { setState((prevState) => { @@ -157,63 +133,69 @@ const InstallFromGitHub: React.FC = ({ updatePayload, on } }) } + return (
- {t('plugin.installFromGitHub.installPlugin')} + {getTitle()}
- {state.step !== InstallStepFromGitHub.installed && t('plugin.installFromGitHub.installNote')} + {!([InstallStepFromGitHub.uploadFailed, InstallStepFromGitHub.installed, InstallStepFromGitHub.installFailed].includes(state.step)) && t('plugin.installFromGitHub.installNote')}
-
- {state.step === InstallStepFromGitHub.setUrl && ( - setState(prevState => ({ ...prevState, repoUrl: value }))} - onNext={handleNext} - onCancel={onClose} - /> - )} - {state.step === InstallStepFromGitHub.selectPackage && ( - setState(prevState => ({ ...prevState, selectedVersion: item.value as string }))} - selectedPackage={state.selectedPackage} - packages={packages} - onSelectPackage={item => setState(prevState => ({ ...prevState, selectedPackage: item.value as string }))} - onNext={handleNext} - onBack={handleBack} - /> - )} - {state.step === InstallStepFromGitHub.readyToInstall && ( - - )} - {state.step === InstallStepFromGitHub.installed && ( - - )} -
+ {([InstallStepFromGitHub.uploadFailed, InstallStepFromGitHub.installed, InstallStepFromGitHub.installFailed].includes(state.step)) + ? + :
+ {state.step === InstallStepFromGitHub.setUrl && ( + setState(prevState => ({ ...prevState, repoUrl: value }))} + onNext={handleUrlSubmit} + onCancel={onClose} + /> + )} + {state.step === InstallStepFromGitHub.selectPackage && ( + setState(prevState => ({ ...prevState, selectedVersion: item.value as string }))} + selectedPackage={state.selectedPackage} + packages={packages} + onSelectPackage={item => setState(prevState => ({ ...prevState, selectedPackage: item.value as string }))} + onUploaded={handleUploaded} + onFailed={handleUploadFail} + onBack={handleBack} + /> + )} + {state.step === InstallStepFromGitHub.readyToInstall && ( + + )} +
}
) } diff --git a/web/app/components/plugins/install-plugin/install-from-github/steps/installed.tsx b/web/app/components/plugins/install-plugin/install-from-github/steps/installed.tsx deleted file mode 100644 index 97be133451..0000000000 --- a/web/app/components/plugins/install-plugin/install-from-github/steps/installed.tsx +++ /dev/null @@ -1,54 +0,0 @@ -import React from 'react' -import Button from '@/app/components/base/button' -import { useTranslation } from 'react-i18next' - -type InstalledProps = { - repoUrl: string - selectedVersion: string - selectedPackage: string - onClose: () => void -} - -const InfoRow = ({ label, value }: { label: string; value: string }) => ( -
-
-
- {label} -
-
-
-
- {value} -
-
-
-) - -const Installed: React.FC = ({ repoUrl, selectedVersion, selectedPackage, onClose }) => { - const { t } = useTranslation() - return ( - <> -
The plugin has been installed successfully.
-
- {[ - { label: t('plugin.installModal.labels.repository'), value: repoUrl }, - { label: t('plugin.installModal.labels.version'), value: selectedVersion }, - { label: t('plugin.installModal.labels.package'), value: selectedPackage }, - ].map(({ label, value }) => ( - - ))} -
-
- -
- - ) -} - -export default Installed diff --git a/web/app/components/plugins/install-plugin/install-from-github/steps/loaded.tsx b/web/app/components/plugins/install-plugin/install-from-github/steps/loaded.tsx index 42d847c584..7b67ef441b 100644 --- a/web/app/components/plugins/install-plugin/install-from-github/steps/loaded.tsx +++ b/web/app/components/plugins/install-plugin/install-from-github/steps/loaded.tsx @@ -7,18 +7,73 @@ import Card from '../../../card' import Badge, { BadgeState } from '@/app/components/base/badge/index' import { pluginManifestToCardPluginProps } from '../../utils' import { useTranslation } from 'react-i18next' +import { installPackageFromGitHub } from '@/service/plugins' +import { RiLoader2Line } from '@remixicon/react' +import { usePluginTasksStore } from '@/app/components/plugins/plugin-page/store' +import checkTaskStatus from '../../base/check-task-status' +import { parseGitHubUrl } from '../../utils' type LoadedProps = { - isLoading: boolean + uniqueIdentifier: string payload: PluginDeclaration + repoUrl: string + selectedVersion: string + selectedPackage: string onBack: () => void - onInstall: () => void + onInstalled: () => void + onFailed: (message?: string) => void } const i18nPrefix = 'plugin.installModal' -const Loaded: React.FC = ({ isLoading, payload, onBack, onInstall }) => { +const Loaded: React.FC = ({ + uniqueIdentifier, + payload, + repoUrl, + selectedVersion, + selectedPackage, + onBack, + onInstalled, + onFailed, +}) => { const { t } = useTranslation() + const [isInstalling, setIsInstalling] = React.useState(false) + const setPluginTasksWithPolling = usePluginTasksStore(s => s.setPluginTasksWithPolling) + const { check } = checkTaskStatus() + + const handleInstall = async () => { + if (isInstalling) return + setIsInstalling(true) + + try { + const { owner, repo } = parseGitHubUrl(repoUrl) + const { all_installed: isInstalled, task_id: taskId } = await installPackageFromGitHub( + `${owner}/${repo}`, + selectedVersion, + selectedPackage, + uniqueIdentifier, + ) + + if (isInstalled) { + onInstalled() + return + } + + setPluginTasksWithPolling() + await check({ + taskId, + pluginUniqueIdentifier: uniqueIdentifier, + }) + onInstalled() + } + catch (e) { + onFailed(e instanceof Error ? e.message : String(e)) + } + finally { + setIsInstalling(false) + } + } + return ( <>
@@ -32,20 +87,19 @@ const Loaded: React.FC = ({ isLoading, payload, onBack, onInstall } />
- + {!isInstalling && ( + + )}
diff --git a/web/app/components/plugins/install-plugin/install-from-github/steps/selectPackage.tsx b/web/app/components/plugins/install-plugin/install-from-github/steps/selectPackage.tsx index 17decf687b..b6758002cd 100644 --- a/web/app/components/plugins/install-plugin/install-from-github/steps/selectPackage.tsx +++ b/web/app/components/plugins/install-plugin/install-from-github/steps/selectPackage.tsx @@ -4,34 +4,70 @@ import React from 'react' import type { Item } from '@/app/components/base/select' import { PortalSelect } from '@/app/components/base/select' import Button from '@/app/components/base/button' -import type { UpdatePluginPayload } from '../../../types' +import type { PluginDeclaration, UpdateFromGitHubPayload } from '../../../types' import { useTranslation } from 'react-i18next' +import { useGitHubUpload } from '../../hooks' type SelectPackageProps = { - updatePayload: UpdatePluginPayload + updatePayload: UpdateFromGitHubPayload + repoUrl: string selectedVersion: string versions: Item[] onSelectVersion: (item: Item) => void selectedPackage: string packages: Item[] onSelectPackage: (item: Item) => void - onNext: () => void + onUploaded: (result: { + uniqueIdentifier: string + manifest: PluginDeclaration + }) => void + onFailed: (errorMsg: string) => void onBack: () => void } const SelectPackage: React.FC = ({ updatePayload, + repoUrl, selectedVersion, versions, onSelectVersion, selectedPackage, packages, onSelectPackage, - onNext, + onUploaded, + onFailed, onBack, }) => { const { t } = useTranslation() const isEdit = Boolean(updatePayload) + const [isUploading, setIsUploading] = React.useState(false) + const { handleUpload, error } = useGitHubUpload() + + const handleUploadPackage = async () => { + if (isUploading) return + setIsUploading(true) + + try { + const repo = repoUrl.replace('https://github.com/', '') + await handleUpload(repo, selectedVersion, selectedPackage, (GitHubPackage) => { + onUploaded({ + uniqueIdentifier: GitHubPackage.unique_identifier, + manifest: GitHubPackage.manifest, + }) + }) + } + catch (e: any) { + if (e.response?.message) + onFailed(e.response?.message) + + else + onFailed(t('plugin.error.uploadFailed')) + } + finally { + setIsUploading(false) + } + } + return ( <>