// Copyright 2020-2022 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only import * as React from 'react'; import { isString } from 'lodash'; import { action } from '@storybook/addon-actions'; import { boolean, text } from '@storybook/addon-knobs'; import { ConversationColors } from '../../types/Colors'; import { pngUrl } from '../../storybook/Fixtures'; import type { Props as MessagesProps } from './Message'; import { Message, TextDirection } from './Message'; import { AUDIO_MP3, IMAGE_PNG, LONG_MESSAGE, VIDEO_MP4, stringToMIMEType, } from '../../types/MIME'; import type { Props } from './Quote'; import { Quote } from './Quote'; import { ReadStatus } from '../../messages/MessageReadStatus'; import { setupI18n } from '../../util/setupI18n'; import enMessages from '../../../_locales/en/messages.json'; import { getDefaultConversation } from '../../test-both/helpers/getDefaultConversation'; import { WidthBreakpoint } from '../_util'; import { ThemeType } from '../../types/Util'; const i18n = setupI18n('en', enMessages); export default { title: 'Components/Conversation/Quote', }; const defaultMessageProps: MessagesProps = { author: getDefaultConversation({ id: 'some-id', title: 'Person X', }), canReact: true, canReply: true, canRetry: true, canRetryDeleteForEveryone: true, canDeleteForEveryone: true, canDownload: true, checkForAccount: action('checkForAccount'), clearSelectedMessage: action('default--clearSelectedMessage'), containerElementRef: React.createRef(), containerWidthBreakpoint: WidthBreakpoint.Wide, conversationColor: 'crimson', conversationId: 'conversationId', conversationTitle: 'Conversation Title', conversationType: 'direct', // override deleteMessage: action('default--deleteMessage'), deleteMessageForEveryone: action('default--deleteMessageForEveryone'), direction: 'incoming', displayTapToViewMessage: action('default--displayTapToViewMessage'), downloadAttachment: action('default--downloadAttachment'), doubleCheckMissingQuoteReference: action( 'default--doubleCheckMissingQuoteReference' ), getPreferredBadge: () => undefined, i18n, id: 'messageId', renderingContext: 'storybook', interactionMode: 'keyboard', isBlocked: false, isMessageRequestAccepted: true, kickOffAttachmentDownload: action('default--kickOffAttachmentDownload'), markAttachmentAsCorrupted: action('default--markAttachmentAsCorrupted'), markViewed: action('default--markViewed'), messageExpanded: action('default--message-expanded'), openConversation: action('default--openConversation'), openGiftBadge: action('openGiftBadge'), openLink: action('default--openLink'), previews: [], reactToMessage: action('default--reactToMessage'), readStatus: ReadStatus.Read, renderEmojiPicker: () =>
, renderReactionPicker: () =>
, renderAudioAttachment: () =>
*AudioAttachment*
, replyToMessage: action('default--replyToMessage'), retrySend: action('default--retrySend'), retryDeleteForEveryone: action('default--retryDeleteForEveryone'), scrollToQuotedMessage: action('default--scrollToQuotedMessage'), selectMessage: action('default--selectMessage'), shouldCollapseAbove: false, shouldCollapseBelow: false, shouldHideMetadata: false, showContactDetail: action('default--showContactDetail'), showContactModal: action('default--showContactModal'), showExpiredIncomingTapToViewToast: action( 'showExpiredIncomingTapToViewToast' ), showExpiredOutgoingTapToViewToast: action( 'showExpiredOutgoingTapToViewToast' ), showForwardMessageModal: action('default--showForwardMessageModal'), showMessageDetail: action('default--showMessageDetail'), showVisualAttachment: action('default--showVisualAttachment'), startConversation: action('default--startConversation'), status: 'sent', text: 'This is really interesting.', textDirection: TextDirection.Default, theme: ThemeType.light, timestamp: Date.now(), }; const renderInMessage = ({ authorTitle, conversationColor, isFromMe, rawAttachment, isViewOnce, isGiftBadge, referencedMessageNotFound, text: quoteText, }: Props) => { const messageProps = { ...defaultMessageProps, conversationColor, quote: { authorId: 'an-author', authorTitle, conversationColor, isFromMe, rawAttachment, isViewOnce, isGiftBadge, referencedMessageNotFound, sentAt: Date.now() - 30 * 1000, text: quoteText, }, }; return (

); }; const createProps = (overrideProps: Partial = {}): Props => ({ authorTitle: text( 'authorTitle', overrideProps.authorTitle || 'Default Sender' ), conversationColor: overrideProps.conversationColor || 'forest', doubleCheckMissingQuoteReference: overrideProps.doubleCheckMissingQuoteReference || action('doubleCheckMissingQuoteReference'), i18n, isFromMe: boolean('isFromMe', overrideProps.isFromMe || false), isIncoming: boolean('isIncoming', overrideProps.isIncoming || false), onClick: action('onClick'), onClose: action('onClose'), rawAttachment: overrideProps.rawAttachment || undefined, referencedMessageNotFound: boolean( 'referencedMessageNotFound', overrideProps.referencedMessageNotFound || false ), isGiftBadge: boolean('isGiftBadge', overrideProps.isGiftBadge || false), isViewOnce: boolean('isViewOnce', overrideProps.isViewOnce || false), text: text( 'text', isString(overrideProps.text) ? overrideProps.text : 'A sample message from a pal' ), }); export const OutgoingByAnotherAuthor = (): JSX.Element => { const props = createProps({ authorTitle: 'Terrence Malick', }); return ; }; OutgoingByAnotherAuthor.story = { name: 'Outgoing by Another Author', }; export const OutgoingByMe = (): JSX.Element => { const props = createProps({ isFromMe: true, }); return ; }; OutgoingByMe.story = { name: 'Outgoing by Me', }; export const IncomingByAnotherAuthor = (): JSX.Element => { const props = createProps({ authorTitle: 'Terrence Malick', isIncoming: true, }); return ; }; IncomingByAnotherAuthor.story = { name: 'Incoming by Another Author', }; export const IncomingByMe = (): JSX.Element => { const props = createProps({ isFromMe: true, isIncoming: true, }); return ; }; IncomingByMe.story = { name: 'Incoming by Me', }; export const IncomingOutgoingColors = (): JSX.Element => { const props = createProps({}); return ( <> {ConversationColors.map(color => renderInMessage({ ...props, conversationColor: color }) )} ); }; IncomingOutgoingColors.story = { name: 'Incoming/Outgoing Colors', }; export const ImageOnly = (): JSX.Element => { const props = createProps({ text: '', rawAttachment: { contentType: IMAGE_PNG, fileName: 'sax.png', isVoiceMessage: false, thumbnail: { contentType: IMAGE_PNG, height: 100, width: 100, path: pngUrl, objectUrl: pngUrl, }, }, }); return ; }; export const ImageAttachment = (): JSX.Element => { const props = createProps({ rawAttachment: { contentType: IMAGE_PNG, fileName: 'sax.png', isVoiceMessage: false, thumbnail: { contentType: IMAGE_PNG, height: 100, width: 100, path: pngUrl, objectUrl: pngUrl, }, }, }); return ; }; export const ImageAttachmentWOThumbnail = (): JSX.Element => { const props = createProps({ rawAttachment: { contentType: IMAGE_PNG, fileName: 'sax.png', isVoiceMessage: false, }, }); return ; }; ImageAttachmentWOThumbnail.story = { name: 'Image Attachment w/o Thumbnail', }; export const ImageTapToView = (): JSX.Element => { const props = createProps({ text: '', isViewOnce: true, rawAttachment: { contentType: IMAGE_PNG, fileName: 'sax.png', isVoiceMessage: false, }, }); return ; }; ImageTapToView.story = { name: 'Image Tap-to-View', }; export const VideoOnly = (): JSX.Element => { const props = createProps({ rawAttachment: { contentType: VIDEO_MP4, fileName: 'great-video.mp4', isVoiceMessage: false, thumbnail: { contentType: IMAGE_PNG, height: 100, width: 100, path: pngUrl, objectUrl: pngUrl, }, }, }); // eslint-disable-next-line @typescript-eslint/no-explicit-any props.text = undefined as any; return ; }; export const VideoAttachment = (): JSX.Element => { const props = createProps({ rawAttachment: { contentType: VIDEO_MP4, fileName: 'great-video.mp4', isVoiceMessage: false, thumbnail: { contentType: IMAGE_PNG, height: 100, width: 100, path: pngUrl, objectUrl: pngUrl, }, }, }); return ; }; export const VideoAttachmentWOThumbnail = (): JSX.Element => { const props = createProps({ rawAttachment: { contentType: VIDEO_MP4, fileName: 'great-video.mp4', isVoiceMessage: false, }, }); return ; }; VideoAttachmentWOThumbnail.story = { name: 'Video Attachment w/o Thumbnail', }; export const VideoTapToView = (): JSX.Element => { const props = createProps({ text: '', isViewOnce: true, rawAttachment: { contentType: VIDEO_MP4, fileName: 'great-video.mp4', isVoiceMessage: false, }, }); return ; }; VideoTapToView.story = { name: 'Video Tap-to-View', }; export const GiftBadge = (): JSX.Element => { const props = createProps({ text: "Some text which shouldn't be rendered", isGiftBadge: true, }); return renderInMessage(props); }; export const AudioOnly = (): JSX.Element => { const props = createProps({ rawAttachment: { contentType: AUDIO_MP3, fileName: 'great-video.mp3', isVoiceMessage: false, }, }); // eslint-disable-next-line @typescript-eslint/no-explicit-any props.text = undefined as any; return ; }; export const AudioAttachment = (): JSX.Element => { const props = createProps({ rawAttachment: { contentType: AUDIO_MP3, fileName: 'great-video.mp3', isVoiceMessage: false, }, }); return ; }; export const VoiceMessageOnly = (): JSX.Element => { const props = createProps({ rawAttachment: { contentType: AUDIO_MP3, fileName: 'great-video.mp3', isVoiceMessage: true, }, }); // eslint-disable-next-line @typescript-eslint/no-explicit-any props.text = undefined as any; return ; }; export const VoiceMessageAttachment = (): JSX.Element => { const props = createProps({ rawAttachment: { contentType: AUDIO_MP3, fileName: 'great-video.mp3', isVoiceMessage: true, }, }); return ; }; export const OtherFileOnly = (): JSX.Element => { const props = createProps({ rawAttachment: { contentType: stringToMIMEType('application/json'), fileName: 'great-data.json', isVoiceMessage: false, }, }); // eslint-disable-next-line @typescript-eslint/no-explicit-any props.text = undefined as any; return ; }; export const MediaTapToView = (): JSX.Element => { const props = createProps({ text: '', isViewOnce: true, rawAttachment: { contentType: AUDIO_MP3, fileName: 'great-video.mp3', isVoiceMessage: false, }, }); return ; }; MediaTapToView.story = { name: 'Media Tap-to-View', }; export const OtherFileAttachment = (): JSX.Element => { const props = createProps({ rawAttachment: { contentType: stringToMIMEType('application/json'), fileName: 'great-data.json', isVoiceMessage: false, }, }); return ; }; export const LongMessageAttachmentShouldBeHidden = (): JSX.Element => { const props = createProps({ rawAttachment: { contentType: LONG_MESSAGE, fileName: 'signal-long-message-123.txt', isVoiceMessage: false, }, }); return ; }; LongMessageAttachmentShouldBeHidden.story = { name: 'Long message attachment (should be hidden)', }; export const NoCloseButton = (): JSX.Element => { const props = createProps(); props.onClose = undefined; return ; }; export const MessageNotFound = (): JSX.Element => { const props = createProps({ referencedMessageNotFound: true, }); return renderInMessage(props); }; export const MissingTextAttachment = (): JSX.Element => { const props = createProps(); // eslint-disable-next-line @typescript-eslint/no-explicit-any props.text = undefined as any; return ; }; MissingTextAttachment.story = { name: 'Missing Text & Attachment', }; export const MentionOutgoingAnotherAuthor = (): JSX.Element => { const props = createProps({ authorTitle: 'Tony Stark', text: '@Captain America Lunch later?', }); return ; }; MentionOutgoingAnotherAuthor.story = { name: '@mention + outgoing + another author', }; export const MentionOutgoingMe = (): JSX.Element => { const props = createProps({ isFromMe: true, text: '@Captain America Lunch later?', }); return ; }; MentionOutgoingMe.story = { name: '@mention + outgoing + me', }; export const MentionIncomingAnotherAuthor = (): JSX.Element => { const props = createProps({ authorTitle: 'Captain America', isIncoming: true, text: '@Tony Stark sure', }); return ; }; MentionIncomingAnotherAuthor.story = { name: '@mention + incoming + another author', }; export const MentionIncomingMe = (): JSX.Element => { const props = createProps({ isFromMe: true, isIncoming: true, text: '@Tony Stark sure', }); return ; }; MentionIncomingMe.story = { name: '@mention + incoming + me', }; export const CustomColor = (): JSX.Element => ( <> ); export const IsStoryReply = (): JSX.Element => { const props = createProps({ text: 'Wow!', }); return ( ); }; IsStoryReply.story = { name: 'isStoryReply', }; export const IsStoryReplyEmoji = (): JSX.Element => { const props = createProps(); return ( ); }; IsStoryReplyEmoji.story = { name: 'isStoryReply emoji', };