// Copyright 2025 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only import type { MutableRefObject } from 'react'; import React, { useCallback, useMemo, useState } from 'react'; import type { ConversationType } from '../../state/ducks/conversations'; import type { PreferredBadgeSelectorType } from '../../state/selectors/badges'; import type { LocalizerType } from '../../types/I18N'; import type { ThemeType } from '../../types/Util'; import { Input } from '../Input'; import { Button, ButtonVariant } from '../Button'; import { ConfirmationDialog } from '../ConfirmationDialog'; import type { ChatFolderSelection } from './EditChatFoldersSelectChatsDialog'; import { EditChatFoldersSelectChatsDialog } from './EditChatFoldersSelectChatsDialog'; import { SettingsRow } from '../PreferencesUtil'; import { Checkbox } from '../Checkbox'; import { Avatar, AvatarSize } from '../Avatar'; import { PreferencesContent } from '../Preferences'; import type { ChatFolderId } from '../../types/ChatFolder'; import { CHAT_FOLDER_NAME_MAX_CHAR_LENGTH, isSameChatFolderParams, normalizeChatFolderParams, validateChatFolderParams, type ChatFolderParams, } from '../../types/ChatFolder'; import type { GetConversationByIdType } from '../../state/selectors/conversations'; import { strictAssert } from '../../util/assert'; export function EditChatFoldersPage(props: { i18n: LocalizerType; existingChatFolderId: ChatFolderId | null; initChatFolderParams: ChatFolderParams; onBack: () => void; conversations: ReadonlyArray; getPreferredBadge: PreferredBadgeSelectorType; theme: ThemeType; settingsPaneRef: MutableRefObject; conversationSelector: GetConversationByIdType; onDeleteChatFolder: (chatFolderId: ChatFolderId) => void; onCreateChatFolder: (chatFolderParams: ChatFolderParams) => void; onUpdateChatFolder: ( chatFolderId: ChatFolderId, chatFolderParams: ChatFolderParams ) => void; }): JSX.Element { const { i18n, initChatFolderParams, existingChatFolderId, onCreateChatFolder, onUpdateChatFolder, onDeleteChatFolder, onBack, conversationSelector, } = props; const [chatFolderParams, setChatFolderParams] = useState(initChatFolderParams); const [showInclusionsDialog, setShowInclusionsDialog] = useState(false); const [showExclusionsDialog, setShowExclusionsDialog] = useState(false); const [showDeleteFolderDialog, setShowDeleteFolderDialog] = useState(false); const [showSaveChangesDialog, setShowSaveChangesDialog] = useState(false); const normalizedChatFolderParams = useMemo(() => { return normalizeChatFolderParams(chatFolderParams); }, [chatFolderParams]); const isChanged = useMemo(() => { return !isSameChatFolderParams( initChatFolderParams, normalizedChatFolderParams ); }, [initChatFolderParams, normalizedChatFolderParams]); const isValid = useMemo(() => { return validateChatFolderParams(normalizedChatFolderParams); }, [normalizedChatFolderParams]); const handleNameChange = useCallback((newName: string) => { setChatFolderParams(prevParams => { return { ...prevParams, name: newName }; }); }, []); const handleShowOnlyUnreadChange = useCallback((newValue: boolean) => { setChatFolderParams(prevParams => { return { ...prevParams, showOnlyUnread: newValue }; }); }, []); const handleShowMutedChatsChange = useCallback((newValue: boolean) => { setChatFolderParams(prevParams => { return { ...prevParams, showMutedChats: newValue }; }); }, []); const handleBackInit = useCallback(() => { if (!isChanged) { onBack(); } else { setShowSaveChangesDialog(true); } }, [isChanged, onBack]); const handleDiscard = useCallback(() => { onBack(); }, [onBack]); const handleSaveClose = useCallback(() => { setShowSaveChangesDialog(false); }, []); const handleSave = useCallback(() => { strictAssert(isChanged, 'tried saving when unchanged'); strictAssert(isValid, 'tried saving when invalid'); if (existingChatFolderId != null) { onUpdateChatFolder(existingChatFolderId, chatFolderParams); } else { onCreateChatFolder(chatFolderParams); } onBack(); }, [ onBack, existingChatFolderId, isChanged, isValid, chatFolderParams, onCreateChatFolder, onUpdateChatFolder, ]); const handleDeleteInit = useCallback(() => { setShowDeleteFolderDialog(true); }, []); const handleDeleteConfirm = useCallback(() => { strictAssert(existingChatFolderId, 'Missing existing chat folder id'); onDeleteChatFolder(existingChatFolderId); setShowDeleteFolderDialog(false); onBack(); }, [existingChatFolderId, onDeleteChatFolder, onBack]); const handleDeleteClose = useCallback(() => { setShowDeleteFolderDialog(false); }, []); const handleSelectInclusions = useCallback(() => { setShowInclusionsDialog(true); }, []); const handleSelectExclusions = useCallback(() => { setShowExclusionsDialog(true); }, []); const handleCloseInclusions = useCallback( (selection: ChatFolderSelection) => { setChatFolderParams(prevParams => { return { ...prevParams, includeAllIndividualChats: selection.selectAllIndividualChats, includeAllGroupChats: selection.selectAllGroupChats, includedConversationIds: selection.selectedRecipientIds, }; }); setShowInclusionsDialog(false); }, [] ); const handleCloseExclusions = useCallback( (selection: ChatFolderSelection) => { setChatFolderParams(prevParams => { return { ...prevParams, includeAllIndividualChats: !selection.selectAllIndividualChats, includeAllGroupChats: !selection.selectAllGroupChats, excludedConversationIds: selection.selectedRecipientIds, }; }); setShowExclusionsDialog(false); }, [] ); return ( } contents={ <>
    {chatFolderParams.includeAllIndividualChats && (
  • 1:1 Chats
  • )} {chatFolderParams.includeAllGroupChats && (
  • Group Chats
  • )} {chatFolderParams.includedConversationIds.map(conversationId => { const conversation = conversationSelector(conversationId); return (
  • {conversation.title}
  • ); })}

{i18n( 'icu:Preferences__EditChatFolderPage__IncludedChatsSection__Help' )}

    {chatFolderParams.excludedConversationIds.map(conversationId => { const conversation = conversationSelector(conversationId); return (
  • {conversation.title}
  • ); })}

{i18n( 'icu:Preferences__EditChatFolderPage__ExceptionsSection__Help' )}

{props.existingChatFolderId != null && (
)} {showInclusionsDialog && ( )} {showExclusionsDialog && ( )} {showDeleteFolderDialog && ( )} {showSaveChangesDialog && ( )} } contentsRef={props.settingsPaneRef} title={i18n('icu:Preferences__EditChatFolderPage__Title')} actions={ <> } /> ); } function DeleteChatFolderDialog(props: { i18n: LocalizerType; onConfirm: () => void; onClose: () => void; }) { const { i18n } = props; return ( {i18n( 'icu:Preferences__EditChatFolderPage__DeleteChatFolderDialog__Description' )} ); } function SaveChangesFolderDialog(props: { i18n: LocalizerType; onSave: () => void; onCancel: () => void; onClose: () => void; }) { const { i18n } = props; return ( {i18n( 'icu:Preferences__EditChatFolderPage__SaveChangesFolderDialog__Description' )} ); }