feat: add mdpanel plugin. then we can create markdown panel in group

pull/90/head
moonrailgun 2 years ago
parent a64f742365
commit 1f21e406a5

@ -11,7 +11,7 @@ export const groupConfigNames = [
'hideGroupMemberDiscriminator',
] as const;
export type GroupConfigNames = typeof groupConfigNames[number];
export type GroupConfigNames = (typeof groupConfigNames)[number];
export interface GroupMember {
roles: string[]; // 角色组
@ -468,3 +468,37 @@ export async function deleteGroupMember(groupId: string, memberId: string) {
memberId,
});
}
/**
* Get Group Panel Data from group.extra service
*/
export async function getGroupPanelExtraData(
groupId: string,
panelId: string,
name: string
): Promise<string | null> {
const { data } = await request.post('/api/group/extra/getPanelData', {
groupId,
panelId,
name,
});
return data.data ?? null;
}
/**
* Save Group Panel Data to group.extra service
*/
export async function saveGroupPanelExtraData(
groupId: string,
panelId: string,
name: string,
data: any
): Promise<void> {
await request.post('/api/group/extra/savePanelData', {
groupId,
panelId,
name,
data: typeof data === 'string' ? data : JSON.stringify(data),
});
}

@ -0,0 +1,11 @@
{
"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
}

@ -0,0 +1,16 @@
{
"name": "@plugins/com.msgbyte.mdpanel",
"main": "src/index.tsx",
"version": "0.0.0",
"description": "Add markdown panel support",
"private": true,
"scripts": {
"sync:declaration": "tailchat declaration github"
},
"dependencies": {},
"devDependencies": {
"@types/styled-components": "^5.1.26",
"react": "18.2.0",
"styled-components": "^5.3.6"
}
}

@ -0,0 +1,53 @@
import React from 'react';
import { GroupExtraDataPanel, Markdown, TextArea } from '@capital/component';
import styled from 'styled-components';
import { Translate } from '../translate';
const MainContent = styled.div`
padding: 10px;
`;
const EditModalContent = styled.div`
padding: 10px;
width: 80vw;
height: 80vh;
display: flex;
flex-direction: column;
.ant-input {
flex: 1;
resize: none;
}
`;
const MarkdownPanel: React.FC = React.memo(() => {
return (
<GroupExtraDataPanel
names={['markdown']}
render={(info) => {
return (
<MainContent>
<Markdown raw={info['markdown'] ?? ''} />
</MainContent>
);
}}
renderEdit={(dataMap: Record<string, string>) => {
return (
<EditModalContent>
<div>{Translate.editTip}</div>
<TextArea
defaultValue={dataMap['markdown']}
onChange={(e) => {
dataMap['markdown'] = e.target.value;
}}
/>
</EditModalContent>
);
}}
/>
);
});
MarkdownPanel.displayName = 'MarkdownPanel';
export default MarkdownPanel;

@ -0,0 +1,17 @@
import { regGroupPanel } from '@capital/common';
import { Loadable } from '@capital/component';
import { Translate } from './translate';
const PLUGIN_ID = 'com.msgbyte.mdpanel';
const PLUGIN_NAME = 'Markdown Panel';
console.log(`Plugin ${PLUGIN_NAME}(${PLUGIN_ID}) is loaded`);
regGroupPanel({
name: `${PLUGIN_NAME}/customwebpanel`,
label: Translate.name,
provider: PLUGIN_NAME,
render: Loadable(() => import('./group/MarkdownPanel'), {
componentName: `${PLUGIN_ID}:MarkdownPanel`,
}),
});

@ -0,0 +1,13 @@
import { localTrans } from '@capital/common';
export const Translate = {
name: localTrans({
'zh-CN': 'Markdown 面板',
'en-US': 'Markdown Panel',
}),
editTip: localTrans({
'zh-CN': '使用markdown语法编辑, 关闭窗口自动保存',
'en-US':
'Edit with markdown syntax, close the window and save automatically',
}),
};

@ -0,0 +1,7 @@
{
"compilerOptions": {
"esModuleInterop": true,
"jsx": "react",
"importsNotUsedAsValues": "error"
}
}

@ -0,0 +1,2 @@
declare module '@capital/common';
declare module '@capital/component';

