Skip to main content
Glama

Storyden

by Southclaws
Mozilla Public License 2.0
227
NotificationsMenu.tsx4.74 kB
import { Portal } from "@ark-ui/react"; import Link from "next/link"; import { MemberAvatar } from "@/components/member/MemberBadge/MemberAvatar"; import { Button } from "@/components/ui/button"; import { ArchiveIcon } from "@/components/ui/icons/Archive"; import { SettingsIcon } from "@/components/ui/icons/Settings"; import { LinkButton } from "@/components/ui/link-button"; import * as Menu from "@/components/ui/menu"; import { Center, LStack, WStack, styled } from "@/styled-system/jsx"; import { hstack } from "@/styled-system/patterns"; import { deriveError } from "@/utils/error"; import { NotificationsTrigger } from "./NotificationsTrigger"; import { NotificationItem } from "./item"; import { Props, useNotifications } from "./useNotifications"; export function NotificationsMenu(props: Props) { const { ready, error, data, handlers } = useNotifications(props); if (!ready) { return ( <NotificationsTrigger hideLabel size="md" variant="ghost" disabled title={deriveError(error)} /> ); } const { unreads, notifications } = data; const isEmpty = notifications.length === 0; return ( <Menu.Root closeOnSelect={false}> <Menu.Trigger cursor="pointer" position="relative" asChild> <NotificationsTrigger hideLabel size="md" variant="ghost" unread={!isEmpty} /> </Menu.Trigger> <Portal> <Menu.Positioner> <Menu.Content minW="48" userSelect="none"> <Menu.ItemGroup id="heading"> <Menu.ItemGroupLabel display="flex" gap="2" alignItems="center"> <LStack fontSize="sm"> <WStack> <styled.p color="fg.muted"> Notifications ({unreads}) </styled.p> <LinkButton href="/notifications" size="xs" variant="outline" > see all </LinkButton> </WStack> </LStack> </Menu.ItemGroupLabel> <Menu.Separator /> {isEmpty ? ( <Center w="full" py="4" color="fg.muted" fontSize="xs"> You&apos;re all caught up! </Center> ) : ( notifications.map((notification) => ( <Menu.Item key={notification.id} value={notification.id} height="auto" py="1" > <WStack className="notification-menu__row" minW="0" gap="1"> <Link className={hstack({ w: "full", minW: "0", justify: "space-between", })} href={notification.url} onClick={() => handlers.handleMarkAs(notification.id, "read") } > <NotificationAvatar notification={notification} /> <LStack gap="0" minW="0"> <styled.span fontWeight="bold" textWrap="nowrap" textOverflow="ellipsis" overflow="hidden" maxW="full" > {notification.source?.handle ?? "System"} </styled.span> <styled.span fontWeight="normal"> {notification.description} </styled.span> </LStack> </Link> <Button variant="ghost" size="sm" title="Mark as read" onClick={() => handlers.handleMarkAs(notification.id, "read") } > <ArchiveIcon /> </Button> </WStack> </Menu.Item> )) )} </Menu.ItemGroup> </Menu.Content> </Menu.Positioner> </Portal> </Menu.Root> ); } export function NotificationAvatar(props: { notification: NotificationItem }) { if (props.notification.source) { return <MemberAvatar profile={props.notification.source} size="sm" />; } return <SettingsIcon />; }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Southclaws/storyden'

If you have feedback or need assistance with the MCP directory API, please join our Discord server