feat: add archive feature
This commit is contained in:
parent
33de808f3e
commit
3270164710
@ -1,4 +1,4 @@
|
||||
import { AppRouterOutput } from '@/api/trpc';
|
||||
import { AppRouterOutput, trpc } from '@/api/trpc';
|
||||
import React from 'react';
|
||||
import { Badge } from '../ui/badge';
|
||||
import dayjs from 'dayjs';
|
||||
@ -6,6 +6,12 @@ import { Tooltip, TooltipContent, TooltipTrigger } from '../ui/tooltip';
|
||||
import { MarkdownViewer } from '../MarkdownEditor';
|
||||
import { FeedIcon } from './FeedIcon';
|
||||
import { cn } from '@/utils/style';
|
||||
import { Button } from '../ui/button';
|
||||
import { LuArchive } from 'react-icons/lu';
|
||||
import { useCurrentWorkspaceId } from '@/store/user';
|
||||
import { useEvent } from '@/hooks/useEvent';
|
||||
import { toast } from 'sonner';
|
||||
import { useTranslation } from '@i18next-toolkit/react';
|
||||
|
||||
type FeedEventItemType =
|
||||
AppRouterOutput['feed']['fetchEventsByCursor']['items'][number];
|
||||
@ -14,6 +20,21 @@ export const FeedEventItem: React.FC<{
|
||||
className?: string;
|
||||
event: FeedEventItemType;
|
||||
}> = React.memo(({ className, event }) => {
|
||||
const workspaceId = useCurrentWorkspaceId();
|
||||
const archiveEventMutation = trpc.feed.archiveEvent.useMutation();
|
||||
const trpcUtils = trpc.useUtils();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const handleArchive = useEvent(async () => {
|
||||
await archiveEventMutation.mutateAsync({
|
||||
workspaceId,
|
||||
channelId: event.channelId,
|
||||
eventId: event.id,
|
||||
});
|
||||
trpcUtils.feed.fetchEventsByCursor.refetch();
|
||||
toast.success(t('Event archived'));
|
||||
});
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
@ -21,16 +42,26 @@ export const FeedEventItem: React.FC<{
|
||||
className
|
||||
)}
|
||||
>
|
||||
<div className="flex-1 gap-2 overflow-hidden">
|
||||
<div className="relative flex-1 gap-2 overflow-hidden">
|
||||
<div className="mb-2 flex w-full items-center gap-2 overflow-hidden text-sm">
|
||||
<div className="border-muted rounded-lg border p-2">
|
||||
<FeedIcon source={event.source} size={24} />
|
||||
</div>
|
||||
<div>
|
||||
|
||||
<div className="overflow-hidden">
|
||||
<MarkdownViewer value={event.eventContent} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
size="icon"
|
||||
variant="secondary"
|
||||
className="absolute right-0 top-0 h-6 w-6 overflow-hidden"
|
||||
onClick={handleArchive}
|
||||
>
|
||||
<LuArchive size={12} />
|
||||
</Button>
|
||||
|
||||
<div className="flex justify-between">
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<Badge>{event.source}</Badge>
|
||||
@ -46,7 +77,7 @@ export const FeedEventItem: React.FC<{
|
||||
<TooltipTrigger className="cursor-default self-end text-xs opacity-60">
|
||||
<div>{dayjs(event.createdAt).fromNow()}</div>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<TooltipContent side="left">
|
||||
<p>{dayjs(event.createdAt).format('YYYY-MM-DD HH:mm:ss')}</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
|
@ -0,0 +1,2 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "FeedEvent" ADD COLUMN "archived" BOOLEAN NOT NULL DEFAULT false;
|
@ -531,6 +531,7 @@ model FeedEvent {
|
||||
senderName String? // use for display who
|
||||
url String? // url link
|
||||
important Boolean
|
||||
archived Boolean @default(false) @db.Boolean
|
||||
|
||||
channel FeedChannel @relation(fields: [channelId], references: [id], onUpdate: Cascade, onDelete: Cascade)
|
||||
|
||||
|
@ -15,6 +15,7 @@ export const FeedEventModelSchema = z.object({
|
||||
senderName: z.string().nullish(),
|
||||
url: z.string().nullish(),
|
||||
important: z.boolean(),
|
||||
archived: z.boolean(),
|
||||
})
|
||||
|
||||
export interface CompleteFeedEvent extends z.infer<typeof FeedEventModelSchema> {
|
||||
|
@ -187,6 +187,7 @@ export const feedRouter = router({
|
||||
const { items, nextCursor } = await fetchDataByCursor(prisma.feedEvent, {
|
||||
where: {
|
||||
channelId,
|
||||
archived: false,
|
||||
},
|
||||
limit,
|
||||
cursor,
|
||||
@ -307,6 +308,60 @@ export const feedRouter = router({
|
||||
|
||||
return event;
|
||||
}),
|
||||
archiveEvent: workspaceOwnerProcedure
|
||||
.meta(
|
||||
buildFeedPublicOpenapi({
|
||||
method: 'PATCH',
|
||||
path: '/{channelId}/{eventId}/archive',
|
||||
})
|
||||
)
|
||||
.input(
|
||||
z.object({
|
||||
channelId: z.string(),
|
||||
eventId: z.string(),
|
||||
})
|
||||
)
|
||||
.output(z.void())
|
||||
.mutation(async ({ input }) => {
|
||||
const { channelId, eventId } = input;
|
||||
|
||||
await prisma.feedEvent.update({
|
||||
data: {
|
||||
archived: true,
|
||||
},
|
||||
where: {
|
||||
id: eventId,
|
||||
channelId,
|
||||
},
|
||||
});
|
||||
}),
|
||||
unarchiveEvent: workspaceOwnerProcedure
|
||||
.meta(
|
||||
buildFeedPublicOpenapi({
|
||||
method: 'PATCH',
|
||||
path: '/{channelId}/{eventId}/unarchive',
|
||||
})
|
||||
)
|
||||
.input(
|
||||
z.object({
|
||||
channelId: z.string(),
|
||||
eventId: z.string(),
|
||||
})
|
||||
)
|
||||
.output(z.void())
|
||||
.mutation(async ({ input }) => {
|
||||
const { channelId, eventId } = input;
|
||||
|
||||
await prisma.feedEvent.update({
|
||||
data: {
|
||||
archived: false,
|
||||
},
|
||||
where: {
|
||||
id: eventId,
|
||||
channelId,
|
||||
},
|
||||
});
|
||||
}),
|
||||
integration: feedIntegrationRouter,
|
||||
});
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user