mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-06-10 15:56:03 +00:00
feat(plugins): add plugin new version detection (#1865)
* feat(plugins): 添加插件更新检测功能 * perf: card style --------- Co-authored-by: Junyan Qin <rockchinq@gmail.com>
This commit is contained in:
@@ -13,6 +13,7 @@ export interface IPluginCardVO {
|
||||
status: string;
|
||||
components: PluginComponent[];
|
||||
debug: boolean;
|
||||
hasUpdate?: boolean;
|
||||
}
|
||||
|
||||
export class PluginCardVO implements IPluginCardVO {
|
||||
@@ -28,6 +29,7 @@ export class PluginCardVO implements IPluginCardVO {
|
||||
install_info: Record<string, any>; // eslint-disable-line @typescript-eslint/no-explicit-any
|
||||
status: string;
|
||||
components: PluginComponent[];
|
||||
hasUpdate?: boolean;
|
||||
|
||||
constructor(prop: IPluginCardVO) {
|
||||
this.author = prop.author;
|
||||
@@ -42,5 +44,6 @@ export class PluginCardVO implements IPluginCardVO {
|
||||
this.debug = prop.debug;
|
||||
this.install_source = prop.install_source;
|
||||
this.install_info = prop.install_info;
|
||||
this.hasUpdate = prop.hasUpdate;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@ import PluginForm from '@/app/home/plugins/components/plugin-installed/plugin-fo
|
||||
import PluginReadme from '@/app/home/plugins/components/plugin-installed/plugin-readme/PluginReadme';
|
||||
import styles from '@/app/home/plugins/plugins.module.css';
|
||||
import { httpClient } from '@/app/infra/http/HttpClient';
|
||||
import { getCloudServiceClientSync } from '@/app/infra/http';
|
||||
import { isNewerVersion } from '@/app/utils/versionCompare';
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
@@ -72,10 +74,68 @@ const PluginInstalledComponent = forwardRef<PluginInstalledComponentRef>(
|
||||
getPluginList();
|
||||
}
|
||||
|
||||
function getPluginList() {
|
||||
httpClient.getPlugins().then((value) => {
|
||||
async function getPluginList() {
|
||||
try {
|
||||
// 获取已安装插件列表
|
||||
const installedPluginsResp = await httpClient.getPlugins();
|
||||
const installedPlugins = installedPluginsResp.plugins;
|
||||
|
||||
// 获取市场插件列表
|
||||
const client = getCloudServiceClientSync();
|
||||
const marketplaceResp = await client.getMarketplacePlugins(1, 100);
|
||||
const marketplacePlugins = marketplaceResp.plugins;
|
||||
|
||||
// 创建市场插件映射,便于快速查找
|
||||
const marketplacePluginMap = new Map();
|
||||
marketplacePlugins.forEach((plugin) => {
|
||||
const key = `${plugin.author}/${plugin.name}`;
|
||||
marketplacePluginMap.set(key, plugin);
|
||||
});
|
||||
|
||||
// 转换并比较版本号
|
||||
const pluginCards = installedPlugins.map((plugin) => {
|
||||
const cardVO = new PluginCardVO({
|
||||
author: plugin.manifest.manifest.metadata.author ?? '',
|
||||
label: extractI18nObject(plugin.manifest.manifest.metadata.label),
|
||||
description: extractI18nObject(
|
||||
plugin.manifest.manifest.metadata.description ?? {
|
||||
en_US: '',
|
||||
zh_Hans: '',
|
||||
},
|
||||
),
|
||||
debug: plugin.debug,
|
||||
enabled: plugin.enabled,
|
||||
name: plugin.manifest.manifest.metadata.name,
|
||||
version: plugin.manifest.manifest.metadata.version ?? '',
|
||||
status: plugin.status,
|
||||
components: plugin.components,
|
||||
priority: plugin.priority,
|
||||
install_source: plugin.install_source,
|
||||
install_info: plugin.install_info,
|
||||
});
|
||||
|
||||
// 检查是否来自市场且有更新
|
||||
if (cardVO.install_source === 'marketplace') {
|
||||
const marketplaceKey = `${cardVO.author}/${cardVO.name}`;
|
||||
const marketplacePlugin = marketplacePluginMap.get(marketplaceKey);
|
||||
if (marketplacePlugin && marketplacePlugin.latest_version) {
|
||||
cardVO.hasUpdate = isNewerVersion(
|
||||
marketplacePlugin.latest_version,
|
||||
cardVO.version,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return cardVO;
|
||||
});
|
||||
|
||||
setPluginList(pluginCards);
|
||||
} catch (error) {
|
||||
console.error('获取插件列表失败:', error);
|
||||
// 失败时仍显示已安装插件,不影响用户体验
|
||||
const installedPluginsResp = await httpClient.getPlugins();
|
||||
setPluginList(
|
||||
value.plugins.map((plugin) => {
|
||||
installedPluginsResp.plugins.map((plugin) => {
|
||||
return new PluginCardVO({
|
||||
author: plugin.manifest.manifest.metadata.author ?? '',
|
||||
label: extractI18nObject(plugin.manifest.manifest.metadata.label),
|
||||
@@ -97,7 +157,7 @@ const PluginInstalledComponent = forwardRef<PluginInstalledComponentRef>(
|
||||
});
|
||||
}),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
|
||||
@@ -159,12 +159,17 @@ export default function PluginCardComponent({
|
||||
}}
|
||||
>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
className="bg-white dark:bg-[#1f1f22] hover:bg-gray-100 dark:hover:bg-[#2a2a2d]"
|
||||
>
|
||||
<Ellipsis className="w-4 h-4" />
|
||||
</Button>
|
||||
<div className="relative">
|
||||
<Button
|
||||
variant="ghost"
|
||||
className="bg-white dark:bg-[#1f1f22] hover:bg-gray-100 dark:hover:bg-[#2a2a2d]"
|
||||
>
|
||||
<Ellipsis className="w-4 h-4" />
|
||||
</Button>
|
||||
{cardVO.hasUpdate && (
|
||||
<div className="absolute -top-0.5 -right-0.5 w-2.5 h-2.5 bg-red-500 rounded-full border-2 border-white dark:border-[#1f1f22]"></div>
|
||||
)}
|
||||
</div>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent>
|
||||
{/**upgrade */}
|
||||
@@ -179,6 +184,11 @@ export default function PluginCardComponent({
|
||||
>
|
||||
<ArrowUp className="w-4 h-4" />
|
||||
<span>{t('plugins.update')}</span>
|
||||
{cardVO.hasUpdate && (
|
||||
<Badge className="ml-auto bg-red-500 hover:bg-red-500 text-white text-[0.6rem] px-1.5 py-0 h-4">
|
||||
{t('plugins.new')}
|
||||
</Badge>
|
||||
)}
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
{/**view source */}
|
||||
|
||||
Reference in New Issue
Block a user