Skip to content

Commit

Permalink
♻️ refactor: refactor plugin dev modal and improve plugin store
Browse files Browse the repository at this point in the history
  • Loading branch information
arvinxx authored and canisminor1990 committed Dec 15, 2023
1 parent 6e29698 commit 4dc5e35
Show file tree
Hide file tree
Showing 9 changed files with 252 additions and 191 deletions.
2 changes: 1 addition & 1 deletion src/features/PluginDevModal/PluginPreview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ const PluginPreview = memo<{ form: FormInstance }>(({ form }) => {

return (
<Card size={'small'} title={t('dev.preview.card')}>
<Form.Item {...items} colon={false} style={{ marginBottom: 0 }} />
<Form.Item {...items} colon={false} style={{ alignItems: 'center', marginBottom: 0 }} />
</Card>
);
});
Expand Down
105 changes: 105 additions & 0 deletions src/features/PluginDevModal/UrlManifestForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import { LobeChatPluginManifest } from '@lobehub/chat-plugin-sdk';
import { ActionIcon, FormItem, Input } from '@lobehub/ui';
import { Form, FormInstance } from 'antd';
import { FileCode, RotateCwIcon } from 'lucide-react';
import { memo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Flexbox } from 'react-layout-kit';

import ManifestPreviewer from '@/components/ManifestPreviewer';
import { pluginService } from '@/services/plugin';
import { useToolStore } from '@/store/tool';
import { pluginSelectors } from '@/store/tool/selectors';
import { PluginInstallError } from '@/types/tool/plugin';

const UrlManifestForm = memo<{ form: FormInstance; isEditMode: boolean }>(
({ form, isEditMode }) => {
const { t } = useTranslation('plugin');

const [manifest, setManifest] = useState<LobeChatPluginManifest>();

const urlKey = ['customParams', 'manifestUrl'];
const pluginIds = useToolStore(pluginSelectors.storeAndInstallPluginsIdList);

return (
<Form form={form} layout={'vertical'}>
<FormItem
extra={
<Flexbox horizontal justify={'space-between'} style={{ marginTop: 8 }}>
{t('dev.meta.manifest.desc')}
{manifest && (
<ManifestPreviewer manifest={manifest}>
<ActionIcon
icon={FileCode}
size={'small'}
title={t('dev.meta.manifest.preview')}
/>
</ManifestPreviewer>
)}
</Flexbox>
}
hasFeedback
label={t('dev.meta.manifest.label')}
name={urlKey}
required
rules={[
{ required: true },
{
message: t('error.urlError'),
pattern: /^https?:\/\/.*/,
},
{
validator: async (_, value) => {
if (!value) return true;

try {
const data = await pluginService.getPluginManifest(value);
setManifest(data);

form.setFieldsValue({ identifier: data.identifier, manifest: data });
} catch (error) {
const err = error as PluginInstallError;
throw t(`error.${err.message}`, { error: err.cause! });
}
},
},
// 编辑模式下,不进行重复校验
isEditMode
? {}
: {
message: t('dev.meta.identifier.errorDuplicate'),
validator: async () => {
const id = form.getFieldValue('identifier');
if (!id) return true;

if (pluginIds.includes(id)) {
throw new Error('Duplicate');
}
},
},
]}
style={{ marginBottom: 0 }}
>
<Input
placeholder={'http://localhost:3400/manifest-dev.json'}
suffix={
<ActionIcon
icon={RotateCwIcon}
onClick={(e) => {
e.stopPropagation();
form.validateFields([urlKey, 'identifier']);
}}
size={'small'}
title={t('dev.meta.manifest.refresh')}
/>
}
/>
</FormItem>
<FormItem name={'identifier'} noStyle />
<FormItem name={'manifest'} noStyle />
</Form>
);
},
);

export default UrlManifestForm;
98 changes: 0 additions & 98 deletions src/features/PluginDevModal/UrlModeForm.tsx

This file was deleted.

12 changes: 4 additions & 8 deletions src/features/PluginDevModal/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,8 @@ import MobilePadding from '@/components/MobilePadding';
import { WIKI_PLUGIN_GUIDE } from '@/const/url';
import { LobeToolCustomPlugin } from '@/types/tool/plugin';

import LocalForm from './LocalForm';
import PluginPreview from './PluginPreview';
import UrlModeForm from './UrlModeForm';
import UrlManifestForm from './UrlManifestForm';

