mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-01-11 22:40:26 +01:00
(feat) Add ability to disable plugins and show bundled plugins (#337)
This commit is contained in:
@@ -5,6 +5,7 @@ import { useAtomValue } from 'jotai';
|
||||
import type { CSSProperties, ReactNode } from 'react';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { allRequestsAtom } from '../hooks/useAllRequests';
|
||||
import { useFolderActions } from '../hooks/useFolderActions';
|
||||
import { useLatestHttpResponse } from '../hooks/useLatestHttpResponse';
|
||||
import { sendAnyHttpRequest } from '../hooks/useSendAnyHttpRequest';
|
||||
import { showDialog } from '../lib/dialog';
|
||||
@@ -30,6 +31,12 @@ interface Props {
|
||||
export function FolderLayout({ folder, style }: Props) {
|
||||
const folders = useAtomValue(foldersAtom);
|
||||
const requests = useAtomValue(allRequestsAtom);
|
||||
const folderActions = useFolderActions();
|
||||
const sendAllAction = useMemo(
|
||||
() => folderActions.find((a) => a.label === 'Send All'),
|
||||
[folderActions],
|
||||
);
|
||||
|
||||
const children = useMemo(() => {
|
||||
return [
|
||||
...folders.filter((f) => f.folderId === folder.id),
|
||||
@@ -37,6 +44,10 @@ export function FolderLayout({ folder, style }: Props) {
|
||||
];
|
||||
}, [folder.id, folders, requests]);
|
||||
|
||||
const handleSendAll = useCallback(() => {
|
||||
sendAllAction?.call(folder);
|
||||
}, [sendAllAction, folder]);
|
||||
|
||||
return (
|
||||
<div style={style} className="p-6 pt-4 overflow-y-auto @container">
|
||||
<HStack space={2} alignItems="center">
|
||||
@@ -48,6 +59,8 @@ export function FolderLayout({ folder, style }: Props) {
|
||||
color="secondary"
|
||||
size="sm"
|
||||
variant="border"
|
||||
onClick={handleSendAll}
|
||||
disabled={sendAllAction == null}
|
||||
>
|
||||
Send All
|
||||
</Button>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
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 { patchModel, pluginsAtom } from '@yaakapp-internal/models';
|
||||
import type { PluginVersion } from '@yaakapp-internal/plugins';
|
||||
import {
|
||||
checkPluginUpdates,
|
||||
@@ -18,6 +18,7 @@ import { usePluginsKey, useRefreshPlugins } from '../../hooks/usePlugins';
|
||||
import { showConfirmDelete } from '../../lib/confirm';
|
||||
import { minPromiseMillis } from '../../lib/minPromiseMillis';
|
||||
import { Button } from '../core/Button';
|
||||
import { Checkbox } from '../core/Checkbox';
|
||||
import { CountBadge } from '../core/CountBadge';
|
||||
import { Icon } from '../core/Icon';
|
||||
import { IconButton } from '../core/IconButton';
|
||||
@@ -34,6 +35,8 @@ import { SelectFile } from '../SelectFile';
|
||||
export function SettingsPlugins() {
|
||||
const [directory, setDirectory] = useState<string | null>(null);
|
||||
const plugins = useAtomValue(pluginsAtom);
|
||||
const bundledPlugins = plugins.filter((p) => p.url == null);
|
||||
const installedPlugins = plugins.filter((p) => p.url != null);
|
||||
const createPlugin = useInstallPlugin();
|
||||
const refreshPlugins = useRefreshPlugins();
|
||||
const [tab, setTab] = useState<string>();
|
||||
@@ -49,7 +52,12 @@ export function SettingsPlugins() {
|
||||
{
|
||||
label: 'Installed',
|
||||
value: 'installed',
|
||||
rightSlot: <CountBadge count={plugins.length} />,
|
||||
rightSlot: <CountBadge count={installedPlugins.length} />,
|
||||
},
|
||||
{
|
||||
label: 'Bundled',
|
||||
value: 'bundled',
|
||||
rightSlot: <CountBadge count={bundledPlugins.length} />,
|
||||
},
|
||||
]}
|
||||
>
|
||||
@@ -101,6 +109,9 @@ export function SettingsPlugins() {
|
||||
</footer>
|
||||
</div>
|
||||
</TabContent>
|
||||
<TabContent value="bundled" className="pb-0">
|
||||
<BundledPlugins />
|
||||
</TabContent>
|
||||
</Tabs>
|
||||
</div>
|
||||
);
|
||||
@@ -119,6 +130,27 @@ function PluginTableRowForInstalledPlugin({ plugin }: { plugin: Plugin }) {
|
||||
name={info.name}
|
||||
displayName={info.displayName}
|
||||
url={plugin.url}
|
||||
showCheckbox={true}
|
||||
showUninstall={true}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function PluginTableRowForBundledPlugin({ 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}
|
||||
showCheckbox={true}
|
||||
showUninstall={false}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -134,6 +166,7 @@ function PluginTableRowForRemotePluginVersion({ pluginVersion }: { pluginVersion
|
||||
name={pluginVersion.name}
|
||||
displayName={pluginVersion.displayName}
|
||||
url={pluginVersion.url}
|
||||
showCheckbox={false}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -144,12 +177,16 @@ function PluginTableRow({
|
||||
version,
|
||||
displayName,
|
||||
url,
|
||||
showCheckbox = true,
|
||||
showUninstall = true,
|
||||
}: {
|
||||
plugin: Plugin | null;
|
||||
name: string;
|
||||
version: string;
|
||||
displayName: string;
|
||||
url: string | null;
|
||||
showCheckbox?: boolean;
|
||||
showUninstall?: boolean;
|
||||
}) {
|
||||
const updates = usePluginUpdates();
|
||||
const latestVersion = updates.data?.plugins.find((u) => u.name === name)?.version;
|
||||
@@ -158,9 +195,26 @@ function PluginTableRow({
|
||||
mutationFn: (name: string) => installPlugin(name, null),
|
||||
});
|
||||
const uninstall = usePromptUninstall(plugin?.id ?? null, displayName);
|
||||
const refreshPlugins = useRefreshPlugins();
|
||||
|
||||
return (
|
||||
<TableRow>
|
||||
{showCheckbox && (
|
||||
<TableCell className="!py-0">
|
||||
<Checkbox
|
||||
hideLabel
|
||||
title={plugin?.enabled ? 'Disable plugin' : 'Enable plugin'}
|
||||
checked={plugin?.enabled ?? false}
|
||||
disabled={plugin == null}
|
||||
onChange={async (enabled) => {
|
||||
if (plugin) {
|
||||
await patchModel(plugin, { enabled });
|
||||
refreshPlugins.mutate();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</TableCell>
|
||||
)}
|
||||
<TableCell className="font-semibold">
|
||||
{url ? (
|
||||
<Link noUnderline href={url}>
|
||||
@@ -170,6 +224,9 @@ function PluginTableRow({
|
||||
displayName
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<InlineCode>{name}</InlineCode>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<HStack space={1.5}>
|
||||
<InlineCode>{version}</InlineCode>
|
||||
@@ -206,7 +263,7 @@ function PluginTableRow({
|
||||
Install
|
||||
</Button>
|
||||
) : null}
|
||||
{uninstall != null && (
|
||||
{showUninstall && uninstall != null && (
|
||||
<Button
|
||||
size="xs"
|
||||
title="Uninstall plugin"
|
||||
@@ -253,6 +310,7 @@ function PluginSearch() {
|
||||
<Table scrollable>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableHeaderCell>Display Name</TableHeaderCell>
|
||||
<TableHeaderCell>Name</TableHeaderCell>
|
||||
<TableHeaderCell>Version</TableHeaderCell>
|
||||
<TableHeaderCell />
|
||||
@@ -271,7 +329,7 @@ function PluginSearch() {
|
||||
}
|
||||
|
||||
function InstalledPlugins() {
|
||||
const plugins = useAtomValue(pluginsAtom);
|
||||
const plugins = useAtomValue(pluginsAtom).filter((p) => p.url != null);
|
||||
|
||||
return plugins.length === 0 ? (
|
||||
<div className="pb-4">
|
||||
@@ -285,6 +343,8 @@ function InstalledPlugins() {
|
||||
<Table scrollable>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableHeaderCell className="w-0" />
|
||||
<TableHeaderCell>Display Name</TableHeaderCell>
|
||||
<TableHeaderCell>Name</TableHeaderCell>
|
||||
<TableHeaderCell>Version</TableHeaderCell>
|
||||
<TableHeaderCell />
|
||||
@@ -299,6 +359,33 @@ function InstalledPlugins() {
|
||||
);
|
||||
}
|
||||
|
||||
function BundledPlugins() {
|
||||
const plugins = useAtomValue(pluginsAtom).filter((p) => p.url == null);
|
||||
|
||||
return plugins.length === 0 ? (
|
||||
<div className="pb-4">
|
||||
<EmptyStateText className="text-center">No bundled plugins found.</EmptyStateText>
|
||||
</div>
|
||||
) : (
|
||||
<Table scrollable>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableHeaderCell className="w-0" />
|
||||
<TableHeaderCell>Display Name</TableHeaderCell>
|
||||
<TableHeaderCell>Name</TableHeaderCell>
|
||||
<TableHeaderCell>Version</TableHeaderCell>
|
||||
<TableHeaderCell />
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<tbody className="divide-y divide-surface-highlight">
|
||||
{plugins.map((p) => (
|
||||
<PluginTableRowForBundledPlugin key={p.id} plugin={p} />
|
||||
))}
|
||||
</tbody>
|
||||
</Table>
|
||||
);
|
||||
}
|
||||
|
||||
function usePromptUninstall(pluginId: string | null, name: string) {
|
||||
const mut = useMutation({
|
||||
mutationKey: ['uninstall_plugin', pluginId],
|
||||
|
||||
@@ -27,8 +27,6 @@ import { selectAtom } from 'jotai/utils';
|
||||
import { memo, useCallback, useEffect, useMemo, useRef } from 'react';
|
||||
import { moveToWorkspace } from '../commands/moveToWorkspace';
|
||||
import { openFolderSettings } from '../commands/openFolderSettings';
|
||||
import { activeCookieJarAtom } from '../hooks/useActiveCookieJar';
|
||||
import { activeEnvironmentAtom } from '../hooks/useActiveEnvironment';
|
||||
import { activeFolderIdAtom } from '../hooks/useActiveFolderId';
|
||||
import { activeRequestIdAtom } from '../hooks/useActiveRequestId';
|
||||
import { activeWorkspaceAtom, activeWorkspaceIdAtom } from '../hooks/useActiveWorkspace';
|
||||
@@ -49,7 +47,6 @@ import { jotaiStore } from '../lib/jotai';
|
||||
import { resolvedModelName } from '../lib/resolvedModelName';
|
||||
import { isSidebarFocused } from '../lib/scopes';
|
||||
import { navigateToRequestOrFolderOrWorkspace } from '../lib/setWorkspaceSearchParams';
|
||||
import { invokeCmd } from '../lib/tauri';
|
||||
import type { ContextMenuProps, DropdownItem } from './core/Dropdown';
|
||||
import { Dropdown } from './core/Dropdown';
|
||||
import type { FieldDef } from './core/Editor/filter/extension';
|
||||
@@ -331,20 +328,6 @@ function Sidebar({ className }: { className?: string }) {
|
||||
leftSlot: <Icon icon="folder_cog" />,
|
||||
onSelect: () => openFolderSettings(child.id),
|
||||
},
|
||||
{
|
||||
label: 'Send All',
|
||||
hidden: !(items.length === 1 && child.model === 'folder'),
|
||||
leftSlot: <Icon icon="send_horizontal" />,
|
||||
onSelect: () => {
|
||||
const environment = jotaiStore.get(activeEnvironmentAtom);
|
||||
const cookieJar = jotaiStore.get(activeCookieJarAtom);
|
||||
invokeCmd('cmd_send_folder', {
|
||||
folderId: child.id,
|
||||
environmentId: environment?.id,
|
||||
cookieJarId: cookieJar?.id,
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Send',
|
||||
hotKeyAction: 'request.send',
|
||||
|
||||
@@ -5,12 +5,16 @@ import { jotaiStore } from '../lib/jotai';
|
||||
import { minPromiseMillis } from '../lib/minPromiseMillis';
|
||||
import { invokeCmd } from '../lib/tauri';
|
||||
import { activeWorkspaceIdAtom } from './useActiveWorkspace';
|
||||
import { useDebouncedValue } from './useDebouncedValue';
|
||||
import { invalidateAllPluginInfo } from './usePluginInfo';
|
||||
|
||||
export function usePluginsKey() {
|
||||
return useAtomValue(pluginsAtom)
|
||||
const pluginKey = useAtomValue(pluginsAtom)
|
||||
.map((p) => p.id + p.updatedAt)
|
||||
.join(',');
|
||||
|
||||
// Debounce plugins both for efficiency and to give plugins a chance to reload after the DB updates
|
||||
return useDebouncedValue(pluginKey, 1000);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user