// Copyright 2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only import React from 'react'; import type { ContactModalStateType, DeleteMessagesPropsType, EditHistoryMessagesType, EditNicknameAndNoteModalPropsType, ForwardMessagesPropsType, MessageRequestActionsConfirmationPropsType, SafetyNumberChangedBlockingDataType, UserNotFoundModalStateType, } from '../state/ducks/globalModals'; import type { LocalizerType, ThemeType } from '../types/Util'; import { UsernameOnboardingState } from '../types/globalModals'; import { missingCaseError } from '../util/missingCaseError'; import { ButtonVariant } from './Button'; import { ConfirmationDialog } from './ConfirmationDialog'; import { SignalConnectionsModal } from './SignalConnectionsModal'; import { WhatsNewModal } from './WhatsNewModal'; import { MediaPermissionsModal } from './MediaPermissionsModal'; import type { StartCallData } from './ConfirmLeaveCallModal'; import type { AttachmentNotAvailableModalType } from './AttachmentNotAvailableModal'; import { TapToViewNotAvailableModal, type DataPropsType as TapToViewNotAvailablePropsType, } from './TapToViewNotAvailableModal'; import { BackfillFailureModal, type DataPropsType as BackfillFailureModalPropsType, } from './BackfillFailureModal'; import type { SmartDraftGifMessageSendModalProps } from '../state/smart/DraftGifMessageSendModal'; import { CriticalIdlePrimaryDeviceModal } from './CriticalIdlePrimaryDeviceModal'; import { LowDiskSpaceBackupImportModal } from './LowDiskSpaceBackupImportModal'; // NOTE: All types should be required for this component so that the smart // component gives you type errors when adding/removing props. export type PropsType = { i18n: LocalizerType; theme: ThemeType; // AddUserToAnotherGroupModal addUserToAnotherGroupModalContactId: string | undefined; renderAddUserToAnotherGroup: () => JSX.Element; // AttachmentNotAvailableModal attachmentNotAvailableModalType: AttachmentNotAvailableModalType | undefined; renderAttachmentNotAvailableModal: () => JSX.Element; // CallLinkAddNameModal callLinkAddNameModalRoomId: string | null; renderCallLinkAddNameModal: () => JSX.Element; // CallLinkEditModal callLinkEditModalRoomId: string | null; renderCallLinkEditModal: () => JSX.Element; // CallLinkPendingParticipantModal callLinkPendingParticipantContactId: string | undefined; renderCallLinkPendingParticipantModal: () => JSX.Element; // ConfirmLeaveCallModal confirmLeaveCallModalState: StartCallData | null; renderConfirmLeaveCallModal: () => JSX.Element; // ContactModal contactModalState: ContactModalStateType | undefined; renderContactModal: () => JSX.Element; // EditHistoryMessagesModal editHistoryMessages: EditHistoryMessagesType | undefined; renderEditHistoryMessagesModal: () => JSX.Element; // EditNicknameAndNoteModal editNicknameAndNoteModalProps: EditNicknameAndNoteModalPropsType | null; renderEditNicknameAndNoteModal: () => JSX.Element; // ErrorModal errorModalProps: | { buttonVariant?: ButtonVariant; description?: string; title?: string | null; } | undefined; renderErrorModal: (opts: { buttonVariant?: ButtonVariant; description?: string; title?: string | null; }) => JSX.Element; // DeleteMessageModal deleteMessagesProps: DeleteMessagesPropsType | undefined; renderDeleteMessagesModal: () => JSX.Element; // DraftGifMessageSendModal draftGifMessageSendModalProps: SmartDraftGifMessageSendModalProps | null; renderDraftGifMessageSendModal: () => JSX.Element; // ForwardMessageModal forwardMessagesProps: ForwardMessagesPropsType | undefined; renderForwardMessagesModal: () => JSX.Element; // MediaPermissionsModal mediaPermissionsModalProps: | { mediaType: 'camera' | 'microphone'; requestor: 'call' | 'voiceNote'; } | undefined; closeMediaPermissionsModal: () => void; openSystemMediaPermissions: (mediaType: 'camera' | 'microphone') => void; // MessageRequestActionsConfirmation messageRequestActionsConfirmationProps: MessageRequestActionsConfirmationPropsType | null; renderMessageRequestActionsConfirmation: () => JSX.Element; // NotePreviewModal notePreviewModalProps: { conversationId: string } | null; renderNotePreviewModal: () => JSX.Element; // SafetyNumberModal safetyNumberModalContactId: string | undefined; renderSafetyNumber: () => JSX.Element; // ShortcutGuideModal isShortcutGuideModalVisible: boolean; renderShortcutGuideModal: () => JSX.Element; // SignalConnectionsModal isSignalConnectionsVisible: boolean; toggleSignalConnectionsModal: () => unknown; // AboutContactModal isAboutContactModalVisible: boolean; renderAboutContactModal: () => JSX.Element | null; // StickerPackPreviewModal stickerPackPreviewId: string | undefined; renderStickerPreviewModal: () => JSX.Element | null; // StoriesSettings isStoriesSettingsVisible: boolean; renderStoriesSettings: () => JSX.Element; // SendAnywayDialog hasSafetyNumberChangeModal: boolean; safetyNumberChangedBlockingData: | SafetyNumberChangedBlockingDataType | undefined; renderSendAnywayDialog: () => JSX.Element; // TapToViewNotAvailableModal tapToViewNotAvailableModalProps: TapToViewNotAvailablePropsType | undefined; hideTapToViewNotAvailableModal: () => void; // BackfillFailureModal backfillFailureModalProps: BackfillFailureModalPropsType | undefined; hideBackfillFailureModal: () => void; // UserNotFoundModal hideUserNotFoundModal: () => unknown; userNotFoundModalState: UserNotFoundModalStateType | undefined; // WhatsNewModal isWhatsNewVisible: boolean; hideWhatsNewModal: () => unknown; // UsernameOnboarding usernameOnboardingState: UsernameOnboardingState; renderUsernameOnboarding: () => JSX.Element; isProfileNameWarningModalVisible: boolean; profileNameWarningModalConversationType?: string; renderProfileNameWarningModal: () => JSX.Element; // CriticalIdlePrimaryDeviceModal, criticalIdlePrimaryDeviceModal: boolean; hideCriticalIdlePrimaryDeviceModal: () => void; // LowDiskSpaceBackupImportModal lowDiskSpaceBackupImportModal: { bytesNeeded: number } | null; hideLowDiskSpaceBackupImportModal: () => void; }; export function GlobalModalContainer({ i18n, // AttachmentNotAvailableModal attachmentNotAvailableModalType, renderAttachmentNotAvailableModal, // AddUserToAnotherGroupModal addUserToAnotherGroupModalContactId, renderAddUserToAnotherGroup, // CallLinkAddNameModal callLinkAddNameModalRoomId, renderCallLinkAddNameModal, // CallLinkEditModal callLinkEditModalRoomId, renderCallLinkEditModal, // CallLinkPendingParticipantModal callLinkPendingParticipantContactId, renderCallLinkPendingParticipantModal, // ConfirmLeaveCallModal confirmLeaveCallModalState, renderConfirmLeaveCallModal, // ContactModal contactModalState, renderContactModal, // EditHistoryMessages editHistoryMessages, renderEditHistoryMessagesModal, // EditNicknameAndNoteModal editNicknameAndNoteModalProps, renderEditNicknameAndNoteModal, // ErrorModal errorModalProps, renderErrorModal, // DeleteMessageModal deleteMessagesProps, renderDeleteMessagesModal, // DraftGifMessageSendModal draftGifMessageSendModalProps, renderDraftGifMessageSendModal, // ForwardMessageModal forwardMessagesProps, renderForwardMessagesModal, // MediaPermissionsModal mediaPermissionsModalProps, closeMediaPermissionsModal, openSystemMediaPermissions, // MessageRequestActionsConfirmation messageRequestActionsConfirmationProps, renderMessageRequestActionsConfirmation, // NotePreviewModal notePreviewModalProps, renderNotePreviewModal, // SafetyNumberModal safetyNumberModalContactId, renderSafetyNumber, // ShortcutGuideModal isShortcutGuideModalVisible, renderShortcutGuideModal, // SignalConnectionsModal isSignalConnectionsVisible, toggleSignalConnectionsModal, // AboutContactModal isAboutContactModalVisible, renderAboutContactModal, // StickerPackPreviewModal stickerPackPreviewId, renderStickerPreviewModal, // StoriesSettings isStoriesSettingsVisible, renderStoriesSettings, // SendAnywayDialog hasSafetyNumberChangeModal, safetyNumberChangedBlockingData, renderSendAnywayDialog, // TapToViewNotAvailableModal tapToViewNotAvailableModalProps, hideTapToViewNotAvailableModal, // BackfillFailureModal backfillFailureModalProps, hideBackfillFailureModal, // UserNotFoundModal hideUserNotFoundModal, userNotFoundModalState, // WhatsNewModal hideWhatsNewModal, isWhatsNewVisible, // UsernameOnboarding usernameOnboardingState, renderUsernameOnboarding, // ProfileNameWarningModal isProfileNameWarningModalVisible, renderProfileNameWarningModal, // CriticalIdlePrimaryDeviceModal criticalIdlePrimaryDeviceModal, hideCriticalIdlePrimaryDeviceModal, // LowDiskSpaceBackupImportModal lowDiskSpaceBackupImportModal, hideLowDiskSpaceBackupImportModal, }: PropsType): JSX.Element | null { // We want the following dialogs to show in this order: // 1. Errors // 2. Safety Number Changes // 3. Forward Modal, so other modals can open it // 4. The Rest (in no particular order, but they're ordered alphabetically) // Errors if (errorModalProps) { return renderErrorModal(errorModalProps); } // Safety Number if (hasSafetyNumberChangeModal || safetyNumberChangedBlockingData) { return renderSendAnywayDialog(); } // Forward Modal if (forwardMessagesProps) { return renderForwardMessagesModal(); } // Media Permissions Modal if (mediaPermissionsModalProps) { return ( ); } // The Rest if (confirmLeaveCallModalState) { return renderConfirmLeaveCallModal(); } if (addUserToAnotherGroupModalContactId) { return renderAddUserToAnotherGroup(); } if (callLinkAddNameModalRoomId) { return renderCallLinkAddNameModal(); } if (callLinkEditModalRoomId) { return renderCallLinkEditModal(); } if (editHistoryMessages) { return renderEditHistoryMessagesModal(); } if (editNicknameAndNoteModalProps) { return renderEditNicknameAndNoteModal(); } if (deleteMessagesProps) { return renderDeleteMessagesModal(); } if (draftGifMessageSendModalProps) { return renderDraftGifMessageSendModal(); } if (messageRequestActionsConfirmationProps) { return renderMessageRequestActionsConfirmation(); } if (notePreviewModalProps) { return renderNotePreviewModal(); } if (isProfileNameWarningModalVisible) { return renderProfileNameWarningModal(); } if (isShortcutGuideModalVisible) { return renderShortcutGuideModal(); } if (isSignalConnectionsVisible) { return ( ); } if (safetyNumberModalContactId) { return renderSafetyNumber(); } if (isAboutContactModalVisible) { return renderAboutContactModal(); } if (contactModalState) { return renderContactModal(); } // This needs to be after the about contact modal because the pending participant modal // opens the about contact modal if (callLinkPendingParticipantContactId) { return renderCallLinkPendingParticipantModal(); } if (isStoriesSettingsVisible) { return renderStoriesSettings(); } if (isWhatsNewVisible) { return ; } if (usernameOnboardingState === UsernameOnboardingState.Open) { return renderUsernameOnboarding(); } if (stickerPackPreviewId) { return renderStickerPreviewModal(); } if (userNotFoundModalState) { let content: string; if (userNotFoundModalState.type === 'phoneNumber') { content = i18n('icu:startConversation--phone-number-not-found', { phoneNumber: userNotFoundModalState.phoneNumber, }); } else if (userNotFoundModalState.type === 'username') { content = i18n('icu:startConversation--username-not-found', { atUsername: userNotFoundModalState.username, }); } else { throw missingCaseError(userNotFoundModalState); } return ( {content} ); } if (attachmentNotAvailableModalType) { return renderAttachmentNotAvailableModal(); } if (tapToViewNotAvailableModalProps) { return ( ); } if (backfillFailureModalProps != null) { return ( ); } if (criticalIdlePrimaryDeviceModal) { return ( ); } if (lowDiskSpaceBackupImportModal) { return ( ); } return null; }