feat: add realtime feed event and desc feed list
This commit is contained in:
parent
adb1cc3919
commit
478d0c2af3
@ -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>
|
||||||
|
@ -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;
|
||||||
|
9
src/server/types/utils.ts
Normal file
9
src/server/types/utils.ts
Normal 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>;
|
@ -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;
|
||||||
|
Loading…
Reference in New Issue
Block a user