diff --git a/src/webapp/features/follows/components/subscribeButton/SubscribeButton.stories.tsx b/src/webapp/features/follows/components/subscribeButton/SubscribeButton.stories.tsx new file mode 100644 index 00000000..ca0c0873 --- /dev/null +++ b/src/webapp/features/follows/components/subscribeButton/SubscribeButton.stories.tsx @@ -0,0 +1,100 @@ +import type { Meta, StoryObj } from '@storybook/nextjs-vite'; +import { useState } from 'react'; +import SubscribeButton, { type SubscriptionPrefs } from './SubscribeButton'; + +const meta: Meta = { + title: 'Features/Follows/SubscribeButton', + component: SubscribeButton, +}; + +export default meta; + +type Story = StoryObj; + +// ─── User ──────────────────────────────────────────────────────────────────── + +/** Following a user but no subscriptions on. Bell is outlined. */ +export const UserDefault: Story = { + args: { + targetId: 'did:plc:user123', + targetType: 'USER', + prefs: { cards: false, connections: false }, + }, +}; + +/** Subscribed to cards only. */ +export const UserCardsOnly: Story = { + args: { + targetId: 'did:plc:user123', + targetType: 'USER', + prefs: { cards: true, connections: false }, + }, +}; + +/** Subscribed to connections only. */ +export const UserConnectionsOnly: Story = { + args: { + targetId: 'did:plc:user123', + targetType: 'USER', + prefs: { cards: false, connections: true }, + }, +}; + +/** Subscribed to everything. Bell is filled. */ +export const UserAllOn: Story = { + args: { + targetId: 'did:plc:user123', + targetType: 'USER', + prefs: { cards: true, connections: true }, + }, +}; + +// ─── Collection ────────────────────────────────────────────────────────────── + +/** Following a collection but no subscriptions on. */ +export const CollectionDefault: Story = { + args: { + targetId: 'col-abc-001', + targetType: 'COLLECTION', + prefs: { cards: false, connections: false }, + }, +}; + +/** Subscribed to cards only. */ +export const CollectionCardsOnly: Story = { + args: { + targetId: 'col-abc-001', + targetType: 'COLLECTION', + prefs: { cards: true, connections: false }, + }, +}; + +/** Subscribed to everything. */ +export const CollectionAllOn: Story = { + args: { + targetId: 'col-abc-001', + targetType: 'COLLECTION', + prefs: { cards: true, connections: true }, + }, +}; + +// ─── Interactive ───────────────────────────────────────────────────────────── + +/** Click the bell to open the menu and toggle subscriptions. */ +export const Interactive: Story = { + render: () => { + const [prefs, setPrefs] = useState({ + cards: false, + connections: false, + }); + + return ( + + ); + }, +}; diff --git a/src/webapp/features/follows/components/subscribeButton/SubscribeButton.tsx b/src/webapp/features/follows/components/subscribeButton/SubscribeButton.tsx new file mode 100644 index 00000000..c2dc4ad9 --- /dev/null +++ b/src/webapp/features/follows/components/subscribeButton/SubscribeButton.tsx @@ -0,0 +1,60 @@ +'use client'; + +import { ActionIcon, Menu } from '@mantine/core'; +import { MdNotificationAdd, MdNotificationsActive } from 'react-icons/md'; +import { IoMdCheckmark } from 'react-icons/io'; + +export interface SubscriptionPrefs { + cards: boolean; + connections: boolean; +} + +interface Props { + targetId: string; + targetType: 'COLLECTION' | 'USER'; + prefs?: SubscriptionPrefs; + onPrefsChange?: (next: SubscriptionPrefs) => void; +} + +const OPTIONS: { key: keyof SubscriptionPrefs; label: string }[] = [ + { key: 'cards', label: 'Cards' }, + { key: 'connections', label: 'Connections' }, +]; + +const DEFAULT_PREFS: SubscriptionPrefs = { cards: false, connections: false }; + +export default function SubscribeButton({ prefs, onPrefsChange }: Props) { + const resolved = prefs ?? DEFAULT_PREFS; + const isActive = resolved.cards || resolved.connections; + + function toggle(key: keyof SubscriptionPrefs) { + onPrefsChange?.({ ...resolved, [key]: !resolved[key] }); + } + + return ( + + + + {isActive ? : } + + + + Notify me about + {OPTIONS.map((opt) => ( + : null} + onClick={() => toggle(opt.key)} + > + {opt.label} + + ))} + + + ); +}