Skip to content

Commit 53f8a7d

Browse files
Merge pull request #1643 from CapSoftware/anon-notifications
Add anonymous view notifications for non-logged-in viewers
2 parents 8d142b9 + 0223f5e commit 53f8a7d

File tree

22 files changed

+11810
-86
lines changed

22 files changed

+11810
-86
lines changed

apps/web/actions/notifications/update-preferences.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export const updatePreferences = async ({
1414
pauseReplies: boolean;
1515
pauseViews: boolean;
1616
pauseReactions: boolean;
17+
pauseAnonViews?: boolean;
1718
};
1819
}) => {
1920
const currentUser = await getCurrentUser();

apps/web/app/(org)/dashboard/_components/Notifications/Filter.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1-
import type { NotificationType } from "@/lib/Notification";
1+
import type { Notification } from "@cap/web-api-contract";
22

3-
export type FilterType = "all" | NotificationType;
3+
type NotificationType = Notification["type"];
4+
5+
export type FilterType = "all" | Exclude<NotificationType, "anon_view">;
46

57
export const Filters: Array<FilterType> = [
68
"all",
@@ -23,5 +25,6 @@ export const matchNotificationFilter = (
2325
type: NotificationType,
2426
): boolean => {
2527
if (filter === "all") return true;
28+
if (filter === "view") return type === "view" || type === "anon_view";
2629
return type === filter;
2730
};

apps/web/app/(org)/dashboard/_components/Notifications/FilterTabs.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
import clsx from "clsx";
22
import { motion } from "framer-motion";
33
import { useEffect, useMemo, useRef } from "react";
4-
import type { NotificationType } from "@/lib/Notification";
54
import { FilterLabels, Filters, type FilterType } from "./Filter";
65

76
type FilterTabsProps = {
87
activeFilter: FilterType;
98
setActiveFilter: (filter: FilterType) => void;
109
loading: boolean;
11-
count?: Record<NotificationType, number>;
10+
count?: Record<Exclude<FilterType, "all">, number>;
1211
};
1312

1413
export const FilterTabs = ({
@@ -49,9 +48,10 @@ export const FilterTabs = ({
4948
>
5049
{Filters.map((filter) => (
5150
<div key={filter} className="relative min-w-fit">
52-
<div
51+
<button
52+
type="button"
5353
onClick={() => setActiveFilter(filter)}
54-
className="flex relative gap-2 items-center py-4 cursor-pointer group"
54+
className="flex relative gap-2 items-center py-4 cursor-pointer group bg-transparent border-0"
5555
>
5656
<p
5757
className={clsx(
@@ -79,7 +79,7 @@ export const FilterTabs = ({
7979
</p>
8080
)}
8181
</div>
82-
</div>
82+
</button>
8383

8484
{/* Indicator */}
8585
{activeFilter === filter && (

apps/web/app/(org)/dashboard/_components/Notifications/NotificationItem.tsx

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,20 @@ import moment from "moment";
77
import Link from "next/link";
88
import { markAsRead } from "@/actions/notifications/mark-as-read";
99
import { SignedImageUrl } from "@/components/SignedImageUrl";
10-
import type { NotificationType } from "@/lib/Notification";
1110

1211
type NotificationItemProps = {
1312
notification: APINotification;
1413
className?: string;
1514
};
1615

16+
type NotificationType = APINotification["type"];
17+
1718
const descriptionMap: Record<NotificationType, string> = {
1819
comment: `commented on your video`,
1920
reply: `replied to your comment`,
2021
view: `viewed your video`,
2122
reaction: `reacted to your video`,
22-
// mention: `mentioned you in a comment`,
23+
anon_view: `viewed your video`,
2324
};
2425

2526
export const NotificationItem = ({
@@ -36,6 +37,12 @@ export const NotificationItem = ({
3637
}
3738
};
3839

40+
const isAnonView = notification.type === "anon_view";
41+
const displayName =
42+
notification.type === "anon_view"
43+
? notification.anonName
44+
: notification.author.name;
45+
3946
return (
4047
<Link
4148
href={link}
@@ -45,11 +52,14 @@ export const NotificationItem = ({
4552
className,
4653
)}
4754
>
48-
{/* Avatar */}
4955
<div className="relative flex-shrink-0">
5056
<SignedImageUrl
51-
image={notification.author.avatar as ImageUpload.ImageUrl | null}
52-
name={notification.author.name}
57+
image={
58+
isAnonView
59+
? null
60+
: (notification.author.avatar as ImageUpload.ImageUrl | null)
61+
}
62+
name={displayName}
5363
className="relative flex-shrink-0 size-7"
5464
letterClass="text-sm"
5565
/>
@@ -58,17 +68,21 @@ export const NotificationItem = ({
5868
)}
5969
</div>
6070

61-
{/* Content */}
6271
<div className="flex flex-col flex-1 justify-center">
6372
<div className="flex gap-1 items-center">
6473
<span className="font-medium text-gray-12 text-[13px]">
65-
{notification.author.name}
74+
{displayName}
6675
</span>
6776
<span className="text-gray-10 text-[13px]">
6877
{descriptionMap[notification.type]}
6978
</span>
7079
</div>
7180

81+
{isAnonView && notification.location && (
82+
<p className="text-[13px] leading-4 text-gray-11">
83+
{notification.location}
84+
</p>
85+
)}
7286
{(notification.type === "comment" || notification.type === "reply") && (
7387
<p className="mb-2 text-[13px] h-fit italic leading-4 text-gray-11 line-clamp-2">
7488
{notification.comment.content}
@@ -79,15 +93,15 @@ export const NotificationItem = ({
7993
</p>
8094
</div>
8195

82-
{/* Icon */}
8396
<div className="flex flex-shrink-0 items-center mt-1">
8497
{notification.type === "comment" && (
8598
<FontAwesomeIcon icon={faComment} className="text-gray-10 size-4" />
8699
)}
87100
{notification.type === "reply" && (
88101
<FontAwesomeIcon icon={faReply} className="text-gray-10 size-4" />
89102
)}
90-
{notification.type === "view" && (
103+
{(notification.type === "view" ||
104+
notification.type === "anon_view") && (
91105
<FontAwesomeIcon icon={faEye} className="text-gray-10 size-4" />
92106
)}
93107
{notification.type === "reaction" && (
@@ -104,8 +118,10 @@ function getLink(notification: APINotification) {
104118
return `/s/${notification.videoId}/?reply=${notification.comment.id}`;
105119
case "comment":
106120
case "reaction":
107-
// case "mention":
108121
return `/s/${notification.videoId}/?comment=${notification.comment.id}`;
122+
case "view":
123+
case "anon_view":
124+
return `/s/${notification.videoId}`;
109125
default:
110126
return `/s/${notification.videoId}`;
111127
}

apps/web/app/(org)/dashboard/_components/Notifications/SettingsDropdown.tsx

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
faCog,
66
faComment,
77
faEye,
8+
faEyeSlash,
89
faReply,
910
faThumbsUp,
1011
type IconDefinition,
@@ -20,13 +21,19 @@ import { useDashboardContext } from "../../Contexts";
2021
type NotificationOption = {
2122
icon: IconDefinition;
2223
label: string;
23-
value: "pauseComments" | "pauseViews" | "pauseReactions" | "pauseReplies";
24+
value:
25+
| "pauseComments"
26+
| "pauseViews"
27+
| "pauseReactions"
28+
| "pauseReplies"
29+
| "pauseAnonViews";
2430
};
2531

2632
const notificationOptions: NotificationOption[] = [
2733
{ icon: faComment, label: "Comments", value: "pauseComments" },
2834
{ icon: faReply, label: "Replies", value: "pauseReplies" },
2935
{ icon: faEye, label: "Views", value: "pauseViews" },
36+
{ icon: faEyeSlash, label: "Anonymous views", value: "pauseAnonViews" },
3037
{ icon: faThumbsUp, label: "Reactions", value: "pauseReactions" },
3138
];
3239

@@ -41,6 +48,7 @@ export const SettingsDropdown = () => {
4148
pauseReplies: false,
4249
pauseViews: false,
4350
pauseReactions: false,
51+
pauseAnonViews: false,
4452
};
4553

4654
await updatePreferences({
@@ -131,7 +139,7 @@ export const SettingsDropdown = () => {
131139
</p>
132140
</div>
133141

134-
{userPreferences?.notifications[option.value] && (
142+
{userPreferences?.notifications?.[option.value] && (
135143
<FontAwesomeIcon
136144
icon={faCheck}
137145
className="text-gray-10 size-2.5 transition-colors group-hover:text-gray-12"

0 commit comments

Comments
 (0)