// SPDX-License-Identifier: AGPL-3.0-or-later import Accessibility from '@app/features/accessibility/state/Accessibility'; import {transitionMobileTextareaButtonState} from '@app/features/channel/components/textarea/MobileTextareaButtonStateMachine'; import {TextareaButton} from '@app/features/channel/components/textarea/TextareaButton'; import textareaButtonsStyles from '@app/features/channel/components/textarea/TextareaButtons.module.css'; import styles from '@app/features/channel/components/textarea/TextareaInput.module.css'; import VoiceMessageRecorder from '@app/features/channel/components/VoiceMessageRecorder'; import type {ExpressionPickerTabType} from '@app/features/expressions/components/popouts/ExpressionPickerPopout'; import { EMOJIS_DESCRIPTOR, GIFS_DESCRIPTOR, MEDIA_DESCRIPTOR, STICKERS_DESCRIPTOR, } from '@app/features/i18n/utils/CommonMessageDescriptors'; import {getReducedMotionProps, type MotionAnimation} from '@app/features/ui/utils/ReducedMotionAnimation'; import {msg} from '@lingui/core/macro'; import {useLingui} from '@lingui/react/macro'; import {GifIcon, ImageSquareIcon, PaperPlaneRightIcon, SmileyIcon, StickerIcon} from '@phosphor-icons/react'; import {clsx} from 'clsx'; import {AnimatePresence, motion} from 'framer-motion'; import React, {useMemo} from 'react'; const SEND_MESSAGE_DESCRIPTOR = msg({ message: 'Send message', comment: 'Button or menu action label in the channel and chat textarea buttons. Keep it concise.', }); const MOBILE_BUTTON_SWAP_MOTION: MotionAnimation = { initial: {opacity: 0, scale: 0.86, y: 3}, animate: {opacity: 1, scale: 1, y: 0}, exit: {opacity: 0, scale: 0.9, y: -2}, transition: { opacity: {duration: 0.08}, y: {duration: 0.08, ease: 'easeOut'}, scale: {type: 'spring', stiffness: 560, damping: 28, mass: 0.55}, }, }; interface TextareaButtonsProps { disabled: boolean; showAllButtons: boolean; showGifButton: boolean; showMemesButton: boolean; showStickersButton: boolean; showEmojiButton: boolean; showMessageSendButton: boolean; canRecordVoice: boolean; isEditingMessage: boolean; hasPendingSticker: boolean; voiceTooltipAnchorRef?: React.RefObject; channelId: string; expressionPickerOpen: boolean; selectedTab: ExpressionPickerTabType; isMobile: boolean; isSlowmodeActive: boolean; isOverLimit: boolean; hasContent: boolean; hasAttachments: boolean; expressionPickerTriggerRef: React.RefObject; invisibleExpressionPickerTriggerRef: React.RefObject; onExpressionPickerToggle: (tab: ExpressionPickerTabType) => void; onSubmit: () => void; disableSendButton?: boolean; } export const TextareaButtons = React.forwardRef( ( { disabled, showAllButtons, showGifButton, showMemesButton, showStickersButton, showEmojiButton, showMessageSendButton, canRecordVoice, isEditingMessage, hasPendingSticker, voiceTooltipAnchorRef, channelId, expressionPickerOpen, selectedTab, isMobile, isSlowmodeActive, isOverLimit, hasContent, hasAttachments, expressionPickerTriggerRef, invisibleExpressionPickerTriggerRef, onExpressionPickerToggle, onSubmit, disableSendButton, }, ref, ) => { const {i18n} = useLingui(); const buttonModel = useMemo( () => transitionMobileTextareaButtonState({ disabled, canRecordVoice, value: '', isSlowmodeActive, isOverCharacterLimit: isOverLimit, isEditingMessage, hasContent, hasAttachments, hasPendingSticker, }), [ canRecordVoice, disabled, hasAttachments, hasContent, hasPendingSticker, isEditingMessage, isOverLimit, isSlowmodeActive, ], ); if (disabled) { return null; } const buttonSwapMotion = getReducedMotionProps(MOBILE_BUTTON_SWAP_MOTION, Accessibility.useReducedMotion); const shouldShowDesktopSendButton = showMessageSendButton; const desktopSendDisabled = isSlowmodeActive || isOverLimit || (!hasContent && !hasAttachments) || disableSendButton; const shouldShowVoiceButton = buttonModel.visibleButton === 'voice'; const mobileSendDisabled = buttonModel.sendButton.disabled || Boolean(disableSendButton); return (
{!isMobile && showAllButtons && ( <> {showGifButton && ( onExpressionPickerToggle('gifs')} data-expression-picker-tab="gifs" keybindAction="chat_toggle_gif" data-flx="channel.textarea.textarea-buttons.textarea-button.expression-picker-toggle" /> )} {showMemesButton && ( onExpressionPickerToggle('memes')} data-expression-picker-tab="memes" keybindAction="chat_toggle_saved_media" data-flx="channel.textarea.textarea-buttons.textarea-button.expression-picker-toggle--2" /> )} {showStickersButton && ( onExpressionPickerToggle('stickers')} data-expression-picker-tab="stickers" keybindAction="chat_toggle_sticker" data-flx="channel.textarea.textarea-buttons.textarea-button.expression-picker-toggle--3" /> )} )} {showEmojiButton && ( onExpressionPickerToggle('emojis')} data-expression-picker-tab="emojis" keybindAction="chat_toggle_emoji" data-flx="channel.textarea.textarea-buttons.textarea-button.expression-picker-toggle--4" /> )}
{isMobile && (
{shouldShowVoiceButton ? ( ) : ( )}
)} {!isMobile && shouldShowDesktopSendButton && ( <>
)}
); }, ); TextareaButtons.displayName = 'TextareaButtons';