-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Expand file tree
/
Copy pathNotificationItem.tsx
More file actions
126 lines (116 loc) · 3.8 KB
/
NotificationItem.tsx
File metadata and controls
126 lines (116 loc) · 3.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
import type { Notification as APINotification } from "@cap/web-api-contract";
import type { ImageUpload } from "@cap/web-domain";
import { faComment, faEye, faReply } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import clsx from "clsx";
import moment from "moment";
import Link from "next/link";
import { markAsRead } from "@/actions/notifications/mark-as-read";
import { SignedImageUrl } from "@/components/SignedImageUrl";
import type { NotificationType } from "@/lib/Notification";
type NotificationItemProps = {
notification: APINotification;
className?: string;
};
const descriptionMap: Record<NotificationType, string> = {
comment: `commented on your video`,
reply: `replied to your comment`,
view: `viewed your video`,
reaction: `reacted to your video`,
anon_view: `viewed your video`,
};
export const NotificationItem = ({
notification,
className,
}: NotificationItemProps) => {
const link = getLink(notification);
const markAsReadHandler = async () => {
try {
await markAsRead(notification.id);
} catch (error) {
console.error("Error marking notification as read:", error);
}
};
const isAnonView = notification.type === "anon_view";
const displayName = isAnonView
? notification.anonName
: notification.author.name;
return (
<Link
href={link}
onClick={markAsReadHandler}
className={clsx(
"flex gap-3 p-4 transition-colors cursor-pointer min-h-fit border-gray-3 hover:bg-gray-2",
className,
)}
>
<div className="relative flex-shrink-0">
{isAnonView ? (
<div className="relative flex-shrink-0 size-7 rounded-full bg-gray-3 flex items-center justify-center text-sm">
🐾
</div>
) : (
<SignedImageUrl
image={notification.author.avatar as ImageUpload.ImageUrl | null}
name={notification.author.name}
className="relative flex-shrink-0 size-7"
letterClass="text-sm"
/>
)}
{notification.readAt === null && (
<div className="absolute top-0 right-0 size-2.5 rounded-full bg-red-500 border-2 border-gray-1"></div>
)}
</div>
<div className="flex flex-col flex-1 justify-center">
<div className="flex gap-1 items-center">
<span className="font-medium text-gray-12 text-[13px]">
{displayName}
</span>
<span className="text-gray-10 text-[13px]">
{descriptionMap[notification.type]}
</span>
</div>
{isAnonView && notification.location && (
<p className="text-[13px] leading-4 text-gray-11">
{notification.location}
</p>
)}
{(notification.type === "comment" || notification.type === "reply") && (
<p className="mb-2 text-[13px] h-fit italic leading-4 text-gray-11 line-clamp-2">
{notification.comment.content}
</p>
)}
<p className="text-xs text-gray-10">
{moment(notification.createdAt).fromNow()}
</p>
</div>
<div className="flex flex-shrink-0 items-center mt-1">
{notification.type === "comment" && (
<FontAwesomeIcon icon={faComment} className="text-gray-10 size-4" />
)}
{notification.type === "reply" && (
<FontAwesomeIcon icon={faReply} className="text-gray-10 size-4" />
)}
{(notification.type === "view" ||
notification.type === "anon_view") && (
<FontAwesomeIcon icon={faEye} className="text-gray-10 size-4" />
)}
{notification.type === "reaction" && (
<span className="text-xl">{notification.comment.content}</span>
)}
</div>
</Link>
);
};
function getLink(notification: APINotification) {
switch (notification.type) {
case "reply":
return `/s/${notification.videoId}/?reply=${notification.comment.id}`;
case "comment":
case "reaction":
return `/s/${notification.videoId}/?comment=${notification.comment.id}`;
case "view":
case "anon_view":
return `/s/${notification.videoId}`;
}
}