feat: feed add markdown support

This commit is contained in:
moonrailgun 2024-06-30 16:53:09 +08:00
parent 4d15cccd1b
commit 56bbe09005
9 changed files with 69 additions and 21 deletions

View File

@ -311,6 +311,9 @@ importers:
react-router-dom:
specifier: ^6.15.0
version: 6.15.0(react-dom@18.2.0)(react@18.2.0)
rehype-external-links:
specifier: ^3.0.0
version: 3.0.0
socket.io-client:
specifier: ^4.7.2
version: 4.7.2
@ -5013,7 +5016,6 @@ packages:
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
libc: [glibc]
requiresBuild: true
dev: false
optional: true
@ -5023,7 +5025,6 @@ packages:
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
libc: [musl]
requiresBuild: true
dev: false
optional: true
@ -5033,7 +5034,6 @@ packages:
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
libc: [glibc]
requiresBuild: true
dev: false
optional: true
@ -5043,7 +5043,6 @@ packages:
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
libc: [musl]
requiresBuild: true
dev: false
optional: true
@ -13642,6 +13641,12 @@ packages:
vfile-location: 5.0.2
web-namespaces: 2.0.1
/hast-util-is-element@3.0.0:
resolution: {integrity: sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==}
dependencies:
'@types/hast': 3.0.4
dev: false
/hast-util-parse-selector@3.1.1:
resolution: {integrity: sha512-jdlwBjEexy1oGz0aJ2f4GKMaVKkA9jwjr4MjAAI22E5fM/TXVZHuS5OpONtdeIkRKqAaryQ2E9xNQxijoThSZA==}
dependencies:
@ -14330,6 +14335,11 @@ packages:
resolution: {integrity: sha512-QGOS8MRMnj/UiOa+aMIgfyHcvkhqNUsUxb1XzskENvbo+rEfp6TOwqd1KPuDzXC4OnGHcMSVxDGRoilqB8ViqA==}
dev: false
/is-absolute-url@4.0.1:
resolution: {integrity: sha512-/51/TKE88Lmm7Gc4/8btclNXWS+g50wXhYJq8HWIBAGUBnoAdRu1aXeh364t/O7wXDAcTJDP8PNuNKWUDWie+A==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
dev: false
/is-absolute@1.0.0:
resolution: {integrity: sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==}
engines: {node: '>=0.10.0'}
@ -21072,6 +21082,17 @@ packages:
resolution: {integrity: sha512-bEAtp/qrtKucxXSJkD4ebopFZYP0q1+3Vb2WECWv/T8yQEgKxDxJ7ztO285tAMaYZVR6mM1GgI6CCn8FROtL1w==}
dev: false
/rehype-external-links@3.0.0:
resolution: {integrity: sha512-yp+e5N9V3C6bwBeAC4n796kc86M4gJCdlVhiMTxIrJG5UHDMh+PJANf9heqORJbt1nrCbDwIlAZKjANIaVBbvw==}
dependencies:
'@types/hast': 3.0.4
'@ungap/structured-clone': 1.2.0
hast-util-is-element: 3.0.0
is-absolute-url: 4.0.1
space-separated-tokens: 2.0.2
unist-util-visit: 5.0.0
dev: false
/rehype-raw@6.1.1:
resolution: {integrity: sha512-d6AKtisSRtDRX4aSPsJGTfnzrX2ZkHQLE5kiUuGOeEoLpbEulFF4hj0mLPbsa+7vmguDKOVVEQdHKDSwoaIDsQ==}
dependencies:

View File

@ -1,8 +1,6 @@
import React from 'react';
import { Editor, EditorProps } from '@bytemd/react';
import { plugins } from './plugins';
import 'bytemd/dist/index.css';
import './style.less';
import { useLocale } from './useLocale';
interface MarkdownEditorProps extends EditorProps {}

View File

@ -1,4 +1,6 @@
import loadable from '@loadable/component';
import 'bytemd/dist/index.css';
import './style.less';
export const MarkdownEditor = loadable(() =>
import('./editor').then((module) => module.MarkdownEditor)

View File

@ -1,6 +1,13 @@
import gfm from '@bytemd/plugin-gfm';
import { BytemdPlugin } from 'bytemd';
import externalLinks from 'rehype-external-links';
export const plugins = [
export const plugins: BytemdPlugin[] = [
gfm(),
// Add more plugins here
{
rehype: (p) =>
p.use(externalLinks, {
target: '_blank',
}),
},
];

View File

@ -7,12 +7,20 @@
z-index: 99;
}
.markdown {
.markdown-body {
a {
text-decoration: underline dotted;
}
}
}
.dark {
.bytemd {
@apply bg-zinc-900 border-zinc-800 text-foreground;
@apply text-foreground border-zinc-800 bg-zinc-900;
.bytemd-toolbar {
@apply bg-zinc-900 border-zinc-800;
@apply border-zinc-800 bg-zinc-900;
.bytemd-toolbar-icon:hover {
@apply bg-muted;
@ -24,7 +32,7 @@
}
.CodeMirror {
@apply bg-zinc-900 text-foreground;
@apply text-foreground bg-zinc-900;
.CodeMirror-cursor {
@apply border-foreground;

View File

@ -1,15 +1,17 @@
import React from 'react';
import { Viewer } from '@bytemd/react';
import { plugins } from './plugins';
import 'bytemd/dist/index.css';
import './style.less';
interface MarkdownViewerProps {
value: string;
}
export const MarkdownViewer: React.FC<MarkdownViewerProps> = React.memo(
(props) => {
return <Viewer plugins={plugins} value={props.value ?? ''} />;
return (
<div className="markdown">
<Viewer plugins={plugins} value={props.value ?? ''} />
</div>
);
}
);
MarkdownViewer.displayName = 'MarkdownViewer';

View File

@ -3,6 +3,7 @@ import React from 'react';
import { Badge } from '../ui/badge';
import dayjs from 'dayjs';
import { Tooltip, TooltipContent, TooltipTrigger } from '../ui/tooltip';
import { MarkdownViewer } from '../MarkdownEditor';
type FeedEventItemType = AppRouterOutput['feed']['events'][number];
@ -12,7 +13,9 @@ export const FeedEventItem: React.FC<{ event: FeedEventItemType }> = React.memo(
<div className="border-muted flex items-center rounded-lg border px-4 py-2">
<div className="flex-1 gap-2">
<div className="mb-2 flex gap-2">
<div>{event.eventContent}</div>
<div>
<MarkdownViewer value={event.eventContent} />
</div>
</div>
<div className="flex flex-wrap gap-2">

View File

@ -87,6 +87,7 @@
"react-resizable-panels": "^2.0.12",
"react-router": "^6.15.0",
"react-router-dom": "^6.15.0",
"rehype-external-links": "^3.0.0",
"socket.io-client": "^4.7.2",
"sonner": "^1.4.3",
"str2int": "^1.1.0",
@ -99,10 +100,10 @@
"devDependencies": {
"@i18next-toolkit/cli": "^1.2.2",
"@tanstack/router-vite-plugin": "^1.20.5",
"@types/jsonexport": "^3.0.5",
"@types/leaflet": "^1.9.8",
"@types/loadable__component": "^5.13.8",
"@types/lodash-es": "^4.17.12",
"@types/jsonexport": "^3.0.5",
"@types/react": "^18.2.22",
"@types/react-dom": "^18.2.7",
"@types/react-grid-layout": "^1.3.5",

View File

@ -43,9 +43,11 @@ export const feedIntegrationRouter = router({
}
if (eventType === 'push') {
const pusher = `${_.get(data, 'pusher.name')}<${_.get(data, 'pusher.email')}>`;
const pusherName = _.get(data, 'pusher.name');
const pusherEmail = _.get(data, 'pusher.email');
const commits = _.map(_.get(data, 'commits') as any[], 'id').join(', ');
const fullName = _.get(data, 'repository.full_name');
const repoUrl = _.get(data, 'repository.html_url');
const ref = String(_.get(data, 'ref'));
const senderId = String(_.get(data, 'sender.id'));
const senderName = String(_.get(data, 'sender.login'));
@ -54,7 +56,7 @@ export const feedIntegrationRouter = router({
data: {
channelId: channelId,
eventName: eventType,
eventContent: `${pusher} push commit ${commits} to [${ref}] in ${fullName}`,
eventContent: `[${pusherName}](${pusherEmail}) push commit **${commits}** to [${ref}] in [${fullName}](${repoUrl})`,
tags: [],
source: 'github',
senderId,
@ -69,10 +71,13 @@ export const feedIntegrationRouter = router({
serializeJSON(event)
);
console.log('serializeJSON(event)', serializeJSON(event));
return 'ok';
} else if (eventType === 'star') {
const starCount = _.get(data, 'repository.stargazers_count');
const fullName = _.get(data, 'repository.full_name');
const repoUrl = _.get(data, 'repository.html_url');
const senderId = String(_.get(data, 'sender.id'));
const senderName = String(_.get(data, 'sender.login'));
const url = String(_.get(data, 'compare'));
@ -80,7 +85,7 @@ export const feedIntegrationRouter = router({
data: {
channelId: channelId,
eventName: eventType,
eventContent: `${senderName} star repo [${fullName}], now is ${starCount}.`,
eventContent: `${senderName} star repo [${fullName}](${repoUrl}), now is ${starCount}.`,
tags: [],
source: 'github',
senderId,
@ -100,6 +105,7 @@ export const feedIntegrationRouter = router({
const action = _.get(data, 'action') as 'opened' | 'closed';
const starCount = _.get(data, 'repository.stargazers_count');
const fullName = _.get(data, 'repository.full_name');
const repoUrl = _.get(data, 'repository.html_url');
const senderId = String(_.get(data, 'sender.id'));
const senderName = String(_.get(data, 'sender.login'));
const url = String(_.get(data, 'issue.url'));
@ -109,10 +115,10 @@ export const feedIntegrationRouter = router({
let eventContent = '';
if (action === 'opened') {
eventName = 'open_issue';
eventContent = `${senderName} open issue [${title}] in repo [${fullName}]`;
eventContent = `${senderName} open issue [${title}] in repo [${fullName}](${repoUrl})`;
} else if (action === 'closed') {
eventName = 'close_issue';
eventContent = `${senderName} close issue [${title}] in repo [${fullName}]`;
eventContent = `${senderName} close issue [${title}] in repo [${fullName}](${repoUrl})`;
}
if (eventContent) {