Skip to content

Commit

Permalink
Notifications
Browse files Browse the repository at this point in the history
  • Loading branch information
tom-sherman committed Nov 3, 2024
1 parent 9a8e5cd commit 8410293
Show file tree
Hide file tree
Showing 32 changed files with 894 additions and 260 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
"use client";

import useSWR from "swr";
import { NotificationCountKey } from "./notification-indicator-shared";

async function fetchNotificationCount() {
const response = await fetch("/api/notification-count");
if (!response.ok) {
throw new Error("Failed to fetch notification count");
}
const count = await response.json();
if (typeof count !== "number") {
throw new Error("Invalid notification count");
}
return count;
}

export function NotificationIndicatorCount() {
const { data: count } = useSWR(NotificationCountKey, fetchNotificationCount, {
suspense: true,
revalidateOnMount: false,
});

if (count === 0) return null;

return (
<div
className="absolute -top-1 -right-1 w-4 h-4 bg-red-500 text-white text-xs rounded-full flex items-center justify-center"
aria-label={`${count} notifications.`}
>
{count > 9 ? "9+" : count}
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const NotificationCountKey = "notificationCount";
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { ReactNode, Suspense } from "react";
import { NotificationIndicatorCount } from "./notification-indicator-client";
import { SWRConfig } from "swr";
import { getUser } from "@/lib/data/user";
import { getNotificationCount } from "@/lib/data/db/notification";
import { ErrorBoundary } from "react-error-boundary";
import { NotificationCountKey } from "./notification-indicator-shared";

export async function NotificationIndicator({
children,
}: {
children: ReactNode;
}) {
const user = await getUser();
if (user === null) return null;
return (
<div className="relative">
{children}
<ErrorBoundary fallback={null}>
<Suspense>
<NotificationIndicatorInner />
</Suspense>
</ErrorBoundary>
</div>
);
}

function NotificationIndicatorInner() {
return (
<SWRConfig
value={{
fallback: {
[NotificationCountKey]: getNotificationCount(),
},
}}
>
<NotificationIndicatorCount />
</SWRConfig>
);
}
51 changes: 0 additions & 51 deletions packages/frontpage/app/(app)/_components/post-list.tsx

This file was deleted.

63 changes: 25 additions & 38 deletions packages/frontpage/app/(app)/_components/theme-toggle.tsx
Original file line number Diff line number Diff line change
@@ -1,49 +1,36 @@
"use client";

import * as React from "react";
import { SunIcon, MoonIcon } from "@radix-ui/react-icons";
import { SunIcon, MoonIcon, Half2Icon } from "@radix-ui/react-icons";
import { useTheme } from "next-themes";

import { Button } from "@/lib/components/ui/button";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
DropdownMenuLabel,
DropdownMenuRadioGroup,
DropdownMenuRadioItem,
DropdownMenuSeparator,
} from "@/lib/components/ui/dropdown-menu";

export function ThemeToggle() {
const { theme, setTheme } = useTheme();

export function ThemeToggleMenuGroup() {
const { setTheme, theme } = useTheme();
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline" size="icon">
<SunIcon className="h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
<MoonIcon className="absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
<span className="sr-only">Toggle theme</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent className="space-y-0.5" align="end">
<DropdownMenuItem
className={`${theme === "light" ? "bg-accent" : ""}`}
onClick={() => setTheme("light")}
>
Light
</DropdownMenuItem>
<DropdownMenuItem
className={`${theme === "dark" ? "bg-accent" : ""}`}
onClick={() => setTheme("dark")}
>
Dark
</DropdownMenuItem>
<DropdownMenuItem
className={`${theme === "system" ? "bg-accent" : ""}`}
onClick={() => setTheme("system")}
>
System
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
<>
<DropdownMenuLabel className="truncate">Theme</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuRadioGroup value={theme} onValueChange={setTheme}>
<DropdownMenuRadioItem value="light" className="flex gap-1.5">
<SunIcon className="size-4" />
<span>Light</span>
</DropdownMenuRadioItem>
<DropdownMenuRadioItem value="dark" className="flex gap-1.5">
<MoonIcon className="h-[1.2rem] w-[1.2rem]" />
<span>Dark</span>
</DropdownMenuRadioItem>
<DropdownMenuRadioItem value="system" className="flex gap-1.5">
<Half2Icon className="h-[1.2rem] w-[1.2rem]" />
<span>System</span>
</DropdownMenuRadioItem>
</DropdownMenuRadioGroup>
</>
);
}
37 changes: 0 additions & 37 deletions packages/frontpage/app/(app)/actions.tsx

This file was deleted.

102 changes: 56 additions & 46 deletions packages/frontpage/app/(app)/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import Link from "next/link";
import { Suspense } from "react";
import { Button } from "@/lib/components/ui/button";
import { isAdmin } from "@/lib/data/user";
import { OpenInNewWindowIcon } from "@radix-ui/react-icons";
import { ThemeToggle } from "./_components/theme-toggle";
import { BellIcon, OpenInNewWindowIcon } from "@radix-ui/react-icons";
import { ThemeToggleMenuGroup } from "./_components/theme-toggle";
import {
getDidFromHandleOrDid,
getVerifiedHandle,
Expand All @@ -22,6 +22,7 @@ import { UserAvatar } from "@/lib/components/user-avatar";
import { FRONTPAGE_ATPROTO_HANDLE } from "@/lib/constants";
import { cookies } from "next/headers";
import { revalidatePath } from "next/cache";
import { NotificationIndicator } from "./_components/notification-indicator";

export default async function Layout({
children,
Expand All @@ -43,7 +44,6 @@ export default async function Layout({
<Link href="/post/new">New</Link>
</Button>
) : null}
<ThemeToggle />
<Suspense>
<LoginOrLogout />
</Suspense>
Expand Down Expand Up @@ -75,53 +75,63 @@ async function LoginOrLogout() {
getVerifiedHandle(session.user.did),
]);
return (
<DropdownMenu>
<DropdownMenuTrigger>
{did ? (
<UserAvatar did={did} size="smedium" />
) : (
<span>{handle}</span>
)}
</DropdownMenuTrigger>
<DropdownMenuContent className="w-56" side="bottom" align="end">
<DropdownMenuLabel className="truncate">{handle}</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuItem asChild>
<Link href={`/profile/${handle}`} className="cursor-pointer">
Profile
<>
<NotificationIndicator>
<Button asChild variant="outline" size="icon">
<Link href="/notifications">
<BellIcon />
</Link>
</DropdownMenuItem>
<Suspense fallback={null}>
{isAdmin().then((isAdmin) =>
isAdmin ? (
<DropdownMenuItem asChild>
<Link href="/moderation" className="cursor-pointer">
Moderation
</Link>
</DropdownMenuItem>
) : null,
</Button>
</NotificationIndicator>
<DropdownMenu>
<DropdownMenuTrigger>
{did ? (
<UserAvatar did={did} size="smedium" />
) : (
<span>{handle}</span>
)}
</Suspense>
<DropdownMenuSeparator />
<form
action={async () => {
"use server";
await signOut();
deleteAuthCookie(await cookies());
revalidatePath("/", "layout");
}}
>
</DropdownMenuTrigger>
<DropdownMenuContent className="w-56" side="bottom" align="end">
<DropdownMenuLabel className="truncate">{handle}</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuItem asChild>
<button
type="submit"
className="w-full text-start cursor-pointer"
>
Logout
</button>
<Link href={`/profile/${handle}`} className="cursor-pointer">
Profile
</Link>
</DropdownMenuItem>
</form>
</DropdownMenuContent>
</DropdownMenu>
<Suspense fallback={null}>
{isAdmin().then((isAdmin) =>
isAdmin ? (
<DropdownMenuItem asChild>
<Link href="/moderation" className="cursor-pointer">
Moderation
</Link>
</DropdownMenuItem>
) : null,
)}
</Suspense>
<ThemeToggleMenuGroup />
<DropdownMenuSeparator />
<form
action={async () => {
"use server";
await signOut();
deleteAuthCookie(await cookies());
revalidatePath("/", "layout");
}}
>
<DropdownMenuItem asChild>
<button
type="submit"
className="w-full text-start cursor-pointer"
>
Logout
</button>
</DropdownMenuItem>
</form>
</DropdownMenuContent>
</DropdownMenu>
</>
);
}

Expand Down
Loading

0 comments on commit 8410293

Please sign in to comment.