diff --git a/web/app/components/plugins/plugin-detail-panel/detail-header.tsx b/web/app/components/plugins/plugin-detail-panel/detail-header.tsx index ad9760feff..b40f264967 100644 --- a/web/app/components/plugins/plugin-detail-panel/detail-header.tsx +++ b/web/app/components/plugins/plugin-detail-panel/detail-header.tsx @@ -13,6 +13,8 @@ import Description from '../card/base/description' import Icon from '../card/base/card-icon' import Title from '../card/base/title' import OrgInfo from '../card/base/org-info' +import { useGitHubReleases } from '../install-plugin/hooks' +import { compareVersion, getLatestVersion } from '@/utils/semver' import OperationDropdown from './operation-dropdown' import PluginInfo from '@/app/components/plugins/plugin-page/plugin-info' import ActionButton from '@/app/components/base/action-button' @@ -20,10 +22,13 @@ import Button from '@/app/components/base/button' import Badge from '@/app/components/base/badge' import Confirm from '@/app/components/base/confirm' import Tooltip from '@/app/components/base/tooltip' +import Toast from '@/app/components/base/toast' import { BoxSparkleFill } from '@/app/components/base/icons/src/vender/plugin' import { Github } from '@/app/components/base/icons/src/public/common' import { uninstallPlugin } from '@/service/plugins' import { useGetLanguage } from '@/context/i18n' +import { useModalContext } from '@/context/modal-context' + import { API_PREFIX, MARKETPLACE_URL_PREFIX } from '@/config' import cn from '@/utils/classnames' @@ -32,16 +37,18 @@ const i18nPrefix = 'plugin.action' type Props = { detail: PluginDetail onHide: () => void - onDelete: () => void + onUpdate: () => void } const DetailHeader = ({ detail, onHide, - onDelete, + onUpdate, }: Props) => { const { t } = useTranslation() const locale = useGetLanguage() + const { fetchReleases } = useGitHubReleases() + const { setShowUpdatePluginModal } = useModalContext() const { installation_id, @@ -53,13 +60,51 @@ const DetailHeader = ({ } = detail const { author, name, label, description, icon, verified } = detail.declaration const isFromGitHub = source === PluginSource.github - // Only plugin installed from GitHub need to check if it's the new version + const hasNewVersion = useMemo(() => { return source === PluginSource.github && latest_version !== version }, [source, latest_version, version]) - // #plugin TODO# update plugin - const handleUpdate = () => { } + const handleUpdate = async () => { + try { + const fetchedReleases = await fetchReleases(author, name) + if (fetchedReleases.length === 0) + return + const versions = fetchedReleases.map(release => release.tag_name) + const latestVersion = getLatestVersion(versions) + if (compareVersion(latestVersion, version) === 1) { + setShowUpdatePluginModal({ + onSaveCallback: () => { + onUpdate() + }, + payload: { + type: PluginSource.github, + github: { + originalPackageInfo: { + id: installation_id, + repo: meta!.repo, + version: meta!.version, + package: meta!.package, + releases: fetchedReleases, + }, + }, + }, + }) + } + else { + Toast.notify({ + type: 'info', + message: 'No new version available', + }) + } + } + catch { + Toast.notify({ + type: 'error', + message: 'Failed to compare versions', + }) + } + } const [isShowPluginInfo, { setTrue: showPluginInfo, @@ -82,9 +127,9 @@ const DetailHeader = ({ hideDeleting() if (res.success) { hideDeleteConfirm() - onDelete() + onUpdate() } - }, [hideDeleteConfirm, hideDeleting, installation_id, showDeleting, onDelete]) + }, [hideDeleteConfirm, hideDeleting, installation_id, showDeleting, onUpdate]) // #plugin TODO# used in apps // const usedInApps = 3 @@ -141,6 +186,7 @@ const DetailHeader = ({
void + onUpdate: () => void } const PluginDetailPanel: FC = ({ - onDelete, + onUpdate, }) => { const pluginDetail = usePluginPageContext(v => v.currentPluginDetail) const setCurrentPluginDetail = usePluginPageContext(v => v.setCurrentPluginDetail) @@ -39,7 +39,7 @@ const PluginDetailPanel: FC = ({
{!!pluginDetail.declaration.tool && } diff --git a/web/app/components/plugins/plugin-detail-panel/operation-dropdown.tsx b/web/app/components/plugins/plugin-detail-panel/operation-dropdown.tsx index b23b29d462..05988c181d 100644 --- a/web/app/components/plugins/plugin-detail-panel/operation-dropdown.tsx +++ b/web/app/components/plugins/plugin-detail-panel/operation-dropdown.tsx @@ -2,6 +2,7 @@ import type { FC } from 'react' import React, { useCallback, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' +import { PluginSource } from '../types' import { RiArrowRightUpLine, RiMoreFill } from '@remixicon/react' import ActionButton from '@/app/components/base/action-button' // import Button from '@/app/components/base/button' @@ -13,6 +14,7 @@ import { import cn from '@/utils/classnames' type Props = { + source: PluginSource onInfo: () => void onCheckVersion: () => void onRemove: () => void @@ -20,10 +22,11 @@ type Props = { } const OperationDropdown: FC = ({ + source, + detailUrl, onInfo, onCheckVersion, onRemove, - detailUrl, }) => { const { t } = useTranslation() const [open, doSetOpen] = useState(false) @@ -56,25 +59,33 @@ const OperationDropdown: FC = ({
-
{ - onInfo() - handleTrigger() - }} - className='px-3 py-1.5 rounded-lg text-text-secondary system-md-regular cursor-pointer hover:bg-state-base-hover' - >{t('plugin.detailPanel.operation.info')}
-
{ - onCheckVersion() - handleTrigger() - }} - className='px-3 py-1.5 rounded-lg text-text-secondary system-md-regular cursor-pointer hover:bg-state-base-hover' - >{t('plugin.detailPanel.operation.checkUpdate')}
- - {t('plugin.detailPanel.operation.viewDetail')} - - -
+ {source === PluginSource.github && ( +
{ + onInfo() + handleTrigger() + }} + className='px-3 py-1.5 rounded-lg text-text-secondary system-md-regular cursor-pointer hover:bg-state-base-hover' + >{t('plugin.detailPanel.operation.info')}
+ )} + {source === PluginSource.github && ( +
{ + onCheckVersion() + handleTrigger() + }} + className='px-3 py-1.5 rounded-lg text-text-secondary system-md-regular cursor-pointer hover:bg-state-base-hover' + >{t('plugin.detailPanel.operation.checkUpdate')}
+ )} + {source === PluginSource.marketplace && ( + + {t('plugin.detailPanel.operation.viewDetail')} + + + )} + {(source === PluginSource.marketplace || source === PluginSource.github) && ( +
+ )}
{ onRemove() diff --git a/web/app/components/plugins/plugin-page/plugins-panel.tsx b/web/app/components/plugins/plugin-page/plugins-panel.tsx index 466df72066..198b418f7c 100644 --- a/web/app/components/plugins/plugin-page/plugins-panel.tsx +++ b/web/app/components/plugins/plugin-page/plugins-panel.tsx @@ -48,7 +48,7 @@ const PluginsPanel = () => { ) : ( )} - invalidateInstalledPluginList()}/> + invalidateInstalledPluginList()}/> ) }