mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-03-20 08:33:52 +01:00
Fixed bugs in Plugin settings pane
This commit is contained in:
@@ -9,17 +9,17 @@ import {
|
||||
searchPlugins,
|
||||
uninstallPlugin,
|
||||
} from '@yaakapp-internal/plugins';
|
||||
import type { PluginUpdatesResponse } from '@yaakapp-internal/plugins/bindings/gen_api';
|
||||
import { useAtomValue } from 'jotai';
|
||||
import React, { useState } from 'react';
|
||||
import { useDebouncedValue } from '../../hooks/useDebouncedValue';
|
||||
import { useInstallPlugin } from '../../hooks/useInstallPlugin';
|
||||
import { usePluginInfo } from '../../hooks/usePluginInfo';
|
||||
import { useRefreshPlugins } from '../../hooks/usePlugins';
|
||||
import { usePluginsKey, useRefreshPlugins } from '../../hooks/usePlugins';
|
||||
import { showConfirmDelete } from '../../lib/confirm';
|
||||
import { minPromiseMillis } from '../../lib/minPromiseMillis';
|
||||
import { Button } from '../core/Button';
|
||||
import { CountBadge } from '../core/CountBadge';
|
||||
import { Icon } from '../core/Icon';
|
||||
import { IconButton } from '../core/IconButton';
|
||||
import { InlineCode } from '../core/InlineCode';
|
||||
import { Link } from '../core/Link';
|
||||
@@ -57,7 +57,7 @@ export function SettingsPlugins() {
|
||||
<TabContent value="search">
|
||||
<PluginSearch />
|
||||
</TabContent>
|
||||
<TabContent value="installed">
|
||||
<TabContent value="installed" className="pb-0">
|
||||
<div className="h-full grid grid-rows-[minmax(0,1fr)_auto]">
|
||||
<InstalledPlugins />
|
||||
<footer className="grid grid-cols-[minmax(0,1fr)_auto] -mx-4 py-2 px-4 border-t bg-surface-highlight border-border-subtle min-w-0">
|
||||
@@ -107,32 +107,64 @@ export function SettingsPlugins() {
|
||||
);
|
||||
}
|
||||
|
||||
function PluginTableRow({
|
||||
plugin,
|
||||
updates,
|
||||
}: {
|
||||
plugin: Plugin;
|
||||
updates: PluginUpdatesResponse | null;
|
||||
}) {
|
||||
const pluginInfo = usePluginInfo(plugin.id);
|
||||
const latestVersion = updates?.plugins.find((u) => u.name === pluginInfo.data?.name)?.version;
|
||||
const installPluginMutation = useMutation({
|
||||
mutationKey: ['install_plugin', plugin.id],
|
||||
mutationFn: (name: string) => installPlugin(name, null),
|
||||
});
|
||||
|
||||
const displayName = pluginInfo.data?.displayName ?? 'Unknown';
|
||||
const uninstallPluginMutation = usePromptUninstall(plugin.id, displayName);
|
||||
|
||||
if (pluginInfo.isPending) {
|
||||
function PluginTableRowForInstalledPlugin({ plugin }: { plugin: Plugin }) {
|
||||
const info = usePluginInfo(plugin.id).data;
|
||||
if (info == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<PluginTableRow
|
||||
plugin={plugin}
|
||||
version={info.version}
|
||||
name={info.name}
|
||||
displayName={info.displayName}
|
||||
url={plugin.url}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function PluginTableRowForRemotePluginVersion({ pluginVersion }: { pluginVersion: PluginVersion }) {
|
||||
const plugin = useAtomValue(pluginsAtom).find((p) => p.id === pluginVersion.id);
|
||||
const pluginInfo = usePluginInfo(plugin?.id ?? null).data;
|
||||
|
||||
return (
|
||||
<PluginTableRow
|
||||
plugin={plugin ?? null}
|
||||
version={pluginInfo?.version ?? pluginVersion.version}
|
||||
name={pluginVersion.name}
|
||||
displayName={pluginVersion.displayName}
|
||||
url={pluginVersion.url}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function PluginTableRow({
|
||||
plugin,
|
||||
name,
|
||||
version,
|
||||
displayName,
|
||||
url,
|
||||
}: {
|
||||
plugin: Plugin | null;
|
||||
name: string;
|
||||
version: string;
|
||||
displayName: string;
|
||||
url: string | null;
|
||||
}) {
|
||||
const updates = usePluginUpdates();
|
||||
const latestVersion = updates.data?.plugins.find((u) => u.name === name)?.version;
|
||||
const installPluginMutation = useMutation({
|
||||
mutationKey: ['install_plugin', name],
|
||||
mutationFn: (name: string) => installPlugin(name, null),
|
||||
});
|
||||
const uninstall = usePromptUninstall(plugin?.id ?? null, displayName);
|
||||
|
||||
return (
|
||||
<TableRow>
|
||||
<TableCell className="font-semibold">
|
||||
{plugin.url ? (
|
||||
<Link noUnderline href={plugin.url}>
|
||||
{url ? (
|
||||
<Link noUnderline href={url}>
|
||||
{displayName}
|
||||
</Link>
|
||||
) : (
|
||||
@@ -140,33 +172,52 @@ function PluginTableRow({
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<InlineCode>{pluginInfo.data?.version ?? 'n/a'}</InlineCode>
|
||||
<HStack space={1.5}>
|
||||
<InlineCode>{version}</InlineCode>
|
||||
{latestVersion != null && (
|
||||
<InlineCode className="text-success flex items-center gap-1">
|
||||
<Icon icon="arrow_up" size="sm" />
|
||||
{latestVersion}
|
||||
</InlineCode>
|
||||
)}
|
||||
</HStack>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<HStack justifyContent="end">
|
||||
{pluginInfo.data && latestVersion != null && (
|
||||
<TableCell className="!py-0">
|
||||
<HStack justifyContent="end" space={1.5}>
|
||||
{plugin != null && latestVersion != null ? (
|
||||
<Button
|
||||
variant="border"
|
||||
color="success"
|
||||
title={`Update to ${latestVersion}`}
|
||||
size="xs"
|
||||
isLoading={installPluginMutation.isPending}
|
||||
onClick={() => installPluginMutation.mutate(pluginInfo.data.name)}
|
||||
onClick={() => installPluginMutation.mutate(name)}
|
||||
>
|
||||
Update
|
||||
</Button>
|
||||
) : plugin == null ? (
|
||||
<Button
|
||||
variant="border"
|
||||
color="primary"
|
||||
title={`Install ${latestVersion}`}
|
||||
size="xs"
|
||||
isLoading={installPluginMutation.isPending}
|
||||
onClick={() => installPluginMutation.mutate(name)}
|
||||
>
|
||||
Install
|
||||
</Button>
|
||||
) : null}
|
||||
{uninstall != null && (
|
||||
<Button
|
||||
size="xs"
|
||||
title="Uninstall plugin"
|
||||
variant="border"
|
||||
isLoading={uninstall.isPending}
|
||||
onClick={() => uninstall.mutate()}
|
||||
>
|
||||
Uninstall
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
size="xs"
|
||||
title="Uninstall plugin"
|
||||
variant="border"
|
||||
isLoading={uninstallPluginMutation.isPending}
|
||||
onClick={async () => {
|
||||
uninstallPluginMutation.mutate();
|
||||
}}
|
||||
>
|
||||
Uninstall
|
||||
</Button>
|
||||
</HStack>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
@@ -209,20 +260,8 @@ function PluginSearch() {
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{results.data.plugins.map((plugin) => (
|
||||
<TableRow key={plugin.id}>
|
||||
<TableCell className="font-semibold">
|
||||
<Link noUnderline href={plugin.url}>
|
||||
{plugin.displayName}
|
||||
</Link>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<InlineCode>{plugin.version}</InlineCode>
|
||||
</TableCell>
|
||||
<TableCell className="w-[6rem]">
|
||||
<InstallPluginButton pluginVersion={plugin} />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
{results.data.plugins.map((p) => (
|
||||
<PluginTableRowForRemotePluginVersion key={p.id} pluginVersion={p} />
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
@@ -232,41 +271,8 @@ function PluginSearch() {
|
||||
);
|
||||
}
|
||||
|
||||
function InstallPluginButton({ pluginVersion }: { pluginVersion: PluginVersion }) {
|
||||
const plugins = useAtomValue(pluginsAtom);
|
||||
const installed = plugins?.some((p) => p.id === pluginVersion.id);
|
||||
const uninstallPluginMutation = usePromptUninstall(pluginVersion.id, pluginVersion.displayName);
|
||||
const installPluginMutation = useMutation({
|
||||
mutationKey: ['install_plugin', pluginVersion.id],
|
||||
mutationFn: (pv: PluginVersion) => installPlugin(pv.name, null),
|
||||
});
|
||||
|
||||
return (
|
||||
<Button
|
||||
size="xs"
|
||||
variant="border"
|
||||
color={installed ? 'default' : 'primary'}
|
||||
className="ml-auto"
|
||||
isLoading={installPluginMutation.isPending || uninstallPluginMutation.isPending}
|
||||
onClick={async () => {
|
||||
if (installed) {
|
||||
uninstallPluginMutation.mutate();
|
||||
} else {
|
||||
installPluginMutation.mutate(pluginVersion);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{installed ? 'Uninstall' : 'Install'}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
function InstalledPlugins() {
|
||||
const plugins = useAtomValue(pluginsAtom);
|
||||
const updates = useQuery({
|
||||
queryKey: ['plugin_updates'],
|
||||
queryFn: () => checkPluginUpdates(),
|
||||
});
|
||||
|
||||
return plugins.length === 0 ? (
|
||||
<div className="pb-4">
|
||||
@@ -286,18 +292,20 @@ function InstalledPlugins() {
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<tbody className="divide-y divide-surface-highlight">
|
||||
{plugins.map((p) => {
|
||||
return <PluginTableRow key={p.id} plugin={p} updates={updates.data ?? null} />;
|
||||
})}
|
||||
{plugins.map((p) => (
|
||||
<PluginTableRowForInstalledPlugin key={p.id} plugin={p} />
|
||||
))}
|
||||
</tbody>
|
||||
</Table>
|
||||
);
|
||||
}
|
||||
|
||||
function usePromptUninstall(pluginId: string, name: string) {
|
||||
return useMutation({
|
||||
function usePromptUninstall(pluginId: string | null, name: string) {
|
||||
const mut = useMutation({
|
||||
mutationKey: ['uninstall_plugin', pluginId],
|
||||
mutationFn: async () => {
|
||||
if (pluginId == null) return;
|
||||
|
||||
const confirmed = await showConfirmDelete({
|
||||
id: 'uninstall-plugin-' + pluginId,
|
||||
title: 'Uninstall Plugin',
|
||||
@@ -313,4 +321,13 @@ function usePromptUninstall(pluginId: string, name: string) {
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
return pluginId == null ? null : mut;
|
||||
}
|
||||
|
||||
function usePluginUpdates() {
|
||||
return useQuery({
|
||||
queryKey: ['plugin_updates', usePluginsKey()],
|
||||
queryFn: () => checkPluginUpdates(),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -177,7 +177,7 @@ export const TabContent = memo(function TabContent({
|
||||
<div
|
||||
tabIndex={-1}
|
||||
data-tab={value}
|
||||
className={classNames(className, 'tab-content', 'hidden w-full h-full py-2')}
|
||||
className={classNames(className, 'tab-content', 'hidden w-full h-full pt-2')}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
|
||||
@@ -6,18 +6,21 @@ import { useAtomValue } from 'jotai';
|
||||
import { queryClient } from '../lib/queryClient';
|
||||
import { invokeCmd } from '../lib/tauri';
|
||||
|
||||
function pluginInfoKey(id: string, plugin: Plugin | null) {
|
||||
return ['plugin_info', id, plugin?.updatedAt ?? 'n/a'];
|
||||
function pluginInfoKey(id: string | null, plugin: Plugin | null) {
|
||||
return ['plugin_info', id ?? 'n/a', plugin?.updatedAt ?? 'n/a'];
|
||||
}
|
||||
|
||||
export function usePluginInfo(id: string) {
|
||||
export function usePluginInfo(id: string | null) {
|
||||
const plugins = useAtomValue(pluginsAtom);
|
||||
// Get the plugin so we can refetch whenever it's updated
|
||||
const plugin = plugins.find((p) => p.id === id);
|
||||
return useQuery({
|
||||
queryKey: pluginInfoKey(id, plugin ?? null),
|
||||
placeholderData: (prev) => prev, // Keep previous data on refetch
|
||||
queryFn: () => invokeCmd<PluginMetadata>('cmd_plugin_info', { id }),
|
||||
queryFn: () => {
|
||||
if (id == null) return null;
|
||||
return invokeCmd<PluginMetadata>('cmd_plugin_info', { id });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user