import type { Meta, StoryFn } from '@storybook/react' import React from 'react' import { Channel, CustomMessageData, LocalMessage, StreamChat, } from 'stream-chat' import { daysAgo, hoursAgo, minutesAgo, now, secondsAgo, } from '../../stories/decorators/storyTime' import CustomChannelPreview from './CustomChannelPreview' type ComponentProps = React.ComponentProps const meta: Meta = { title: 'ChannelList/ChannelPreview', component: CustomChannelPreview, parameters: { layout: 'centered', }, decorators: [(Story) => ], args: { onChannelSelect: (channel) => console.log('Channel selected:', channel.id), }, } export default meta // Mock user for the client const mockUser = { id: 'current-user', name: 'Current User', image: 'https://i.pravatar.cc/150?img=1', } // Helper to create a mock channel const createMockChannel = (options: { id: string participantName: string participantId: string participantImage?: string lastMessageText?: string lastMessageTime?: Date unreadCount?: number lastMessageAttachments?: Array<{ asset_url?: string image_url?: string og_scrape_url?: string thumb_url?: string }> lastMessageMetadata?: CustomMessageData['metadata'] }): Channel => { const { id, participantName, participantId, participantImage, lastMessageText = 'Hey! How are you doing?', lastMessageTime = now(), unreadCount = 0, lastMessageAttachments, lastMessageMetadata, } = options const eventListeners = new Map void>>() return { id, cid: `messaging:${id}`, _client: { userID: mockUser.id, } as unknown as StreamChat, state: { members: { [mockUser.id]: { user: mockUser, user_id: mockUser.id, }, [participantId]: { user: { id: participantId, name: participantName, image: participantImage, }, user_id: participantId, }, }, messages: lastMessageText || lastMessageAttachments || lastMessageMetadata ? ([ { id: `msg-${id}-1`, text: lastMessageText, created_at: lastMessageTime.toISOString(), user: { id: participantId, name: participantName, }, attachments: lastMessageAttachments, metadata: lastMessageMetadata, }, ] as unknown as LocalMessage[]) : ([] as LocalMessage[]), unreadCount, }, on: (eventName: string, listener: (...args: unknown[]) => void) => { if (!eventListeners.has(eventName)) { eventListeners.set(eventName, new Set()) } eventListeners.get(eventName)?.add(listener) return { unsubscribe: () => { eventListeners.get(eventName)?.delete(listener) }, } }, off: (eventName: string, listener: (...args: unknown[]) => void) => { eventListeners.get(eventName)?.delete(listener) }, } as unknown as Channel } const Template: StoryFn = (args) => { return (
) } // Per-variant single-row stories stay in Storybook for browsing/design review // but are individually covered by rows in the PreviewStates / MessageTypeStates // / UrlAndAttachmentStates grids — skip in Chromatic so those grids are the // single snapshot source of truth. const skipInChromatic = { chromatic: { disableSnapshot: true } } // Deterministic local avatar for the grids so a pravatar network flake can't // diff a whole grid snapshot. Individual stories keep their remote images // (they're skipped in Chromatic anyway). const LOCAL_AVATAR = '/image-thumbnail.jpg' export const Default: StoryFn = Template.bind({}) Default.args = { channel: createMockChannel({ id: 'channel-1', participantName: 'Alice Johnson', participantId: 'participant-1', participantImage: 'https://i.pravatar.cc/150?img=2', lastMessageText: 'Hey! How are you doing?', lastMessageTime: now(), }), } Default.parameters = skipInChromatic export const Selected: StoryFn = Template.bind({}) Selected.args = { channel: createMockChannel({ id: 'channel-2', participantName: 'Bob Smith', participantId: 'participant-2', participantImage: 'https://i.pravatar.cc/150?img=3', lastMessageText: 'That sounds great!', lastMessageTime: minutesAgo(5), }), selectedChannel: createMockChannel({ id: 'channel-2', participantName: 'Bob Smith', participantId: 'participant-2', participantImage: 'https://i.pravatar.cc/150?img=3', }), } Selected.parameters = skipInChromatic export const WithUnreadMessages: StoryFn = Template.bind({}) WithUnreadMessages.args = { channel: createMockChannel({ id: 'channel-3', participantName: 'Carol Williams', participantId: 'participant-3', participantImage: 'https://i.pravatar.cc/150?img=4', lastMessageText: 'Did you see my last message?', lastMessageTime: minutesAgo(15), unreadCount: 3, }), } WithUnreadMessages.parameters = skipInChromatic export const ManyUnreadMessages: StoryFn = Template.bind({}) ManyUnreadMessages.args = { channel: createMockChannel({ id: 'channel-4', participantName: 'David Brown', participantId: 'participant-4', participantImage: 'https://i.pravatar.cc/150?img=5', lastMessageText: 'Please check this out!', lastMessageTime: hoursAgo(1), unreadCount: 127, }), } ManyUnreadMessages.parameters = skipInChromatic export const NoAvatar: StoryFn = Template.bind({}) NoAvatar.args = { channel: createMockChannel({ id: 'channel-5', participantName: 'Emma Davis', participantId: 'participant-5', lastMessageText: 'Thanks for your help!', lastMessageTime: minutesAgo(30), }), } NoAvatar.parameters = skipInChromatic export const NoMessages: StoryFn = Template.bind({}) NoMessages.args = { channel: createMockChannel({ id: 'channel-6', participantName: 'Frank Miller', participantId: 'participant-6', participantImage: 'https://i.pravatar.cc/150?img=6', lastMessageText: '', }), } NoMessages.parameters = skipInChromatic export const LongMessage: StoryFn = Template.bind({}) LongMessage.args = { channel: createMockChannel({ id: 'channel-7', participantName: 'Grace Lee', participantId: 'participant-7', participantImage: 'https://i.pravatar.cc/150?img=7', lastMessageText: 'This is a very long message that should be truncated because it contains way too much text to display in the preview. We want to make sure the component handles this gracefully.', lastMessageTime: hoursAgo(2), }), } LongMessage.parameters = skipInChromatic export const LongName: StoryFn = Template.bind({}) LongName.args = { channel: createMockChannel({ id: 'channel-8', participantName: 'Alexander Christopher Wellington-Montgomery III', participantId: 'participant-8', lastMessageText: 'Nice to meet you!', lastMessageTime: minutesAgo(45), }), } LongName.parameters = skipInChromatic export const ChatbotMessage: StoryFn = Template.bind({}) ChatbotMessage.args = { channel: createMockChannel({ id: 'channel-chatbot', participantName: 'Emily Watson', participantId: 'participant-chatbot', participantImage: 'https://i.pravatar.cc/150?img=9', lastMessageText: 'Thanks for reaching out! How can I help you today?', lastMessageTime: minutesAgo(2), lastMessageMetadata: { custom_type: 'MESSAGE_CHATBOT' }, }), } ChatbotMessage.parameters = skipInChromatic export const TipMessage: StoryFn = Template.bind({}) TipMessage.args = { channel: createMockChannel({ id: 'channel-tip', participantName: 'Alice Johnson', participantId: 'participant-tip', participantImage: 'https://i.pravatar.cc/150?img=2', lastMessageText: 'Take my money!', lastMessageTime: minutesAgo(3), lastMessageMetadata: { custom_type: 'MESSAGE_TIP', amount_text: '$5.00' }, }), } TipMessage.parameters = skipInChromatic export const PaidMessage: StoryFn = Template.bind({}) PaidMessage.args = { channel: createMockChannel({ id: 'channel-paid', participantName: 'Bob Smith', participantId: 'participant-paid', participantImage: 'https://i.pravatar.cc/150?img=3', lastMessageText: 'I paid for this message!', lastMessageTime: minutesAgo(10), lastMessageMetadata: { custom_type: 'MESSAGE_PAID', amount_text: '$10.00' }, }), } PaidMessage.parameters = skipInChromatic export const PaidAttachment: StoryFn = Template.bind({}) PaidAttachment.args = { channel: createMockChannel({ id: 'channel-paid-attachment', participantName: 'Carol Williams', participantId: 'participant-paid-attachment', participantImage: 'https://i.pravatar.cc/150?img=4', lastMessageText: 'Check out this exclusive photo!', lastMessageTime: minutesAgo(20), lastMessageMetadata: { custom_type: 'MESSAGE_ATTACHMENT', attachment_title: 'exclusive-photo.jpg', }, }), } PaidAttachment.parameters = skipInChromatic const renderMessagePreviewWithMarkers = ( message: LocalMessage | undefined, defaultPreview?: string ): React.ReactNode => { if (!message) return defaultPreview const metadata = ( message as LocalMessage & { metadata?: Record } ).metadata if (metadata?.custom_type === 'MESSAGE_URGENT') { return ( <> 🔥 URGENT:{' '} {defaultPreview} ) } if (metadata?.is_priority) { return `⚡ ${defaultPreview}` } if (metadata?.is_vip) { return `👑 ${defaultPreview}` } return defaultPreview } export const WithCustomMessagePreview: StoryFn = () => { const channels = [ createMockChannel({ id: 'channel-urgent', participantName: 'Alice Urgent', participantId: 'participant-urgent', participantImage: 'https://i.pravatar.cc/150?img=36', lastMessageText: 'Critical issue needs immediate attention! Please review the server logs for more details and escalate to the on-call engineer. This system outage is affecting multiple users, and we need to resolve it as soon as possible. Impacted services include messaging, notifications, and real-time updates. All hands are required to investigate and remediate the situation. If you have any questions or need additional resources, contact the incident commander immediately!', lastMessageTime: minutesAgo(1), lastMessageMetadata: { custom_type: 'MESSAGE_URGENT' }, unreadCount: 3, }), createMockChannel({ id: 'channel-priority', participantName: 'Bob Priority', participantId: 'participant-priority', participantImage: 'https://i.pravatar.cc/150?img=37', lastMessageText: 'High priority message', lastMessageTime: minutesAgo(5), lastMessageMetadata: { is_priority: true }, unreadCount: 1, }), createMockChannel({ id: 'channel-vip', participantName: 'Carol VIP', participantId: 'participant-vip', participantImage: 'https://i.pravatar.cc/150?img=38', lastMessageText: 'Thanks for the support!', lastMessageTime: minutesAgo(10), lastMessageMetadata: { is_vip: true }, }), createMockChannel({ id: 'channel-normal', participantName: 'David Normal', participantId: 'participant-normal', participantImage: 'https://i.pravatar.cc/150?img=39', lastMessageText: 'Hey, how are you?', lastMessageTime: minutesAgo(30), }), ] return (
{channels.map((channel) => ( { console.log('Channel selected:', channel.id) }} renderMessagePreview={renderMessagePreviewWithMarkers} /> ))}
) } export const SelectedWithUnread: StoryFn = Template.bind({}) SelectedWithUnread.args = { channel: createMockChannel({ id: 'channel-9', participantName: 'Helen Park', participantId: 'participant-9', participantImage: 'https://i.pravatar.cc/150?img=8', lastMessageText: 'Important update!', lastMessageTime: minutesAgo(10), unreadCount: 5, }), selectedChannel: createMockChannel({ id: 'channel-9', participantName: 'Helen Park', participantId: 'participant-9', }), } SelectedWithUnread.parameters = skipInChromatic export const MultipleChannels: StoryFn = () => { const [selectedChannelId, _setSelectedChannelId] = React.useState< string | null >('channel-2') const channels = [ createMockChannel({ id: 'channel-1', participantName: 'Alice Johnson', participantId: 'participant-1', participantImage: 'https://i.pravatar.cc/150?img=2', lastMessageText: 'Hey! How are you doing?', lastMessageTime: now(), unreadCount: 2, }), createMockChannel({ id: 'channel-2', participantName: 'Bob Smith', participantId: 'participant-2', participantImage: 'https://i.pravatar.cc/150?img=3', lastMessageText: 'That sounds great!', lastMessageTime: minutesAgo(5), }), createMockChannel({ id: 'channel-3', participantName: 'Carol Williams', participantId: 'participant-3', participantImage: 'https://i.pravatar.cc/150?img=4', lastMessageText: 'See you tomorrow', lastMessageTime: hoursAgo(1), unreadCount: 15, }), createMockChannel({ id: 'channel-4', participantName: 'David Brown', participantId: 'participant-4', lastMessageText: 'Thanks!', lastMessageTime: hoursAgo(2), }), ] const selectedChannel = channels.find((c) => c.id === selectedChannelId) || null return (
{channels.map((channel) => ( { console.log('Channel selected:', channel.id) }} /> ))}
) } export const WithUrl: StoryFn = Template.bind({}) WithUrl.args = { channel: createMockChannel({ id: 'channel-url', participantName: 'Ivan Rodriguez', participantId: 'participant-url', participantImage: 'https://i.pravatar.cc/150?img=10', lastMessageText: 'https://example.com/page', lastMessageTime: minutesAgo(8), }), } WithUrl.parameters = skipInChromatic export const WithVeryLongUrl: StoryFn = Template.bind({}) WithVeryLongUrl.args = { channel: createMockChannel({ id: 'channel-long-url', participantName: 'Julia Martinez', participantId: 'participant-long-url', participantImage: 'https://i.pravatar.cc/150?img=11', lastMessageText: 'https://example.com/very/long/path/with/many/segments/and/query/parameters?param1=value1¶m2=value2¶m3=value3¶m4=value4¶m5=very-long-value-that-makes-the-url-extremely-long', lastMessageTime: minutesAgo(20), }), } WithVeryLongUrl.parameters = skipInChromatic // Time-based stories // // Each story below covers exactly one row of the `formatRelativeTime` // output matrix. The `TimeVariations` story further down already snapshots // all of them together in a labeled table — kept here for Storybook // browsing but skipped in Chromatic to avoid 9 redundant snapshots. export const JustNow: StoryFn = Template.bind({}) JustNow.args = { channel: createMockChannel({ id: 'channel-just-now', participantName: 'Kevin Chen', participantId: 'participant-just-now', participantImage: 'https://i.pravatar.cc/150?img=12', lastMessageText: 'I just sent this!', lastMessageTime: secondsAgo(30), }), } JustNow.parameters = skipInChromatic export const FiveMinutesAgo: StoryFn = Template.bind({}) FiveMinutesAgo.args = { channel: createMockChannel({ id: 'channel-5m', participantName: 'Laura Thompson', participantId: 'participant-5m', participantImage: 'https://i.pravatar.cc/150?img=13', lastMessageText: 'Be right back', lastMessageTime: minutesAgo(5), }), } FiveMinutesAgo.parameters = skipInChromatic export const ThreeHoursAgo: StoryFn = Template.bind({}) ThreeHoursAgo.args = { channel: createMockChannel({ id: 'channel-3h', participantName: 'Michael Yang', participantId: 'participant-3h', participantImage: 'https://i.pravatar.cc/150?img=14', lastMessageText: 'Got it, thanks!', lastMessageTime: hoursAgo(3), }), } ThreeHoursAgo.parameters = skipInChromatic export const Yesterday: StoryFn = Template.bind({}) Yesterday.args = { channel: createMockChannel({ id: 'channel-yesterday', participantName: 'Nancy Wilson', participantId: 'participant-yesterday', participantImage: 'https://i.pravatar.cc/150?img=15', lastMessageText: 'See you tomorrow', lastMessageTime: hoursAgo(30), }), } Yesterday.parameters = skipInChromatic export const ThreeDaysAgo: StoryFn = Template.bind({}) ThreeDaysAgo.args = { channel: createMockChannel({ id: 'channel-3d', participantName: 'Oscar Martinez', participantId: 'participant-3d', participantImage: 'https://i.pravatar.cc/150?img=16', lastMessageText: 'Have a great weekend!', lastMessageTime: daysAgo(3), }), } ThreeDaysAgo.parameters = skipInChromatic export const LastWeek: StoryFn = Template.bind({}) LastWeek.args = { channel: createMockChannel({ id: 'channel-1w', participantName: 'Patricia Brown', participantId: 'participant-1w', participantImage: 'https://i.pravatar.cc/150?img=17', lastMessageText: 'Talk to you next week', lastMessageTime: daysAgo(7), }), } LastWeek.parameters = skipInChromatic export const TwoWeeksAgo: StoryFn = Template.bind({}) TwoWeeksAgo.args = { channel: createMockChannel({ id: 'channel-2w', participantName: 'Quinn Anderson', participantId: 'participant-2w', participantImage: 'https://i.pravatar.cc/150?img=18', lastMessageText: 'Sounds good', lastMessageTime: daysAgo(14), }), } TwoWeeksAgo.parameters = skipInChromatic export const OneMonthAgo: StoryFn = Template.bind({}) OneMonthAgo.args = { channel: createMockChannel({ id: 'channel-1m', participantName: 'Rachel Green', participantId: 'participant-1m', participantImage: 'https://i.pravatar.cc/150?img=19', lastMessageText: 'Happy New Year!', lastMessageTime: daysAgo(30), }), } OneMonthAgo.parameters = skipInChromatic export const SixMonthsAgo: StoryFn = Template.bind({}) SixMonthsAgo.args = { channel: createMockChannel({ id: 'channel-6m', participantName: 'Sam Taylor', participantId: 'participant-6m', participantImage: 'https://i.pravatar.cc/150?img=20', lastMessageText: 'Long time no talk!', lastMessageTime: daysAgo(180), }), } SixMonthsAgo.parameters = skipInChromatic export const TimeVariations: StoryFn = () => { const [selectedChannelId, _setSelectedChannelId] = React.useState< string | null >(null) const timeVariations = [ { id: 'time-just-now', name: 'Just Now', time: secondsAgo(30), message: 'Just sent', }, { id: 'time-5m', name: '5 Minutes', time: minutesAgo(5), message: '5 minutes ago', }, { id: 'time-30m', name: '30 Minutes', time: minutesAgo(30), message: '30 minutes ago', }, { id: 'time-3h', name: '3 Hours', time: hoursAgo(3), message: '3 hours ago', }, { id: 'time-yesterday', name: 'Yesterday', time: hoursAgo(30), message: 'Yesterday', }, { id: 'time-3d', name: '3 Days', time: daysAgo(3), message: '3 days ago', }, { id: 'time-1w', name: '1 Week', time: daysAgo(7), message: '1 week ago', }, { id: 'time-2w', name: '2 Weeks', time: daysAgo(14), message: '2 weeks ago', }, { id: 'time-1m', name: '1 Month', time: daysAgo(30), message: '1 month ago', }, { id: 'time-6m', name: '6 Months', time: daysAgo(180), message: '6 months ago', }, ] const channels = timeVariations.map((variation, index) => createMockChannel({ id: variation.id, participantName: variation.name, participantId: `participant-${variation.id}`, participantImage: `https://i.pravatar.cc/150?img=${21 + index}`, lastMessageText: variation.message, lastMessageTime: variation.time, }) ) const selectedChannel = channels.find((c) => c.id === selectedChannelId) || null return (
{channels.map((channel) => ( { console.log('Channel selected:', channel.id) }} /> ))}
) } // Message text fallback stories export const WithAttachmentAssetUrl: StoryFn = Template.bind({}) WithAttachmentAssetUrl.args = { channel: createMockChannel({ id: 'channel-attachment-asset', participantName: 'Tom Wilson', participantId: 'participant-attachment-asset', participantImage: 'https://i.pravatar.cc/150?img=30', lastMessageText: '', // No text, only attachment lastMessageTime: minutesAgo(10), lastMessageAttachments: [ { asset_url: 'https://example.com/document.pdf', }, ], }), } WithAttachmentAssetUrl.parameters = skipInChromatic export const WithAttachmentImageUrl: StoryFn = Template.bind({}) WithAttachmentImageUrl.args = { channel: createMockChannel({ id: 'channel-attachment-image', participantName: 'Uma Patel', participantId: 'participant-attachment-image', participantImage: 'https://i.pravatar.cc/150?img=31', lastMessageText: '', // No text, only attachment lastMessageTime: minutesAgo(15), lastMessageAttachments: [ { image_url: 'https://example.com/image.jpg', }, ], }), } // Same fallback render as WithAttachmentAssetUrl ("📎 Sent an attachment") — // kept as a separate story to document the field, skipped in Chromatic to // avoid a redundant snapshot. WithAttachmentImageUrl.parameters = skipInChromatic export const WithAttachmentOgScrapeUrl: StoryFn = Template.bind( {} ) WithAttachmentOgScrapeUrl.args = { channel: createMockChannel({ id: 'channel-attachment-og', participantName: 'Victor Zhang', participantId: 'participant-attachment-og', participantImage: 'https://i.pravatar.cc/150?img=32', lastMessageText: '', // No text, only attachment lastMessageTime: minutesAgo(7), lastMessageAttachments: [ { og_scrape_url: 'https://example.com/article', }, ], }), } WithAttachmentOgScrapeUrl.parameters = skipInChromatic export const WithAttachmentThumbUrl: StoryFn = Template.bind({}) WithAttachmentThumbUrl.args = { channel: createMockChannel({ id: 'channel-attachment-thumb', participantName: 'Wendy Kim', participantId: 'participant-attachment-thumb', participantImage: 'https://i.pravatar.cc/150?img=33', lastMessageText: '', // No text, only attachment lastMessageTime: minutesAgo(12), lastMessageAttachments: [ { thumb_url: 'https://example.com/thumb.jpg', }, ], }), } WithAttachmentThumbUrl.parameters = skipInChromatic export const WithLongAttachmentUrl: StoryFn = Template.bind({}) WithLongAttachmentUrl.args = { channel: createMockChannel({ id: 'channel-attachment-long', participantName: 'Xavier Cruz', participantId: 'participant-attachment-long', participantImage: 'https://i.pravatar.cc/150?img=34', lastMessageText: '', // No text, only attachment lastMessageTime: minutesAgo(20), lastMessageAttachments: [ { asset_url: 'https://example.com/very/long/path/to/file/with/many/segments/document-with-very-long-name-that-should-be-truncated.pdf', }, ], }), } WithLongAttachmentUrl.parameters = skipInChromatic interface GridRow { label: string channel: Channel selectedChannel?: Channel } const StatesGrid: StoryFn<{ rows: GridRow[] }> = ({ rows }) => (
{rows.map(({ label, channel, selectedChannel }) => (
{label}
{ console.log('Channel selected:', channel.id) }} />
))}
) export const PreviewStates: StoryFn = () => { const selected = createMockChannel({ id: 'channel-2', participantName: 'Bob Smith', participantId: 'participant-2', participantImage: LOCAL_AVATAR, lastMessageText: 'That sounds great!', lastMessageTime: minutesAgo(5), }) const selectedUnread = createMockChannel({ id: 'channel-9', participantName: 'Helen Park', participantId: 'participant-9', participantImage: LOCAL_AVATAR, lastMessageText: 'Important update!', lastMessageTime: minutesAgo(10), unreadCount: 5, }) const rows: GridRow[] = [ { label: 'Default', channel: createMockChannel({ id: 'channel-1', participantName: 'Alice Johnson', participantId: 'participant-1', participantImage: LOCAL_AVATAR, lastMessageText: 'Hey! How are you doing?', lastMessageTime: now(), }), }, { label: 'Selected', channel: selected, selectedChannel: selected, }, { label: 'With unread messages', channel: createMockChannel({ id: 'channel-3', participantName: 'Carol Williams', participantId: 'participant-3', participantImage: LOCAL_AVATAR, lastMessageText: 'Did you see my last message?', lastMessageTime: minutesAgo(15), unreadCount: 3, }), }, { label: 'Many unread messages', channel: createMockChannel({ id: 'channel-4', participantName: 'David Brown', participantId: 'participant-4', participantImage: LOCAL_AVATAR, lastMessageText: 'Please check this out!', lastMessageTime: hoursAgo(1), unreadCount: 127, }), }, { label: 'Selected with unread', channel: selectedUnread, selectedChannel: selectedUnread, }, { label: 'No avatar', channel: createMockChannel({ id: 'channel-5', participantName: 'Emma Davis', participantId: 'participant-5', lastMessageText: 'Thanks for your help!', lastMessageTime: minutesAgo(30), }), }, { label: 'No messages', channel: createMockChannel({ id: 'channel-6', participantName: 'Frank Miller', participantId: 'participant-6', participantImage: LOCAL_AVATAR, lastMessageText: '', }), }, { label: 'Long message', channel: createMockChannel({ id: 'channel-7', participantName: 'Grace Lee', participantId: 'participant-7', participantImage: LOCAL_AVATAR, lastMessageText: 'This is a very long message that should be truncated because it contains way too much text to display in the preview. We want to make sure the component handles this gracefully.', lastMessageTime: hoursAgo(2), }), }, { label: 'Long name', channel: createMockChannel({ id: 'channel-8', participantName: 'Alexander Christopher Wellington-Montgomery III', participantId: 'participant-8', lastMessageText: 'Nice to meet you!', lastMessageTime: minutesAgo(45), }), }, ] return } export const MessageTypeStates: StoryFn = () => { const rows: GridRow[] = [ { label: 'Chatbot message', channel: createMockChannel({ id: 'channel-chatbot', participantName: 'Emily Watson', participantId: 'participant-chatbot', participantImage: LOCAL_AVATAR, lastMessageText: 'Thanks for reaching out! How can I help you today?', lastMessageTime: minutesAgo(2), lastMessageMetadata: { custom_type: 'MESSAGE_CHATBOT' }, }), }, { label: 'Tip message', channel: createMockChannel({ id: 'channel-tip', participantName: 'Alice Johnson', participantId: 'participant-tip', participantImage: LOCAL_AVATAR, lastMessageText: 'Take my money!', lastMessageTime: minutesAgo(3), lastMessageMetadata: { custom_type: 'MESSAGE_TIP', amount_text: '$5.00', }, }), }, { label: 'Paid message', channel: createMockChannel({ id: 'channel-paid', participantName: 'Bob Smith', participantId: 'participant-paid', participantImage: LOCAL_AVATAR, lastMessageText: 'I paid for this message!', lastMessageTime: minutesAgo(10), lastMessageMetadata: { custom_type: 'MESSAGE_PAID', amount_text: '$10.00', }, }), }, { label: 'Paid attachment', channel: createMockChannel({ id: 'channel-paid-attachment', participantName: 'Carol Williams', participantId: 'participant-paid-attachment', participantImage: LOCAL_AVATAR, lastMessageText: 'Check out this exclusive photo!', lastMessageTime: minutesAgo(20), lastMessageMetadata: { custom_type: 'MESSAGE_ATTACHMENT', attachment_title: 'exclusive-photo.jpg', }, }), }, ] return } export const UrlAndAttachmentStates: StoryFn = () => { const rows: GridRow[] = [ { label: 'With URL', channel: createMockChannel({ id: 'channel-url', participantName: 'Ivan Rodriguez', participantId: 'participant-url', participantImage: LOCAL_AVATAR, lastMessageText: 'https://example.com/page', lastMessageTime: minutesAgo(8), }), }, { label: 'With very long URL', channel: createMockChannel({ id: 'channel-long-url', participantName: 'Julia Martinez', participantId: 'participant-long-url', participantImage: LOCAL_AVATAR, lastMessageText: 'https://example.com/very/long/path/with/many/segments/and/query/parameters?param1=value1¶m2=value2¶m3=value3¶m4=value4¶m5=very-long-value-that-makes-the-url-extremely-long', lastMessageTime: minutesAgo(20), }), }, { label: 'Attachment (asset_url)', channel: createMockChannel({ id: 'channel-attachment-asset', participantName: 'Tom Wilson', participantId: 'participant-attachment-asset', participantImage: LOCAL_AVATAR, lastMessageText: '', lastMessageTime: minutesAgo(10), lastMessageAttachments: [ { asset_url: 'https://example.com/document.pdf' }, ], }), }, { label: 'Attachment (image_url)', channel: createMockChannel({ id: 'channel-attachment-image', participantName: 'Uma Patel', participantId: 'participant-attachment-image', participantImage: LOCAL_AVATAR, lastMessageText: '', lastMessageTime: minutesAgo(15), lastMessageAttachments: [ { image_url: 'https://example.com/image.jpg' }, ], }), }, { label: 'Attachment (og_scrape_url)', channel: createMockChannel({ id: 'channel-attachment-og', participantName: 'Victor Zhang', participantId: 'participant-attachment-og', participantImage: LOCAL_AVATAR, lastMessageText: '', lastMessageTime: minutesAgo(7), lastMessageAttachments: [ { og_scrape_url: 'https://example.com/article' }, ], }), }, { label: 'Attachment (thumb_url)', channel: createMockChannel({ id: 'channel-attachment-thumb', participantName: 'Wendy Kim', participantId: 'participant-attachment-thumb', participantImage: LOCAL_AVATAR, lastMessageText: '', lastMessageTime: minutesAgo(12), lastMessageAttachments: [ { thumb_url: 'https://example.com/thumb.jpg' }, ], }), }, { label: 'Long attachment URL', channel: createMockChannel({ id: 'channel-attachment-long', participantName: 'Xavier Cruz', participantId: 'participant-attachment-long', participantImage: LOCAL_AVATAR, lastMessageText: '', lastMessageTime: minutesAgo(20), lastMessageAttachments: [ { asset_url: 'https://example.com/very/long/path/to/file/with/many/segments/document-with-very-long-name-that-should-be-truncated.pdf', }, ], }), }, ] return }