interface DevModalProps {
mode?: 'edit' | 'create';
Expand Down Expand Up @@ -136,7 +135,6 @@ const DevModal = memo<DevModalProps>(
type={'info'}
/>
</MobilePadding>
<PluginPreview form={form} />
<Segmented
block
onChange={(e) => {
Expand All @@ -159,11 +157,9 @@ const DevModal = memo<DevModalProps>(
]}
/>
{configMode === 'url' ? (
<UrlModeForm form={form} isEditMode={mode === 'edit'} />
) : (
<LocalForm form={form} mode={mode} />
)}
{/*<MetaForm form={form} mode={mode} />*/}
<UrlManifestForm form={form} isEditMode={mode === 'edit'} />
) : null}
<PluginPreview form={form} />
</Flexbox>
</Modal>
</Form.Provider>
Expand Down
47 changes: 47 additions & 0 deletions src/features/PluginStore/AddPluginButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { Icon } from '@lobehub/ui';
import { Button } from 'antd';
import { LucideBlocks } from 'lucide-react';
import { forwardRef, useState } from 'react';
import { useTranslation } from 'react-i18next';

import DevModal from '@/features/PluginDevModal';
import { useToolStore } from '@/store/tool';

const AddPluginButton = forwardRef<HTMLButtonElement>((props, ref) => {
const { t } = useTranslation('setting');
const [showModal, setModal] = useState(false);

const [installCustomPlugin, updateNewDevPlugin] = useToolStore((s) => [
s.installCustomPlugin,
s.updateNewCustomPlugin,
]);

return (
<div
onClick={(e) => {
e.stopPropagation();
}}
>
<DevModal
onOpenChange={setModal}
onSave={async (devPlugin) => {
await installCustomPlugin(devPlugin);
// toggleAgentPlugin(devPlugin.identifier);
}}
onValueChange={updateNewDevPlugin}
open={showModal}
/>
<Button
icon={<Icon icon={LucideBlocks} />}
onClick={() => {
setModal(true);
}}
ref={ref}
>
{t('plugin.addTooltip')}
</Button>
</div>
);
});

export default AddPluginButton;
16 changes: 16 additions & 0 deletions src/features/PluginStore/InstalledPluginList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { SpotlightCard } from '@lobehub/ui';
import isEqual from 'fast-deep-equal';
import { memo } from 'react';

import { useToolStore } from '@/store/tool';
import { pluginSelectors } from '@/store/tool/selectors';

import PluginItem from './PluginItem';

export const OnlineList = memo(() => {
const installedPlugins = useToolStore(pluginSelectors.installedPluginMetaList, isEqual);

return <SpotlightCard columns={2} gap={16} items={installedPlugins} renderItem={PluginItem} />;
});

export default OnlineList;
43 changes: 43 additions & 0 deletions src/features/PluginStore/OnlineList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { Icon, SpotlightCard } from '@lobehub/ui';
import { Empty } from 'antd';
import isEqual from 'fast-deep-equal';
import { ServerCrash } from 'lucide-react';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import { Center } from 'react-layout-kit';

import { useToolStore } from '@/store/tool';
import { pluginStoreSelectors } from '@/store/tool/selectors';

import Loading from './Loading';
import PluginItem from './PluginItem';

export const OnlineList = memo(() => {
const { t } = useTranslation('plugin');

const [useFetchPluginList] = useToolStore((s) => [s.useFetchPluginStore]);

const pluginStoreList = useToolStore(pluginStoreSelectors.onlinePluginStore, isEqual);

const { isLoading, error } = useFetchPluginList();
const isEmpty = pluginStoreList.length === 0;

return isLoading ? (
<Loading />
) : isEmpty ? (
<Center gap={12} padding={40}>
{error ? (
<>
<Icon icon={ServerCrash} size={{ fontSize: 80 }} />
{t('store.networkError')}
</>
) : (
<Empty description={t('store.empty')} image={Empty.PRESENTED_IMAGE_SIMPLE}></Empty>
)}
</Center>
) : (
<SpotlightCard columns={2} gap={16} items={pluginStoreList} renderItem={PluginItem} />
);
});

export default OnlineList;
Loading

0 comments on commit 4dc5e35

Please sign in to comment.