mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-09-16 14:57:15 +00:00
feat(web): improve marketplace install workflows
This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
import { Download } from 'lucide-react';
|
||||
|
||||
import { Progress } from '@/components/ui/progress';
|
||||
|
||||
export default function MarketplaceInstallButton({
|
||||
installing,
|
||||
progress,
|
||||
disabled,
|
||||
label,
|
||||
onInstall,
|
||||
}: {
|
||||
installing: boolean;
|
||||
progress: number;
|
||||
disabled: boolean;
|
||||
label: string;
|
||||
onInstall: () => void;
|
||||
}) {
|
||||
const normalizedProgress = Math.max(0, Math.min(100, Math.round(progress)));
|
||||
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
aria-label={label}
|
||||
title={label}
|
||||
disabled={disabled}
|
||||
className="row-span-2 flex h-8 w-16 shrink-0 items-center justify-center justify-self-end rounded-md text-muted-foreground transition-colors hover:bg-background hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-45"
|
||||
onPointerDown={(event) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}}
|
||||
onClick={(event) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
onInstall();
|
||||
}}
|
||||
>
|
||||
{installing ? (
|
||||
<span className="flex w-14 flex-col gap-1" aria-live="polite">
|
||||
<span className="text-center text-[10px] font-medium leading-none tabular-nums text-primary">
|
||||
{normalizedProgress}%
|
||||
</span>
|
||||
<Progress value={normalizedProgress} className="h-1 bg-primary/15" />
|
||||
</span>
|
||||
) : (
|
||||
<Download className="size-4" />
|
||||
)}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
@@ -113,6 +113,10 @@ import {
|
||||
} from '@/components/ui/popover';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { useSidebarData, SidebarEntityItem } from './SidebarDataContext';
|
||||
import {
|
||||
pluginTaskKey,
|
||||
usePluginInstallTasks,
|
||||
} from '@/app/home/plugins/components/plugin-install-task';
|
||||
import { FeedbackPopoverContent } from './FeedbackPopover';
|
||||
import {
|
||||
type WorkspaceQuotaItem,
|
||||
@@ -404,6 +408,7 @@ function NavItems({
|
||||
const pathname = location.pathname;
|
||||
const [searchParams] = useSearchParams();
|
||||
const sidebarData = useSidebarData();
|
||||
const refreshSidebarPlugins = sidebarData.refreshPlugins;
|
||||
const quotaStatus = useWorkspaceQuotaStatus();
|
||||
const { state: sidebarState, isMobile } = useSidebar();
|
||||
const { t } = useTranslation();
|
||||
@@ -452,19 +457,36 @@ function NavItems({
|
||||
const [targetPluginItem, setTargetPluginItem] =
|
||||
useState<SidebarEntityItem | null>(null);
|
||||
const [deleteData, setDeleteData] = useState(false);
|
||||
const {
|
||||
addTask: addPluginTask,
|
||||
setSelectedTaskId: setSelectedPluginTaskId,
|
||||
registerOnTaskComplete,
|
||||
unregisterOnTaskComplete,
|
||||
} = usePluginInstallTasks();
|
||||
|
||||
const asyncTask = useAsyncTask({
|
||||
onSuccess: () => {
|
||||
const msg =
|
||||
pluginOpType === PluginOperationType.DELETE
|
||||
? t('plugins.deleteSuccess')
|
||||
: t('plugins.updateSuccess');
|
||||
toast.success(msg);
|
||||
toast.success(t('plugins.deleteSuccess'));
|
||||
setShowPluginOpModal(false);
|
||||
sidebarData.refreshPlugins();
|
||||
},
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const onPluginTaskComplete = (
|
||||
_taskId: number,
|
||||
success: boolean,
|
||||
_error?: string,
|
||||
operation?: 'install' | 'upgrade',
|
||||
) => {
|
||||
if (success && operation === 'upgrade') {
|
||||
refreshSidebarPlugins();
|
||||
}
|
||||
};
|
||||
registerOnTaskComplete(onPluginTaskComplete);
|
||||
return () => unregisterOnTaskComplete(onPluginTaskComplete);
|
||||
}, [refreshSidebarPlugins, registerOnTaskComplete, unregisterOnTaskComplete]);
|
||||
|
||||
function handlePluginDelete(item: SidebarEntityItem) {
|
||||
setTargetPluginItem(item);
|
||||
setPluginOpType(PluginOperationType.DELETE);
|
||||
@@ -490,21 +512,37 @@ function NavItems({
|
||||
? targetPluginItem.id.substring(slashIdx + 1)
|
||||
: targetPluginItem.id;
|
||||
|
||||
const apiCall =
|
||||
pluginOpType === PluginOperationType.DELETE
|
||||
? httpClient.removePlugin(author, name, deleteData)
|
||||
: httpClient.upgradePlugin(author, name);
|
||||
if (pluginOpType === PluginOperationType.UPDATE) {
|
||||
httpClient
|
||||
.upgradePlugin(author, name)
|
||||
.then((res) => {
|
||||
addPluginTask({
|
||||
taskId: res.task_id,
|
||||
pluginName: `${author}/${name}`,
|
||||
source: 'marketplace',
|
||||
extensionType: 'plugin',
|
||||
operation: 'upgrade',
|
||||
});
|
||||
setSelectedPluginTaskId(
|
||||
pluginTaskKey(res.task_id, 'marketplace', 'upgrade'),
|
||||
);
|
||||
setShowPluginOpModal(false);
|
||||
setTargetPluginItem(null);
|
||||
asyncTask.reset();
|
||||
})
|
||||
.catch((error) => {
|
||||
toast.error(t('plugins.updateError') + error.message);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
apiCall
|
||||
httpClient
|
||||
.removePlugin(author, name, deleteData)
|
||||
.then((res) => {
|
||||
asyncTask.startTask(res.task_id);
|
||||
})
|
||||
.catch((error) => {
|
||||
const errorMessage =
|
||||
pluginOpType === PluginOperationType.DELETE
|
||||
? t('plugins.deleteError') + error.message
|
||||
: t('plugins.updateError') + error.message;
|
||||
toast.error(errorMessage);
|
||||
toast.error(t('plugins.deleteError') + error.message);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user