// Copyright 2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only import type { MouseEvent, ReactNode, JSX } from 'react'; import { useCallback, useState } from 'react'; import classNames from 'classnames'; import { CustomColorEditor } from './CustomColorEditor.dom.tsx'; import { Modal } from './Modal.dom.tsx'; import type { ConversationColorType, CustomColorType, } from '../types/Colors.std.ts'; import { ConversationColors } from '../types/Colors.std.ts'; import type { ConversationType } from '../state/ducks/conversations.preload.ts'; import type { LocalizerType } from '../types/Util.std.ts'; import { SampleMessageBubbles } from './SampleMessageBubbles.dom.tsx'; import { PanelRow } from './conversation/conversation-details/PanelRow.dom.tsx'; import { getCustomColorStyle } from '../util/getCustomColorStyle.dom.ts'; import { useDelayedRestoreFocus } from '../hooks/useRestoreFocus.dom.ts'; import { AxoDropdownMenu } from '../axo/AxoDropdownMenu.dom.tsx'; import { tw } from '../axo/tw.dom.tsx'; import { AxoConfirmDialog } from '../axo/AxoConfirmDialog.dom.tsx'; type CustomColorDataType = { id?: string; value?: CustomColorType; }; export type PropsDataType = { conversationId?: string; customColors?: Record; getConversationsWithCustomColor: (colorId: string) => Array; i18n: LocalizerType; isGlobal?: boolean; selectedColor?: ConversationColorType; selectedCustomColor: CustomColorDataType; }; type PropsActionType = { addCustomColor: (color: CustomColorType, conversationId?: string) => unknown; colorSelected: (payload: { conversationId: string; conversationColor?: ConversationColorType; customColorData?: { id: string; value: CustomColorType; }; }) => unknown; editCustomColor: (colorId: string, color: CustomColorType) => unknown; removeCustomColor: (colorId: string) => unknown; removeCustomColorOnConversations: (colorId: string) => unknown; resetAllChatColors: () => void; resetDefaultChatColor: () => void; setGlobalDefaultConversationColor: ( color: ConversationColorType, customColorData?: { id: string; value: CustomColorType; } ) => unknown; }; export type PropsType = PropsDataType & PropsActionType; export function ChatColorPicker({ addCustomColor, colorSelected, conversationId, customColors = {}, editCustomColor, getConversationsWithCustomColor, i18n, isGlobal = false, removeCustomColor, removeCustomColorOnConversations, resetAllChatColors, resetDefaultChatColor, selectedColor = ConversationColors[0], selectedCustomColor, setGlobalDefaultConversationColor, }: PropsType): JSX.Element { const [confirmResetAll, setConfirmResetAll] = useState(false); const [confirmResetWhat, setConfirmResetWhat] = useState(false); const [customColorToEdit, setCustomColorToEdit] = useState< CustomColorDataType | undefined >(undefined); const [focusRef] = useDelayedRestoreFocus(); const onSelectColor = ( conversationColor: ConversationColorType, customColorData?: { id: string; value: CustomColorType } ): void => { if (conversationId) { colorSelected({ conversationId, conversationColor, customColorData, }); } else { setGlobalDefaultConversationColor(conversationColor, customColorData); } }; const renderCustomColorEditorWrapper = () => ( setCustomColorToEdit(undefined)} onSave={(color: CustomColorType) => { if (customColorToEdit?.id) { editCustomColor(customColorToEdit.id, color); onSelectColor('custom', { id: customColorToEdit.id, value: color, }); } else { addCustomColor(color, conversationId); } }} /> ); return (
{customColorToEdit ? renderCustomColorEditorWrapper() : null} {i18n('icu:ChatColorPicker__confirm-reset-default')} { resetDefaultChatColor(); resetAllChatColors(); }} > {i18n('icu:ChatColorPicker__resetAll')} {i18n('icu:ChatColorPicker__confirm-reset')}
{ConversationColors.map((color, i) => (

{conversationId ? ( { colorSelected({ conversationId }); }} /> ) : null} { if (isGlobal) { setConfirmResetWhat(true); } else { setConfirmResetAll(true); } }} />
); } type CustomColorBubblePropsType = { color: CustomColorType; colorId: string; getConversationsWithCustomColor: (colorId: string) => Array; i18n: LocalizerType; isSelected: boolean; onDelete: () => void; onDupe: () => unknown; onEdit: () => unknown; onChoose: () => unknown; }; function CustomColorBubble({ color, colorId, getConversationsWithCustomColor, i18n, isSelected, onDelete, onDupe, onEdit, onChoose, }: CustomColorBubblePropsType): JSX.Element { const [confirmDeleteCount, setConfirmDeleteCount] = useState< number | undefined >(undefined); const handleClick = useCallback( (event: MouseEvent) => { if (!isSelected) { onChoose(); event.currentTarget.focus(); } }, [isSelected, onChoose] ); const handleDelete = useCallback(() => { const conversations = getConversationsWithCustomColor(colorId); if (!conversations.length) { onDelete(); } else { setConfirmDeleteCount(conversations.length); } }, [getConversationsWithCustomColor, colorId, onDelete]); return ( <> setConfirmDeleteCount(undefined)} title={i18n('icu:ChatColorPicker__delete--title')} description={i18n('icu:ChatColorPicker__delete--message', { num: confirmDeleteCount ?? 0, })} > {i18n('icu:ChatColorPicker__context--delete')}