+export const FeedEventItem: React.FC<{
+ className?: string;
+ event: FeedEventItemType;
+}> = React.memo(({ className, event }) => {
+ return (
+
+
+
+
+
-
-
-
{event.source}
-
-
{event.eventName}
-
- {event.tags.map((tag) => (
-
{tag}
- ))}
+
+
-
-
- {dayjs(event.createdAt).fromNow()}
-
-
- {dayjs(event.createdAt).format('YYYY-MM-DD HH:mm:ss')}
-
-
+
+ {event.source}
+
+ {event.eventName}
+
+ {event.tags.map((tag) => (
+ {tag}
+ ))}
+
- );
- }
-);
+
+
+
+ {dayjs(event.createdAt).fromNow()}
+
+
+ {dayjs(event.createdAt).format('YYYY-MM-DD HH:mm:ss')}
+
+
+
+ );
+});
FeedEventItem.displayName = 'FeedEventItem';
diff --git a/src/client/routes/feed/$channelId/index.tsx b/src/client/routes/feed/$channelId/index.tsx
index a010486..e666684 100644
--- a/src/client/routes/feed/$channelId/index.tsx
+++ b/src/client/routes/feed/$channelId/index.tsx
@@ -1,7 +1,6 @@
import { defaultErrorHandler, defaultSuccessHandler, trpc } from '@/api/trpc';
import { CommonHeader } from '@/components/CommonHeader';
import { CommonWrapper } from '@/components/CommonWrapper';
-import { ScrollArea } from '@/components/ui/scroll-area';
import { useCurrentWorkspaceId } from '@/store/user';
import { routeAuthBeforeLoad } from '@/utils/route';
import { useTranslation } from '@i18next-toolkit/react';
@@ -16,6 +15,7 @@ import { FeedIntegration } from '@/components/feed/FeedIntegration';
import { DialogWrapper } from '@/components/DialogWrapper';
import { useSocketSubscribeList } from '@/api/socketio';
import { useMemo } from 'react';
+import { SimpleVirtualList } from '@/components/SimpleVirtualList';
export const Route = createFileRoute('/feed/$channelId/')({
beforeLoad: routeAuthBeforeLoad,
@@ -30,10 +30,18 @@ function PageComponent() {
workspaceId,
channelId,
});
- const { data: events = [] } = trpc.feed.events.useQuery({
- workspaceId,
- channelId,
- });
+
+ const { data, hasNextPage, fetchNextPage, isFetchingNextPage } =
+ trpc.feed.fetchEventsByCursor.useInfiniteQuery(
+ {
+ workspaceId,
+ channelId,
+ },
+ {
+ getNextPageParam: (lastPage) => lastPage.nextCursor,
+ }
+ );
+
const deleteMutation = trpc.feed.deleteChannel.useMutation({
onSuccess: defaultSuccessHandler,
onError: defaultErrorHandler,
@@ -55,8 +63,8 @@ function PageComponent() {
});
const fullEvents = useMemo(
- () => [...realtimeEvents, ...events],
- [realtimeEvents, events]
+ () => [...realtimeEvents, ...(data?.pages.flatMap((p) => p.items) ?? [])],
+ [realtimeEvents, data]
);
return (
@@ -102,19 +110,21 @@ function PageComponent() {
/>
}
>
- {fullEvents && fullEvents.length === 0 ? (
-
-
-
- ) : (
-
-
- {(fullEvents ?? []).map((event) => (
-
- ))}
-
-
- )}
+
+
}
+ renderEmpty={() => (
+
+
+
+ )}
+ />
+
);
}
diff --git a/src/server/trpc/routers/feed/index.ts b/src/server/trpc/routers/feed/index.ts
index fc9a92e..b5894ef 100644
--- a/src/server/trpc/routers/feed/index.ts
+++ b/src/server/trpc/routers/feed/index.ts
@@ -15,6 +15,7 @@ import {
import { prisma } from '../../../model/_client';
import _ from 'lodash';
import { buildFeedPublicOpenapi, feedIntegrationRouter } from './integration';
+import { fetchDataByCursor } from '../../../utils/prisma';
export const feedRouter = router({
channels: workspaceProcedure
@@ -113,33 +114,42 @@ export const feedRouter = router({
return channel;
}),
- events: workspaceProcedure
+ fetchEventsByCursor: workspaceProcedure
.meta(
buildFeedOpenapi({
method: 'GET',
- path: '/{channelId}/events',
+ path: '/{channelId}/fetchEventsByCursor',
+ description: 'Fetch workspace feed channel events',
})
)
.input(
z.object({
channelId: z.string(),
+ limit: z.number().min(1).max(100).default(50),
+ cursor: z.string().optional(),
+ })
+ )
+ .output(
+ z.object({
+ items: z.array(FeedEventModelSchema),
+ nextCursor: z.string().optional(),
})
)
- .output(z.array(FeedEventModelSchema))
.query(async ({ input }) => {
- const { channelId } = input;
+ const { channelId, cursor, limit } = input;
- const events = await prisma.feedEvent.findMany({
+ const { items, nextCursor } = await fetchDataByCursor(prisma.feedEvent, {
where: {
- channelId: channelId,
- },
- take: 50,
- orderBy: {
- createdAt: 'desc',
+ channelId,
},
+ limit,
+ cursor,
});
- return events;
+ return {
+ items,
+ nextCursor,
+ };
}),
createChannel: workspaceOwnerProcedure
.meta(