mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-03-30 22:22:02 +02:00
Install plugins from Yaak plugin registry (#230)
This commit is contained in:
@@ -1,8 +1,13 @@
|
||||
import { useMutation, useQuery } from '@tanstack/react-query';
|
||||
import { openUrl } from '@tauri-apps/plugin-opener';
|
||||
import type { Plugin } from '@yaakapp-internal/models';
|
||||
import { pluginsAtom } from '@yaakapp-internal/models';
|
||||
import { installPlugin, PluginVersion, searchPlugins } from '@yaakapp-internal/plugins';
|
||||
import { Plugin, pluginsAtom } from '@yaakapp-internal/models';
|
||||
import {
|
||||
checkPluginUpdates,
|
||||
installPlugin,
|
||||
PluginVersion,
|
||||
searchPlugins,
|
||||
} from '@yaakapp-internal/plugins';
|
||||
import { PluginUpdatesResponse } from '@yaakapp-internal/plugins/bindings/gen_api';
|
||||
import { useAtomValue } from 'jotai';
|
||||
import React, { useState } from 'react';
|
||||
import { useDebouncedValue } from '../../hooks/useDebouncedValue';
|
||||
@@ -43,15 +48,8 @@ export function SettingsPlugins() {
|
||||
<PluginSearch />
|
||||
</TabContent>
|
||||
<TabContent value="installed">
|
||||
<InstalledPlugins />
|
||||
<form
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
if (directory == null) return;
|
||||
createPlugin.mutate(directory);
|
||||
setDirectory(null);
|
||||
}}
|
||||
>
|
||||
<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">
|
||||
<SelectFile
|
||||
size="xs"
|
||||
@@ -62,7 +60,16 @@ export function SettingsPlugins() {
|
||||
/>
|
||||
<HStack>
|
||||
{directory && (
|
||||
<Button size="xs" type="submit" color="primary" className="ml-auto">
|
||||
<Button
|
||||
size="xs"
|
||||
color="primary"
|
||||
className="ml-auto"
|
||||
onClick={() => {
|
||||
if (directory == null) return;
|
||||
createPlugin.mutate(directory);
|
||||
setDirectory(null);
|
||||
}}
|
||||
>
|
||||
Add Plugin
|
||||
</Button>
|
||||
)}
|
||||
@@ -83,31 +90,61 @@ export function SettingsPlugins() {
|
||||
/>
|
||||
</HStack>
|
||||
</footer>
|
||||
</form>
|
||||
</div>
|
||||
</TabContent>
|
||||
</Tabs>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function PluginInfo({ plugin }: { plugin: Plugin }) {
|
||||
function PluginTableRow({
|
||||
plugin,
|
||||
updates,
|
||||
}: {
|
||||
plugin: Plugin;
|
||||
updates: PluginUpdatesResponse | null;
|
||||
}) {
|
||||
const pluginInfo = usePluginInfo(plugin.id);
|
||||
const deletePlugin = useUninstallPlugin();
|
||||
const uninstallPlugin = useUninstallPlugin();
|
||||
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),
|
||||
});
|
||||
if (pluginInfo.data == null) return null;
|
||||
|
||||
return (
|
||||
<tr className="group">
|
||||
<td className="py-2 select-text cursor-text w-full">{pluginInfo.data?.name}</td>
|
||||
<td className="py-2 select-text cursor-text text-right">
|
||||
<TableRow>
|
||||
<TableCell className="font-semibold">{pluginInfo.data.displayName}</TableCell>
|
||||
<TableCell>
|
||||
<InlineCode>{pluginInfo.data?.version}</InlineCode>
|
||||
</td>
|
||||
<td className="py-2 select-text cursor-text pl-2">
|
||||
<IconButton
|
||||
size="sm"
|
||||
icon="trash"
|
||||
title="Uninstall plugin"
|
||||
onClick={() => deletePlugin.mutate(plugin.id)}
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
</TableCell>
|
||||
<TableCell className="w-full text-text-subtle">{pluginInfo.data.description}</TableCell>
|
||||
<TableCell>
|
||||
<HStack>
|
||||
{latestVersion != null && (
|
||||
<Button
|
||||
variant="border"
|
||||
color="success"
|
||||
title={`Update to ${latestVersion}`}
|
||||
size="xs"
|
||||
isLoading={installPluginMutation.isPending}
|
||||
onClick={() => installPluginMutation.mutate(pluginInfo.data.name)}
|
||||
>
|
||||
Update
|
||||
</Button>
|
||||
)}
|
||||
<IconButton
|
||||
size="sm"
|
||||
icon="trash"
|
||||
title="Uninstall plugin"
|
||||
onClick={async () => {
|
||||
uninstallPlugin.mutate({ pluginId: plugin.id, name: pluginInfo.data.displayName });
|
||||
}}
|
||||
/>
|
||||
</HStack>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -135,7 +172,7 @@ function PluginSearch() {
|
||||
<EmptyStateText>
|
||||
<LoadingIcon size="xl" className="text-text-subtlest" />
|
||||
</EmptyStateText>
|
||||
) : (results.data.results ?? []).length === 0 ? (
|
||||
) : (results.data.plugins ?? []).length === 0 ? (
|
||||
<EmptyStateText>No plugins found</EmptyStateText>
|
||||
) : (
|
||||
<Table>
|
||||
@@ -148,22 +185,20 @@ function PluginSearch() {
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{results.data.results.map((plugin) => {
|
||||
return (
|
||||
<TableRow key={plugin.id}>
|
||||
<TableCell className="font-semibold">{plugin.displayName}</TableCell>
|
||||
<TableCell className="text-text-subtle">
|
||||
<InlineCode>{plugin.version}</InlineCode>
|
||||
</TableCell>
|
||||
<TableCell className="w-full text-text-subtle">
|
||||
{plugin.description ?? 'n/a'}
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<InstallPluginButton plugin={plugin} />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
);
|
||||
})}
|
||||
{results.data.plugins.map((plugin) => (
|
||||
<TableRow key={plugin.id}>
|
||||
<TableCell className="font-semibold">{plugin.displayName}</TableCell>
|
||||
<TableCell>
|
||||
<InlineCode>{plugin.version}</InlineCode>
|
||||
</TableCell>
|
||||
<TableCell className="w-full text-text-subtle">
|
||||
{plugin.description ?? 'n/a'}
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<InstallPluginButton plugin={plugin} />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
)}
|
||||
@@ -174,23 +209,23 @@ function PluginSearch() {
|
||||
|
||||
function InstallPluginButton({ plugin }: { plugin: PluginVersion }) {
|
||||
const plugins = useAtomValue(pluginsAtom);
|
||||
const deletePlugin = useUninstallPlugin();
|
||||
const uninstallPlugin = useUninstallPlugin();
|
||||
const installed = plugins?.some((p) => p.id === plugin.id);
|
||||
const installPluginMutation = useMutation({
|
||||
mutationKey: ['install_plugin', plugin.id],
|
||||
mutationFn: installPlugin,
|
||||
mutationFn: (pv: PluginVersion) => installPlugin(pv.name, null),
|
||||
});
|
||||
|
||||
return (
|
||||
<Button
|
||||
size="xs"
|
||||
variant={installed ? 'solid' : 'border'}
|
||||
color={installed ? 'primary' : 'secondary'}
|
||||
variant="border"
|
||||
color={installed ? 'secondary' : 'primary'}
|
||||
className="ml-auto"
|
||||
isLoading={installPluginMutation.isPending}
|
||||
onClick={async () => {
|
||||
if (installed) {
|
||||
deletePlugin.mutate(plugin.id);
|
||||
uninstallPlugin.mutate({ pluginId: plugin.id, name: plugin.displayName });
|
||||
} else {
|
||||
installPluginMutation.mutate(plugin);
|
||||
}
|
||||
@@ -203,6 +238,11 @@ function InstallPluginButton({ plugin }: { plugin: PluginVersion }) {
|
||||
|
||||
function InstalledPlugins() {
|
||||
const plugins = useAtomValue(pluginsAtom);
|
||||
const updates = useQuery({
|
||||
queryKey: ['plugin_updates'],
|
||||
queryFn: () => checkPluginUpdates(),
|
||||
});
|
||||
|
||||
return plugins.length === 0 ? (
|
||||
<div className="pb-4">
|
||||
<EmptyStateText className="text-center">
|
||||
@@ -212,19 +252,20 @@ function InstalledPlugins() {
|
||||
</EmptyStateText>
|
||||
</div>
|
||||
) : (
|
||||
<table className="w-full text-sm mb-auto min-w-full max-w-full divide-y divide-surface-highlight">
|
||||
<thead>
|
||||
<tr>
|
||||
<th className="py-2 text-left">Plugin</th>
|
||||
<th className="py-2 text-right">Version</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<Table>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableHeaderCell>Name</TableHeaderCell>
|
||||
<TableHeaderCell>Version</TableHeaderCell>
|
||||
<TableHeaderCell>Description</TableHeaderCell>
|
||||
<TableHeaderCell />
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<tbody className="divide-y divide-surface-highlight">
|
||||
{plugins.map((p) => (
|
||||
<PluginInfo key={p.id} plugin={p} />
|
||||
))}
|
||||
{plugins.map((p) => {
|
||||
return <PluginTableRow key={p.id} plugin={p} updates={updates.data ?? null} />;
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</Table>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user