feat: add realtime feed event and desc feed list

This commit is contained in:
moonrailgun 2024-06-29 08:46:11 +08:00
parent adb1cc3919
commit 478d0c2af3
4 changed files with 55 additions and 15 deletions

View File

@ -1,9 +1,4 @@
import { import { defaultErrorHandler, defaultSuccessHandler, trpc } from '@/api/trpc';
AppRouterOutput,
defaultErrorHandler,
defaultSuccessHandler,
trpc,
} from '@/api/trpc';
import { CommonHeader } from '@/components/CommonHeader'; import { CommonHeader } from '@/components/CommonHeader';
import { CommonWrapper } from '@/components/CommonWrapper'; import { CommonWrapper } from '@/components/CommonWrapper';
import { ScrollArea } from '@/components/ui/scroll-area'; import { ScrollArea } from '@/components/ui/scroll-area';
@ -19,6 +14,8 @@ import { FeedApiGuide } from '@/components/feed/FeedApiGuide';
import { FeedEventItem } from '@/components/feed/FeedEventItem'; import { FeedEventItem } from '@/components/feed/FeedEventItem';
import { FeedIntegration } from '@/components/feed/FeedIntegration'; import { FeedIntegration } from '@/components/feed/FeedIntegration';
import { DialogWrapper } from '@/components/DialogWrapper'; import { DialogWrapper } from '@/components/DialogWrapper';
import { useSocketSubscribeList } from '@/api/socketio';
import { useMemo } from 'react';
export const Route = createFileRoute('/feed/$channelId/')({ export const Route = createFileRoute('/feed/$channelId/')({
beforeLoad: routeAuthBeforeLoad, beforeLoad: routeAuthBeforeLoad,
@ -33,7 +30,7 @@ function PageComponent() {
workspaceId, workspaceId,
channelId, channelId,
}); });
const { data: events } = trpc.feed.events.useQuery({ const { data: events = [] } = trpc.feed.events.useQuery({
workspaceId, workspaceId,
channelId, channelId,
}); });
@ -53,6 +50,15 @@ function PageComponent() {
}); });
}); });
const realtimeEvents = useSocketSubscribeList('onReceiveFeedEvent', {
filter: (event) => event.channelId === channelId,
});
const fullEvents = useMemo(
() => [...realtimeEvents, ...events],
[realtimeEvents, events]
);
return ( return (
<CommonWrapper <CommonWrapper
header={ header={
@ -96,14 +102,14 @@ function PageComponent() {
/> />
} }
> >
{events && events.length === 0 ? ( {fullEvents && fullEvents.length === 0 ? (
<div className="w-full overflow-hidden p-4"> <div className="w-full overflow-hidden p-4">
<FeedApiGuide channelId={channelId} /> <FeedApiGuide channelId={channelId} />
</div> </div>
) : ( ) : (
<ScrollArea className="h-full overflow-hidden p-4"> <ScrollArea className="h-full overflow-hidden p-4">
<div className="space-y-2"> <div className="space-y-2">
{(events ?? []).map((event) => ( {(fullEvents ?? []).map((event) => (
<FeedEventItem key={event.id} event={event} /> <FeedEventItem key={event.id} event={event} />
))} ))}
</div> </div>

View File

@ -11,6 +11,7 @@ import { OpenApiMeta } from 'trpc-openapi';
import { FeedChannelModelSchema, FeedEventModelSchema } from '../../prisma/zod'; import { FeedChannelModelSchema, FeedEventModelSchema } from '../../prisma/zod';
import { prisma } from '../../model/_client'; import { prisma } from '../../model/_client';
import _ from 'lodash'; import _ from 'lodash';
import { subscribeEventBus } from '../../ws/shared';
export const feedIntegrationRouter = router({ export const feedIntegrationRouter = router({
github: publicProcedure github: publicProcedure
@ -32,6 +33,21 @@ export const feedIntegrationRouter = router({
const eventType = ctx.req.headers['x-github-event']; const eventType = ctx.req.headers['x-github-event'];
const { channelId, ...data } = input; const { channelId, ...data } = input;
const workspaceId = await prisma.feedChannel
.findFirst({
where: {
id: channelId,
},
select: {
workspaceId: true,
},
})
.then((res) => res?.workspaceId);
if (!workspaceId) {
return 'Not found';
}
if (eventType === 'push') { if (eventType === 'push') {
const pusher = `${_.get(data, 'pusher.name')}<${_.get(data, 'pusher.email')}>`; const pusher = `${_.get(data, 'pusher.name')}<${_.get(data, 'pusher.email')}>`;
const commits = _.map(_.get(data, 'commits') as any[], 'id').join(', '); const commits = _.map(_.get(data, 'commits') as any[], 'id').join(', ');
@ -40,7 +56,7 @@ export const feedIntegrationRouter = router({
const senderId = String(_.get(data, 'sender.id')); const senderId = String(_.get(data, 'sender.id'));
const senderName = String(_.get(data, 'sender.login')); const senderName = String(_.get(data, 'sender.login'));
const url = String(_.get(data, 'compare')); const url = String(_.get(data, 'compare'));
await prisma.feedEvent.create({ const event = await prisma.feedEvent.create({
data: { data: {
channelId: channelId, channelId: channelId,
eventName: eventType, eventName: eventType,
@ -53,6 +69,7 @@ export const feedIntegrationRouter = router({
url, url,
}, },
}); });
subscribeEventBus.emit('onReceiveFeedEvent', workspaceId, event);
return 'ok'; return 'ok';
} else if (eventType === 'star') { } else if (eventType === 'star') {
@ -61,7 +78,7 @@ export const feedIntegrationRouter = router({
const senderId = String(_.get(data, 'sender.id')); const senderId = String(_.get(data, 'sender.id'));
const senderName = String(_.get(data, 'sender.login')); const senderName = String(_.get(data, 'sender.login'));
const url = String(_.get(data, 'compare')); const url = String(_.get(data, 'compare'));
await prisma.feedEvent.create({ const event = await prisma.feedEvent.create({
data: { data: {
channelId: channelId, channelId: channelId,
eventName: eventType, eventName: eventType,
@ -74,6 +91,7 @@ export const feedIntegrationRouter = router({
url, url,
}, },
}); });
subscribeEventBus.emit('onReceiveFeedEvent', workspaceId, event);
return 'ok'; return 'ok';
} else if (eventType === 'issues') { } else if (eventType === 'issues') {
@ -96,7 +114,7 @@ export const feedIntegrationRouter = router({
} }
if (eventContent) { if (eventContent) {
await prisma.feedEvent.create({ const event = await prisma.feedEvent.create({
data: { data: {
channelId: channelId, channelId: channelId,
eventName: eventName, eventName: eventName,
@ -109,6 +127,7 @@ export const feedIntegrationRouter = router({
url, url,
}, },
}); });
subscribeEventBus.emit('onReceiveFeedEvent', workspaceId, event);
return 'ok'; return 'ok';
} }
@ -235,6 +254,10 @@ export const feedRouter = router({
where: { where: {
channelId: channelId, channelId: channelId,
}, },
take: 50,
orderBy: {
createdAt: 'desc',
},
}); });
return events; return events;

View File

@ -0,0 +1,9 @@
type NestedSwapDatesWithStrings<T> = {
[k in keyof T]: T[k] extends Date | undefined
? string
: T[k] extends object
? NestedSwapDatesWithStrings<T[k]>
: T[k];
};
export type Serialize<T> = NestedSwapDatesWithStrings<T>;

View File

@ -1,13 +1,15 @@
import { MonitorData } from '@prisma/client';
import { EventEmitter } from 'eventemitter-strict'; import { EventEmitter } from 'eventemitter-strict';
import { Socket } from 'socket.io'; import { Socket } from 'socket.io';
import { MaybePromise, ServerStatusInfo } from '../../types'; import { MaybePromise, ServerStatusInfo } from '../../types';
import { FeedEvent, MonitorData } from '@prisma/client';
import { Serialize } from '../types/utils';
type SubscribeEventFn<T> = (workspaceId: string, eventData: T) => void; type SubscribeEventFn<T> = (workspaceId: string, eventData: T) => void;
export interface SubscribeEventMap { export interface SubscribeEventMap {
onServerStatusUpdate: SubscribeEventFn<Record<string, ServerStatusInfo>>; onServerStatusUpdate: SubscribeEventFn<Record<string, ServerStatusInfo>>;
onMonitorReceiveNewData: SubscribeEventFn<MonitorData>; onMonitorReceiveNewData: SubscribeEventFn<MonitorData>;
onReceiveFeedEvent: SubscribeEventFn<Serialize<FeedEvent>>;
} }
type SocketEventFn<T, U = unknown> = ( type SocketEventFn<T, U = unknown> = (
@ -29,14 +31,14 @@ export const socketEventBus = new EventEmitter<SocketEventMap>();
export const subscribeEventBus = new EventEmitter<SubscribeEventMap>(); export const subscribeEventBus = new EventEmitter<SubscribeEventMap>();
type SubscribeInitializerFn< type SubscribeInitializerFn<
T extends keyof SubscribeEventMap = keyof SubscribeEventMap T extends keyof SubscribeEventMap = keyof SubscribeEventMap,
> = ( > = (
workspaceId: string, workspaceId: string,
socket: Socket socket: Socket
) => MaybePromise<Parameters<SubscribeEventMap[T]>[1]> | MaybePromise<void>; ) => MaybePromise<Parameters<SubscribeEventMap[T]>[1]> | MaybePromise<void>;
const subscribeInitializerList: [ const subscribeInitializerList: [
keyof SubscribeEventMap, keyof SubscribeEventMap,
SubscribeInitializerFn SubscribeInitializerFn,
][] = []; ][] = [];
let i = 0; let i = 0;