diff --git a/packages/react/src/store/settingsStore.js b/packages/react/src/store/settingsStore.js index 4f9d78b68..f3e613729 100644 --- a/packages/react/src/store/settingsStore.js +++ b/packages/react/src/store/settingsStore.js @@ -1,7 +1,7 @@ import { create } from 'zustand'; const useSettingsStore = create((set) => ({ - messageLimit: 5000, + messageLimit: null, // Will be fetched from RC server (Message_MaxAllowedSize) setMessageLimit: (messageLimit) => set(() => ({ messageLimit })), })); diff --git a/packages/react/src/views/AttachmentPreview/AttachmentPreview.js b/packages/react/src/views/AttachmentPreview/AttachmentPreview.js index 01123dafb..fe9fdc4ba 100644 --- a/packages/react/src/views/AttachmentPreview/AttachmentPreview.js +++ b/packages/react/src/views/AttachmentPreview/AttachmentPreview.js @@ -1,10 +1,18 @@ import React, { useContext, useState, useRef, useEffect } from 'react'; import { css } from '@emotion/react'; -import { Box, Icon, Button, Input, Modal } from '@embeddedchat/ui-elements'; +import { + Box, + Icon, + Button, + Input, + Modal, + useTheme, +} from '@embeddedchat/ui-elements'; import useAttachmentWindowStore from '../../store/attachmentwindow'; import CheckPreviewType from './CheckPreviewType'; import RCContext from '../../context/RCInstance'; import { useMessageStore, useMemberStore } from '../../store'; +import useSettingsStore from '../../store/settingsStore'; import getAttachmentPreviewStyles from './AttachmentPreview.styles'; import { parseEmoji } from '../../lib/emoji'; import MembersList from '../Mentions/MembersList'; @@ -13,29 +21,38 @@ import useSearchMentionUser from '../../hooks/useSearchMentionUser'; const AttachmentPreview = () => { const { RCInstance, ECOptions } = useContext(RCContext); + const { theme } = useTheme(); const styles = getAttachmentPreviewStyles(); const toggle = useAttachmentWindowStore((state) => state.toggle); const data = useAttachmentWindowStore((state) => state.data); const setData = useAttachmentWindowStore((state) => state.setData); + const [isPending, setIsPending] = useState(false); const messageRef = useRef(null); + + // Mention UI states const [showMembersList, setShowMembersList] = useState(false); const [filteredMembers, setFilteredMembers] = useState([]); const [mentionIndex, setMentionIndex] = useState(-1); const [startReadMentionUser, setStartReadMentionUser] = useState(false); - const [keyPressed, setKeyPressed] = useState(null); - const [fileName, setFileName] = useState(data?.name); + // File name + const [fileName, setFileName] = useState(data?.name ?? ''); + useEffect(() => setFileName(data?.name ?? ''), [data?.name]); - const threadId = useMessageStore((state) => state.threadMainMessage?._id); - const handleFileName = (e) => { - setFileName(e.target.value); - }; + // Description + const [description, setDescription] = useState(''); + const charCount = description.length; - const { members } = useMemberStore((state) => ({ - members: state.members, - })); + // Character limit is fetched from RC server (Message_MaxAllowedSize) + // via ChatHeader and stored in settingsStore. + const msgMaxLength = useSettingsStore((s) => s?.messageLimit); + + const isOverLimit = msgMaxLength && charCount > msgMaxLength; + + const threadId = useMessageStore((state) => state.threadMainMessage?._id); + const { members } = useMemberStore((state) => ({ members: state.members })); const searchMentionUser = useSearchMentionUser( members, @@ -46,47 +63,49 @@ const AttachmentPreview = () => { setShowMembersList ); + const handleFileName = (e) => setFileName(e.target.value); + const handleFileDescription = (e) => { - const description = e.target.value; - messageRef.current.value = parseEmoji(description); - searchMentionUser(description); + const raw = e.target.value || ''; + setDescription(raw); + + // If Input forwards ref to native input, keep it in sync (safe-guard) + if (messageRef.current && typeof messageRef.current.value !== 'undefined') { + try { + messageRef.current.value = raw; + } catch (err) { + // ignore if ref doesn't allow direct value set + } + } + + searchMentionUser(raw); }; const submit = async () => { + if (isPending) return; + if (msgMaxLength && description.length > msgMaxLength) return; + setIsPending(true); - await RCInstance.sendAttachment( - data, - fileName, - messageRef.current.value, - ECOptions?.enableThreads ? threadId : undefined - ); - toggle(); - setData(null); - if (isPending) { + try { + await RCInstance.sendAttachment( + data, + fileName, + parseEmoji(description), + ECOptions?.enableThreads ? threadId : undefined + ); + toggle(); + setData(null); + } finally { setIsPending(false); } }; - useEffect(() => { - const keyHandler = (e) => { - if (e.key === 'Enter') { - e.preventDefault(); - setKeyPressed('Enter'); - } - }; - - document.addEventListener('keydown', keyHandler); - return () => { - document.removeEventListener('keydown', keyHandler); - }; - }, []); - - useEffect(() => { - if (keyPressed === 'Enter') { + const onDescKeyDown = (e) => { + if (e.key === 'Enter' && !e.shiftKey) { + e.preventDefault(); submit(); - setKeyPressed(null); } - }, [keyPressed, submit]); + }; return ( @@ -98,11 +117,12 @@ const AttachmentPreview = () => { css={css` margin-right: 0.5rem; `} - />{' '} + /> File Upload + { > + + {/* FILE NAME */} { File name { - handleFileName(e); - }} + onChange={handleFileName} value={fileName} type="text" css={styles.input} @@ -140,6 +160,7 @@ const AttachmentPreview = () => { + {/* FILE DESCRIPTION */} { > File description + {showMembersList && ( @@ -161,21 +183,77 @@ const AttachmentPreview = () => { setFilteredMembers={setFilteredMembers} setStartReadMentionUser={setStartReadMentionUser} setShowMembersList={setShowMembersList} - css={css` - width: auto; - `} /> )} + + {/* DESCRIPTION INPUT */} { - handleFileDescription(e); - }} + onChange={handleFileDescription} + onKeyDown={onDescKeyDown} type="text" - css={styles.input} placeholder="Description" ref={messageRef} + value={description} + css={css` + ${styles.input}; + border-color: ${isOverLimit + ? theme.colors.destructive + : null}; + color: ${isOverLimit ? theme.colors.destructive : null}; + `} /> + + {/* ALERT (left) and COUNTER (right) on the same row below the input */} + {msgMaxLength && ( + + {/* ALERT: left aligned (starts at left of the box). Only visible when over limit. */} + + {isOverLimit + ? `Cannot upload file, description is over the ${msgMaxLength} character limit` + : ''} + + + {/* COUNTER: right aligned */} + + + )} @@ -190,12 +268,8 @@ const AttachmentPreview = () => { -