@ -734,6 +734,16 @@ importers:
dependencies:
shepherd.js: 8.3.1
client/web/plugins/com.msgbyte.mdpanel:
specifiers:
'@types/styled-components': ^5.1.26
react: 18.2.0
styled-components: ^5.3.6
devDependencies:
'@types/styled-components': 5.1.26
react: 18.2.0
styled-components: 5.3.6_react@18.2.0
client/web/plugins/com.msgbyte.miaolang:
specifiers:
miao-lang: ^1.0.5
@ -1890,7 +1900,7 @@ packages:
resolution: {integrity: sha512-7wqMOJq8doJMZmP4ApXTzLxSr7+oO2jroJURrVEp6XShrQUObV8Tq/D0NCcoYg2uHqUrjzO0zwBjoYzelxK+sw==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/types': 7.20.7
'@babel/types': 7.21.2
'@jridgewell/gen-mapping': 0.3.2
jsesc: 2.5.2
@ -2210,6 +2220,7 @@ packages:
dependencies:
'@babel/template': 7.20.7
'@babel/types': 7.21.2
dev: false
/@babel/helper-function-name/7.21.0:
resolution: {integrity: sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==}
@ -2246,8 +2257,8 @@ packages:
'@babel/helper-split-export-declaration': 7.18.6
'@babel/helper-validator-identifier': 7.19.1
'@babel/template': 7.20.7
'@babel/traverse': 7.20.12
'@babel/types': 7.20.7
'@babel/traverse': 7.21.2
'@babel/types': 7.21.2
transitivePeerDependencies:
- supports-color
@ -2407,8 +2418,8 @@ packages:
engines: {node: '>=6.9.0'}
dependencies:
'@babel/template': 7.20.7
'@babel/traverse': 7.20.12
'@babel/types': 7.20.7
'@babel/traverse': 7.21.2
'@babel/types': 7.21.2
transitivePeerDependencies:
- supports-color
@ -2443,7 +2454,7 @@ packages:
engines: {node: '>=6.0.0'}
hasBin: true
dependencies:
'@babel/types': 7.20.7
'@babel/types': 7.21.2
/@babel/parser/7.21.2:
resolution: {integrity: sha512-URpaIJQwEkEC2T9Kn+Ai6Xe/02iNaVCuT/PtoRz3GPVJVDpPd7mLo+VddTbhCRU9TXqW5mSrQfXZyi8kDKOVpQ==}
@ -6291,54 +6302,54 @@ packages:
engines: {node: '>=6.9.0'}
dependencies:
'@babel/code-frame': 7.18.6
'@babel/generator': 7.20.7
'@babel/generator': 7.21.1
'@babel/helper-environment-visitor': 7.18.9
'@babel/helper-function-name': 7.19.0
'@babel/helper-function-name': 7.21.0
'@babel/helper-hoist-variables': 7.18.6
'@babel/helper-split-export-declaration': 7.18.6
'@babel/parser': 7.20.7
'@babel/types': 7.20.7
'@babel/parser': 7.21.2
'@babel/types': 7.21.2
debug: 4.3.4
globals: 11.12.0
transitivePeerDependencies:
- supports-color
/@babel/traverse/7.20.12_supports-color@5.5.0:
resolution: {integrity: sha512-MsIbFN0u+raeja38qboyF8TIT7K0BFzz/Yd/77ta4MsUsmP2RAnidIlwq7d5HFQrH/OZJecGV6B71C4zAgpoSQ==}
/@babel/traverse/7.20.5:
resolution: {integrity: sha512-WM5ZNN3JITQIq9tFZaw1ojLU3WgWdtkxnhM1AegMS+PvHjkM5IXjmYEGY7yukz5XS4sJyEf2VzWjI8uAavhxBQ==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/code-frame': 7.18.6
'@babel/generator': 7.20.7
'@babel/generator': 7.20.5
'@babel/helper-environment-visitor': 7.18.9
'@babel/helper-function-name': 7.19.0
'@babel/helper-hoist-variables': 7.18.6
'@babel/helper-split-export-declaration': 7.18.6
'@babel/parser': 7.20.7
'@babel/types': 7.20.7
debug: 4.3.4_supports-color@5.5.0
'@babel/parser': 7.20.5
'@babel/types': 7.20.5
debug: 4.3.4
globals: 11.12.0
transitivePeerDependencies:
- supports-color
dev: false
/@babel/traverse/7.20.5:
resolution: {integrity: sha512-WM5ZNN3JITQIq9tFZaw1ojLU3WgWdtkxnhM1AegMS+PvHjkM5IXjmYEGY7yukz5XS4sJyEf2VzWjI8uAavhxBQ==}
/@babel/traverse/7.21.2:
resolution: {integrity: sha512-ts5FFU/dSUPS13tv8XiEObDu9K+iagEKME9kAbaP7r0Y9KtZJZ+NGndDvWoRAYNpeWafbpFeki3q9QoMD6gxyw==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/code-frame': 7.18.6
'@babel/generator': 7.20.5
'@babel/generator': 7.21.1
'@babel/helper-environment-visitor': 7.18.9
'@babel/helper-function-name': 7.19.0
'@babel/helper-function-name': 7.21.0
'@babel/helper-hoist-variables': 7.18.6
'@babel/helper-split-export-declaration': 7.18.6
'@babel/parser': 7.20.5
'@babel/types': 7.20.5
'@babel/parser': 7.21.2
'@babel/types': 7.21.2
debug: 4.3.4
globals: 11.12.0
transitivePeerDependencies:
- supports-color
dev: false
/@babel/traverse/7.21.2:
/@babel/traverse/7.21.2_supports-color@5.5.0:
resolution: {integrity: sha512-ts5FFU/dSUPS13tv8XiEObDu9K+iagEKME9kAbaP7r0Y9KtZJZ+NGndDvWoRAYNpeWafbpFeki3q9QoMD6gxyw==}
engines: {node: '>=6.9.0'}
dependencies:
@ -6350,7 +6361,7 @@ packages:
'@babel/helper-split-export-declaration': 7.18.6
'@babel/parser': 7.21.2
'@babel/types': 7.21.2
debug: 4.3.4
debug: 4.3.4_supports-color@5.5.0
globals: 11.12.0
transitivePeerDependencies:
- supports-color
@ -14405,7 +14416,7 @@ packages:
/@types/hoist-non-react-statics/3.3.1:
resolution: {integrity: sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==}
dependencies:
'@types/react': 18.0.20
'@types/react': 18.0.26
hoist-non-react-statics: 3.3.2
/@types/html-minifier-terser/5.1.2:
@ -14850,7 +14861,7 @@ packages:
/@types/react-color/3.0.6:
resolution: {integrity: sha512-OzPIO5AyRmLA7PlOyISlgabpYUa3En74LP8mTMa0veCA719SvYQov4WLMsHvCgXP+L+KI9yGhYnqZafVGG0P4w==}
dependencies:
'@types/react': 17.0.53
'@types/react': 18.0.26
'@types/reactcss': 1.2.6
dev: false
@ -14879,7 +14890,7 @@ packages:
/@types/react-is/17.0.3:
resolution: {integrity: sha512-aBTIWg1emtu95bLTLx0cpkxwGW3ueZv71nE2YFBpL8k/z5czEW8yYpOo8Dp+UUAFAtKwNaOsh/ioSeQnWlZcfw==}
dependencies:
'@types/react': 17.0.53
'@types/react': 18.0.26
dev: false
/@types/react-mentions/4.1.8:
@ -14981,7 +14992,7 @@ packages:
/@types/reactcss/1.2.6:
resolution: {integrity: sha512-qaIzpCuXNWomGR1Xq8SCFTtF4v8V27Y6f+b9+bzHiv087MylI/nTCqqdChNeWS7tslgROmYB7yeiruWX7WnqNg==}
dependencies:
'@types/react': 17.0.53
'@types/react': 18.0.26
dev: false
/@types/refractor/3.0.2:
@ -15067,7 +15078,7 @@ packages:
resolution: {integrity: sha512-KuKJ9Z6xb93uJiIyxo/+ksS7yLjS1KzG6iv5i78dhVg/X3u5t1H7juRWqVmodIdz6wGVaIApo1u01kmFRdJHVw==}
dependencies:
'@types/hoist-non-react-statics': 3.3.1
'@types/react': 18.0.20
'@types/react': 18.0.26
csstype: 3.1.1
dev: true
@ -17144,7 +17155,7 @@ packages:
babel-plugin-syntax-jsx: 6.18.0
lodash: 4.17.21
picomatch: 2.3.1
styled-components: 5.3.6_7i5myeigehqah43i5u7wbekgba
styled-components: 5.3.6_react@18.2.0
/babel-plugin-syntax-jsx/6.18.0:
resolution: {integrity: sha512-qrPaCSo9c8RHNRHIotaufGbuOBN8rtdC4QrrFFc43vyWCCz7Kl7GL1PGaXtMGQZUXrkCjNEgxDfmAuAabr/rlw==}
@ -25678,7 +25689,7 @@ packages:
pretty-format: 27.5.1
slash: 3.0.0
strip-json-comments: 3.1.1
ts-node: 10.9.1_zlol4fzmmjgb3bdeviopae4asm
ts-node: 10.9.1_4hee3ckhxcse3era5mxqjwg7u4
transitivePeerDependencies:
- bufferutil
- canvas
@ -36530,7 +36541,7 @@ packages:
react-is: '>= 16.8.0'
dependencies:
'@babel/helper-module-imports': 7.18.6
'@babel/traverse': 7.20.12_supports-color@5.5.0
'@babel/traverse': 7.21.2_supports-color@5.5.0
'@emotion/is-prop-valid': 1.2.0
'@emotion/stylis': 0.8.5
'@emotion/unitless': 0.7.5
@ -36542,6 +36553,7 @@ packages:
react-is: 18.2.0
shallowequal: 1.1.0
supports-color: 5.5.0
dev: false
/styled-components/5.3.6_mdz3marskokvq6744hhidi3r5a:
resolution: {integrity: sha512-hGTZquGAaTqhGWldX7hhfzjnIYBZ0IXQXkCYdvF1Sq3DsUaLx6+NTHC5Jj1ooM2F68sBiVz3lvhfwQs/S3l6qg==}
@ -36553,7 +36565,7 @@ packages:
react-is: '>= 16.8.0'
dependencies:
'@babel/helper-module-imports': 7.18.6
'@babel/traverse': 7.20.12_supports-color@5.5.0
'@babel/traverse': 7.21.2_supports-color@5.5.0
'@emotion/is-prop-valid': 1.2.0
'@emotion/stylis': 0.8.5
'@emotion/unitless': 0.7.5
@ -36577,7 +36589,7 @@ packages:
react-is: '>= 16.8.0'
dependencies:
'@babel/helper-module-imports': 7.18.6
'@babel/traverse': 7.20.12_supports-color@5.5.0
'@babel/traverse': 7.21.2_supports-color@5.5.0
'@emotion/is-prop-valid': 1.2.0
'@emotion/stylis': 0.8.5
'@emotion/unitless': 0.7.5
@ -36587,7 +36599,6 @@ packages:
react: 18.2.0
shallowequal: 1.1.0
supports-color: 5.5.0
dev: true
/styled-system/5.1.5:
resolution: {integrity: sha512-7VoD0o2R3RKzOzPK0jYrVnS8iJdfkKsQJNiLRDjikOpQVqQHns/DXWaPZOH4tIKkhAT7I6wIsy9FWTWh2X3q+A==}
@ -39543,7 +39554,7 @@ packages:
mime-types: 2.1.35
range-parser: 1.2.1
schema-utils: 4.0.0
webpack: 5.75.0_webpack-cli@4.10.0
webpack: 5.75.0
/webpack-dev-server/4.11.1_pda42hcaj7d62cr262fr632kue:
resolution: {integrity: sha512-lILVz9tAUy1zGFwieuaQtYiadImb5M3d+H+L1zDYalYoDl0cksAB1UNyuE5MMWJrG6zR1tXkCP2fitl7yoUJiw==}

@ -3,7 +3,15 @@ import type {
GroupExtraDocument,
GroupExtraModel,
} from '../../../models/group/group-extra';
import { TcService, TcContext, TcDbService } from 'tailchat-server-sdk';
import {
TcService,
TcContext,
TcDbService,
call,
PERMISSION,
t,
NoPermissionError,
} from 'tailchat-server-sdk';
interface GroupExtraService
extends TcService,
@ -22,6 +30,10 @@ class GroupExtraService extends TcService {
panelId: 'string',
name: 'string',
},
cache: {
keys: ['groupId', 'panelId', 'name'],
ttl: 60 * 60, // 1 hour
},
});
this.registerAction('savePanelData', this.savePanelData, {
params: {
@ -60,6 +72,16 @@ class GroupExtraService extends TcService {
}>
) {
const { groupId, panelId, name, data } = ctx.params;
const userId = ctx.meta.userId;
const [hasPermission] = await call(ctx).checkUserPermissions(
groupId,
userId,
[PERMISSION.core.managePanel]
);
if (!hasPermission) {
throw new NoPermissionError(t('没有操作权限'));
}
await this.adapter.model.findOneAndUpdate(
{
@ -75,8 +97,18 @@ class GroupExtraService extends TcService {
}
);
await this.cleanGroupPanelDataCache(groupId, panelId, name);
return true;
}
private cleanGroupPanelDataCache(
groupId: string,
panelId: string,
name: string
) {
return this.cleanActionCache('getPanelData', [groupId, panelId, name]);
}
}
export default GroupExtraService;

Loading…
Cancel
Save