// Copyright 2020-2022 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only import * as React from 'react'; import { action } from '@storybook/addon-actions'; import { select } from '@storybook/addon-knobs'; import { storiesOf } from '@storybook/react'; import type { PropsType } from './LeftPane'; import { LeftPane, LeftPaneMode } from './LeftPane'; import { CaptchaDialog } from './CaptchaDialog'; import { CrashReportDialog } from './CrashReportDialog'; import type { ConversationType } from '../state/ducks/conversations'; import { MessageSearchResult } from './conversationList/MessageSearchResult'; import { setupI18n } from '../util/setupI18n'; import enMessages from '../../_locales/en/messages.json'; import { ThemeType } from '../types/Util'; import { getDefaultConversation } from '../test-both/helpers/getDefaultConversation'; import { StorybookThemeContext } from '../../.storybook/StorybookThemeContext'; const i18n = setupI18n('en', enMessages); const story = storiesOf('Components/LeftPane', module); const defaultConversations: Array<ConversationType> = [ getDefaultConversation({ id: 'fred-convo', title: 'Fred Willard', }), getDefaultConversation({ id: 'marc-convo', isSelected: true, title: 'Marc Barraca', }), ]; const defaultGroups: Array<ConversationType> = [ getDefaultConversation({ id: 'biking-group', title: 'Mtn Biking Arizona 🚵☀️⛰', type: 'group', sharedGroupNames: [], }), getDefaultConversation({ id: 'dance-group', title: 'Are we dancers? 💃', type: 'group', sharedGroupNames: [], }), ]; const defaultArchivedConversations: Array<ConversationType> = [ getDefaultConversation({ id: 'michelle-archive-convo', title: 'Michelle Mercure', isArchived: true, }), ]; const pinnedConversations: Array<ConversationType> = [ getDefaultConversation({ id: 'philly-convo', isPinned: true, title: 'Philip Glass', }), getDefaultConversation({ id: 'robbo-convo', isPinned: true, title: 'Robert Moog', }), ]; const defaultModeSpecificProps = { mode: LeftPaneMode.Inbox as const, pinnedConversations, conversations: defaultConversations, archivedConversations: defaultArchivedConversations, isAboutToSearchInAConversation: false, startSearchCounter: 0, }; const emptySearchResultsGroup = { isLoading: false, results: [] }; const useProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({ cantAddContactToGroup: action('cantAddContactToGroup'), clearGroupCreationError: action('clearGroupCreationError'), clearSearch: action('clearSearch'), closeCantAddContactToGroupModal: action('closeCantAddContactToGroupModal'), closeMaximumGroupSizeModal: action('closeMaximumGroupSizeModal'), closeRecommendedGroupSizeModal: action('closeRecommendedGroupSizeModal'), composeDeleteAvatarFromDisk: action('composeDeleteAvatarFromDisk'), composeReplaceAvatar: action('composeReplaceAvatar'), composeSaveAvatarToDisk: action('composeSaveAvatarToDisk'), createGroup: action('createGroup'), getPreferredBadge: () => undefined, i18n, modeSpecificProps: defaultModeSpecificProps, preferredWidthFromStorage: 320, openConversationInternal: action('openConversationInternal'), regionCode: 'US', challengeStatus: select( 'challengeStatus', ['idle', 'required', 'pending'], 'idle' ), crashReportCount: select('challengeReportCount', [0, 1], 0), setChallengeStatus: action('setChallengeStatus'), renderExpiredBuildDialog: () => <div />, renderMainHeader: () => <div />, renderMessageSearchResult: (id: string) => ( <MessageSearchResult body="Lorem ipsum wow" bodyRanges={[]} conversationId="marc-convo" from={defaultConversations[0]} getPreferredBadge={() => undefined} i18n={i18n} id={id} openConversationInternal={action('openConversationInternal')} sentAt={1587358800000} snippet="Lorem <<left>>ipsum<<right>> wow" theme={ThemeType.light} to={defaultConversations[1]} /> ), renderNetworkStatus: () => <div />, renderRelinkDialog: () => <div />, renderUpdateDialog: () => <div />, renderCaptchaDialog: () => ( <CaptchaDialog i18n={i18n} isPending={overrideProps.challengeStatus === 'pending'} onContinue={action('onCaptchaContinue')} onSkip={action('onCaptchaSkip')} /> ), renderCrashReportDialog: () => ( <CrashReportDialog i18n={i18n} isPending={false} uploadCrashReports={action('uploadCrashReports')} eraseCrashReports={action('eraseCrashReports')} /> ), selectedConversationId: undefined, selectedMessageId: undefined, savePreferredLeftPaneWidth: action('savePreferredLeftPaneWidth'), searchInConversation: action('searchInConversation'), setComposeSearchTerm: action('setComposeSearchTerm'), setComposeGroupAvatar: action('setComposeGroupAvatar'), setComposeGroupName: action('setComposeGroupName'), setComposeGroupExpireTimer: action('setComposeGroupExpireTimer'), showArchivedConversations: action('showArchivedConversations'), showInbox: action('showInbox'), startComposing: action('startComposing'), showChooseGroupMembers: action('showChooseGroupMembers'), startNewConversationFromPhoneNumber: action( 'startNewConversationFromPhoneNumber' ), startNewConversationFromUsername: action('startNewConversationFromUsername'), startSearch: action('startSearch'), startSettingGroupMetadata: action('startSettingGroupMetadata'), theme: React.useContext(StorybookThemeContext), toggleComposeEditingAvatar: action('toggleComposeEditingAvatar'), toggleConversationInChooseMembers: action( 'toggleConversationInChooseMembers' ), updateSearchTerm: action('updateSearchTerm'), ...overrideProps, }); // Inbox stories story.add('Inbox: no conversations', () => ( <LeftPane {...useProps({ modeSpecificProps: { mode: LeftPaneMode.Inbox, pinnedConversations: [], conversations: [], archivedConversations: [], isAboutToSearchInAConversation: false, startSearchCounter: 0, }, })} /> )); story.add('Inbox: only pinned conversations', () => ( <LeftPane {...useProps({ modeSpecificProps: { mode: LeftPaneMode.Inbox, pinnedConversations, conversations: [], archivedConversations: [], isAboutToSearchInAConversation: false, startSearchCounter: 0, }, })} /> )); story.add('Inbox: only non-pinned conversations', () => ( <LeftPane {...useProps({ modeSpecificProps: { mode: LeftPaneMode.Inbox, pinnedConversations: [], conversations: defaultConversations, archivedConversations: [], isAboutToSearchInAConversation: false, startSearchCounter: 0, }, })} /> )); story.add('Inbox: only archived conversations', () => ( <LeftPane {...useProps({ modeSpecificProps: { mode: LeftPaneMode.Inbox, pinnedConversations: [], conversations: [], archivedConversations: defaultArchivedConversations, isAboutToSearchInAConversation: false, startSearchCounter: 0, }, })} /> )); story.add('Inbox: pinned and archived conversations', () => ( <LeftPane {...useProps({ modeSpecificProps: { mode: LeftPaneMode.Inbox, pinnedConversations, conversations: [], archivedConversations: defaultArchivedConversations, isAboutToSearchInAConversation: false, startSearchCounter: 0, }, })} /> )); story.add('Inbox: non-pinned and archived conversations', () => ( <LeftPane {...useProps({ modeSpecificProps: { mode: LeftPaneMode.Inbox, pinnedConversations: [], conversations: defaultConversations, archivedConversations: defaultArchivedConversations, isAboutToSearchInAConversation: false, startSearchCounter: 0, }, })} /> )); story.add('Inbox: pinned and non-pinned conversations', () => ( <LeftPane {...useProps({ modeSpecificProps: { mode: LeftPaneMode.Inbox, pinnedConversations, conversations: defaultConversations, archivedConversations: [], isAboutToSearchInAConversation: false, startSearchCounter: 0, }, })} /> )); story.add('Inbox: pinned, non-pinned, and archived conversations', () => ( <LeftPane {...useProps()} /> )); // Search stories story.add('Search: no results when searching everywhere', () => ( <LeftPane {...useProps({ modeSpecificProps: { mode: LeftPaneMode.Search, conversationResults: emptySearchResultsGroup, contactResults: emptySearchResultsGroup, messageResults: emptySearchResultsGroup, searchTerm: 'foo bar', primarySendsSms: false, }, })} /> )); story.add('Search: no results when searching everywhere (SMS)', () => ( <LeftPane {...useProps({ modeSpecificProps: { mode: LeftPaneMode.Search, conversationResults: emptySearchResultsGroup, contactResults: emptySearchResultsGroup, messageResults: emptySearchResultsGroup, searchTerm: 'foo bar', primarySendsSms: true, }, })} /> )); story.add('Search: no results when searching in a conversation', () => ( <LeftPane {...useProps({ modeSpecificProps: { mode: LeftPaneMode.Search, conversationResults: emptySearchResultsGroup, contactResults: emptySearchResultsGroup, messageResults: emptySearchResultsGroup, searchConversationName: 'Bing Bong', searchTerm: 'foo bar', primarySendsSms: false, }, })} /> )); story.add('Search: all results loading', () => ( <LeftPane {...useProps({ modeSpecificProps: { mode: LeftPaneMode.Search, conversationResults: { isLoading: true }, contactResults: { isLoading: true }, messageResults: { isLoading: true }, searchTerm: 'foo bar', primarySendsSms: false, }, })} /> )); story.add('Search: some results loading', () => ( <LeftPane {...useProps({ modeSpecificProps: { mode: LeftPaneMode.Search, conversationResults: { isLoading: false, results: defaultConversations, }, contactResults: { isLoading: true }, messageResults: { isLoading: true }, searchTerm: 'foo bar', primarySendsSms: false, }, })} /> )); story.add('Search: has conversations and contacts, but not messages', () => ( <LeftPane {...useProps({ modeSpecificProps: { mode: LeftPaneMode.Search, conversationResults: { isLoading: false, results: defaultConversations, }, contactResults: { isLoading: false, results: defaultConversations }, messageResults: { isLoading: false, results: [] }, searchTerm: 'foo bar', primarySendsSms: false, }, })} /> )); story.add('Search: all results', () => ( <LeftPane {...useProps({ modeSpecificProps: { mode: LeftPaneMode.Search, conversationResults: { isLoading: false, results: defaultConversations, }, contactResults: { isLoading: false, results: defaultConversations }, messageResults: { isLoading: false, results: [ { id: 'msg1', conversationId: 'foo' }, { id: 'msg2', conversationId: 'bar' }, ], }, searchTerm: 'foo bar', primarySendsSms: false, }, })} /> )); // Archived stories story.add('Archive: no archived conversations', () => ( <LeftPane {...useProps({ modeSpecificProps: { mode: LeftPaneMode.Archive, archivedConversations: [], searchConversation: undefined, searchTerm: '', }, })} /> )); story.add('Archive: archived conversations', () => ( <LeftPane {...useProps({ modeSpecificProps: { mode: LeftPaneMode.Archive, archivedConversations: defaultConversations, searchConversation: undefined, searchTerm: '', }, })} /> )); story.add('Archive: searching a conversation', () => ( <LeftPane {...useProps({ modeSpecificProps: { mode: LeftPaneMode.Archive, archivedConversations: defaultConversations, searchConversation: undefined, searchTerm: '', }, })} /> )); // Compose stories story.add('Compose: no results', () => ( <LeftPane {...useProps({ modeSpecificProps: { mode: LeftPaneMode.Compose, composeContacts: [], composeGroups: [], isUsernamesEnabled: true, isFetchingUsername: false, regionCode: 'US', searchTerm: '', }, })} /> )); story.add('Compose: some contacts, no search term', () => ( <LeftPane {...useProps({ modeSpecificProps: { mode: LeftPaneMode.Compose, composeContacts: defaultConversations, composeGroups: [], isUsernamesEnabled: true, isFetchingUsername: false, regionCode: 'US', searchTerm: '', }, })} /> )); story.add('Compose: some contacts, with a search term', () => ( <LeftPane {...useProps({ modeSpecificProps: { mode: LeftPaneMode.Compose, composeContacts: defaultConversations, composeGroups: [], isUsernamesEnabled: true, isFetchingUsername: false, regionCode: 'US', searchTerm: 'ar', }, })} /> )); story.add('Compose: some groups, no search term', () => ( <LeftPane {...useProps({ modeSpecificProps: { mode: LeftPaneMode.Compose, composeContacts: [], composeGroups: defaultGroups, isUsernamesEnabled: true, isFetchingUsername: false, regionCode: 'US', searchTerm: '', }, })} /> )); story.add('Compose: some groups, with search term', () => ( <LeftPane {...useProps({ modeSpecificProps: { mode: LeftPaneMode.Compose, composeContacts: [], composeGroups: defaultGroups, isUsernamesEnabled: true, isFetchingUsername: false, regionCode: 'US', searchTerm: 'ar', }, })} /> )); story.add('Compose: search is valid username', () => ( <LeftPane {...useProps({ modeSpecificProps: { mode: LeftPaneMode.Compose, composeContacts: [], composeGroups: [], isUsernamesEnabled: true, isFetchingUsername: false, regionCode: 'US', searchTerm: 'someone', }, })} /> )); story.add('Compose: search is valid username, fetching username', () => ( <LeftPane {...useProps({ modeSpecificProps: { mode: LeftPaneMode.Compose, composeContacts: [], composeGroups: [], isUsernamesEnabled: true, isFetchingUsername: true, regionCode: 'US', searchTerm: 'someone', }, })} /> )); story.add('Compose: search is valid username, but flag is not enabled', () => ( <LeftPane {...useProps({ modeSpecificProps: { mode: LeftPaneMode.Compose, composeContacts: [], composeGroups: [], isUsernamesEnabled: false, isFetchingUsername: false, regionCode: 'US', searchTerm: 'someone', }, })} /> )); story.add('Compose: all kinds of results, no search term', () => ( <LeftPane {...useProps({ modeSpecificProps: { mode: LeftPaneMode.Compose, composeContacts: defaultConversations, composeGroups: defaultGroups, isUsernamesEnabled: true, isFetchingUsername: false, regionCode: 'US', searchTerm: '', }, })} /> )); story.add('Compose: all kinds of results, with a search term', () => ( <LeftPane {...useProps({ modeSpecificProps: { mode: LeftPaneMode.Compose, composeContacts: defaultConversations, composeGroups: defaultGroups, isUsernamesEnabled: true, isFetchingUsername: false, regionCode: 'US', searchTerm: 'someone', }, })} /> )); // Captcha flow story.add('Captcha dialog: required', () => ( <LeftPane {...useProps({ modeSpecificProps: { mode: LeftPaneMode.Inbox, pinnedConversations, conversations: defaultConversations, archivedConversations: [], isAboutToSearchInAConversation: false, startSearchCounter: 0, }, challengeStatus: 'required', })} /> )); story.add('Captcha dialog: pending', () => ( <LeftPane {...useProps({ modeSpecificProps: { mode: LeftPaneMode.Inbox, pinnedConversations, conversations: defaultConversations, archivedConversations: [], isAboutToSearchInAConversation: false, startSearchCounter: 0, }, challengeStatus: 'pending', })} /> )); // Crash report flow story.add('Crash report dialog', () => ( <LeftPane {...useProps({ modeSpecificProps: { mode: LeftPaneMode.Inbox, pinnedConversations, conversations: defaultConversations, archivedConversations: [], isAboutToSearchInAConversation: false, startSearchCounter: 0, }, crashReportCount: 42, })} /> )); // Set group metadata story.add('Group Metadata: No Timer', () => ( <LeftPane {...useProps({ modeSpecificProps: { mode: LeftPaneMode.SetGroupMetadata, groupAvatar: undefined, groupName: 'Group 1', groupExpireTimer: 0, hasError: false, isCreating: false, isEditingAvatar: false, selectedContacts: defaultConversations, userAvatarData: [], }, })} /> )); story.add('Group Metadata: Regular Timer', () => ( <LeftPane {...useProps({ modeSpecificProps: { mode: LeftPaneMode.SetGroupMetadata, groupAvatar: undefined, groupName: 'Group 1', groupExpireTimer: 24 * 3600, hasError: false, isCreating: false, isEditingAvatar: false, selectedContacts: defaultConversations, userAvatarData: [], }, })} /> )); story.add('Group Metadata: Custom Timer', () => ( <LeftPane {...useProps({ modeSpecificProps: { mode: LeftPaneMode.SetGroupMetadata, groupAvatar: undefined, groupName: 'Group 1', groupExpireTimer: 7 * 3600, hasError: false, isCreating: false, isEditingAvatar: false, selectedContacts: defaultConversations, userAvatarData: [], }, })} /> ));