feat: add GroupExtraDataPanel for support panel which allow edit

pull/90/head
moonrailgun 2 years ago
parent 310540631d
commit a64f742365

@ -41,6 +41,7 @@ export const Markdown: React.FC<{
className="tailchat-markdown"
transformImageUri={(src) => transformUrl(src)}
transformLinkUri={(href) => transformUrl(href)}
linkTarget="_blank"
skipHtml={true}
components={components}
>

@ -141,17 +141,20 @@ export function closeModal(key?: number): void {
export function openModal(
content: React.ReactNode,
props?: Pick<ModalProps, 'closable' | 'maskClosable'> & {
onCloseModal?: () => void;
onCloseModal?: () => void | Promise<void>;
}
): number {
const key = PortalAdd(
<Modal
{...props}
visible={true}
onChangeVisible={(visible) => {
onChangeVisible={async (visible) => {
if (visible === false) {
if (typeof props?.onCloseModal === 'function') {
await props.onCloseModal();
}
closeModal(key);
props?.onCloseModal?.();
}
}}
>

@ -0,0 +1,149 @@
import { useGroupPanelContext } from '@/context/GroupPanelContext';
import React, { useReducer, useRef } from 'react';
import {
model,
PERMISSION,
showSuccessToasts,
showToasts,
t,
useAsync,
useHasGroupPermission,
useMemoizedFn,
} from 'tailchat-shared';
import { Problem } from '@/components/Problem';
import { ErrorView } from '@/components/ErrorView';
import { Loading } from '@/components/Loading';
import { GroupPanelContainer } from './shared/GroupPanelContainer';
import { IconBtn } from '@/components/IconBtn';
import { openModal } from '@/components/Modal';
import _isEqual from 'lodash/isEqual';
type GroupExtraDataPanelRenderInfo = Record<string, string>; // <name, value>
interface GroupExtraDataPanelInnerProps extends GroupExtraDataPanelProps {
groupId: string;
panelId: string;
}
/**
* A Component which can edit panel and display with group extra data
*/
const GroupExtraDataPanelInner: React.FC<GroupExtraDataPanelInnerProps> =
React.memo((props) => {
const { groupId, panelId, names } = props;
const [updateIndex, updateInfo] = useReducer((state) => state + 1, 0);
const {
value: info = {},
loading,
error,
} = useAsync(async () => {
const list = await Promise.all(
names.map((name) =>
model.group
.getGroupPanelExtraData(props.groupId, props.panelId, name)
.then((data) => ({
name,
data,
}))
)
);
return list.reduce((prev, curr) => {
return {
...prev,
[curr.name]: curr.data,
};
}, {});
}, [groupId, panelId, names, updateIndex]);
const [hasPermission] = useHasGroupPermission(groupId, [
PERMISSION.core.managePanel,
]);
const savingRef = useRef(false);
const handleEdit = useMemoizedFn(() => {
const dataMap = { ...info };
if (!props.renderEdit) {
console.warn('[GroupExtraDataPanel] Please set renderEdit');
return;
}
const handleSave = async () => {
if (savingRef.current === true) {
showToasts(t('正在保存, 请稍后'));
return;
}
if (!_isEqual(dataMap, info)) {
savingRef.current = true;
await Promise.all(
Object.entries(dataMap).map(([name, data]) =>
model.group.saveGroupPanelExtraData(groupId, panelId, name, data)
)
);
savingRef.current = false;
updateInfo();
showSuccessToasts();
}
};
openModal(props.renderEdit(dataMap), {
onCloseModal: handleSave,
});
});
if (error) {
return <ErrorView error={error} />;
}
return (
<GroupPanelContainer
groupId={groupId}
panelId={panelId}
prefixActions={() =>
hasPermission
? [
<IconBtn
key="edit"
title={t('编辑')}
shape="square"
icon="mdi:square-edit-outline"
iconClassName="text-2xl"
onClick={handleEdit}
/>,
]
: []
}
>
<Loading spinning={loading}>
<div>{props.render(info)}</div>
</Loading>
</GroupPanelContainer>
);
});
GroupExtraDataPanelInner.displayName = 'GroupExtraDataPanelInner';
interface GroupExtraDataPanelProps {
names: string[];
renderEdit: (dataMap: Record<string, string>) => React.ReactNode;
render: (info: GroupExtraDataPanelRenderInfo) => React.ReactNode;
}
export const GroupExtraDataPanel: React.FC<GroupExtraDataPanelProps> =
React.memo((props) => {
const context = useGroupPanelContext();
if (context === null) {
return <Problem text={t('组件只能在群组面板中才能正常显示')} />;
}
return (
<GroupExtraDataPanelInner
{...props}
groupId={context.groupId}
panelId={context.panelId}
/>
);
});
GroupExtraDataPanel.displayName = 'GroupExtraDataPanel';

@ -1,3 +1,4 @@
import { Problem } from '@/components/Problem';
import { findPluginPanelInfoByName } from '@/utils/plugin-helper';
import { Alert } from 'antd';
import React, { useMemo } from 'react';
@ -64,7 +65,7 @@ export const GroupPluginPanel: React.FC<GroupPluginPanelProps> = React.memo(
if (!Component) {
// 没有找到插件组件
// TODO: Fallback
return null;
return <Problem text={t('插件渲染函数不存在')} />;
}
return <Component panelInfo={panelInfo} />;

@ -59,6 +59,17 @@ export const builtinPlugins: PluginManifest[] = _compact([
'description.zh-CN': '为应用首次打开介绍应用的能力',
requireRestart: true,
},
{
label: 'Markdown Panel',
'label.zh-CN': 'Markdown 面板',
name: 'com.msgbyte.mdpanel',
url: '/plugins/com.msgbyte.mdpanel/index.js',
version: '0.0.0',
author: 'moonrailgun',
description: 'Add markdown panel support',
'description.zh-CN': '增加 Markdown 面板支持',
requireRestart: true,
},
// isOffical
isOffical && {
label: 'Posthog',

@ -30,6 +30,7 @@ export {
export { Link } from 'react-router-dom';
export { MessageAckContainer } from '@/components/ChatBox/ChatMessageList/MessageAckContainer';
export { GroupExtraDataPanel } from '@/components/Panel/group/GroupExtraDataPanel';
export { Image } from '@/components/Image';
export { IconBtn } from '@/components/IconBtn';
export { PillTabs, PillTabPane } from '@/components/PillTabs';

@ -74,7 +74,7 @@ export const GroupPanelRender: React.FC<GroupPanelRenderProps> = React.memo(
if (panelInfo.type === GroupPanelType.PLUGIN) {
return (
<GroupPanelContext.Provider value={groupPanelContextValue}>
<GroupPluginPanel groupId={groupId} panelId={panelInfo.id} />;
<GroupPluginPanel groupId={groupId} panelId={panelInfo.id} />
</GroupPanelContext.Provider>
);
}

@ -48,7 +48,7 @@ class GroupExtraService extends TcService {
name,
});
return res?.data ?? null;
return { data: res?.data ?? null };
}
async savePanelData(
@ -69,6 +69,9 @@ class GroupExtraService extends TcService {
},
{
data: String(data),
},
{
upsert: true, // Create if not exist
}
);

Loading…
Cancel
Save