From e86eaedcef1db8f5abb1b6f6638e2eef5ceb8eb9 Mon Sep 17 00:00:00 2001 From: srijnabhargav Date: Thu, 11 Dec 2025 12:55:01 +0000 Subject: [PATCH 1/3] feat: added max length validation in file uplode modal in file description --- .../AttachmentPreview/AttachmentPreview.js | 223 ++++++++++-------- 1 file changed, 129 insertions(+), 94 deletions(-) diff --git a/packages/react/src/views/AttachmentPreview/AttachmentPreview.js b/packages/react/src/views/AttachmentPreview/AttachmentPreview.js index 01123dafb..5c09efb0e 100644 --- a/packages/react/src/views/AttachmentPreview/AttachmentPreview.js +++ b/packages/react/src/views/AttachmentPreview/AttachmentPreview.js @@ -1,41 +1,59 @@ -import React, { useContext, useState, useRef, useEffect } from 'react'; +import React, { useContext, useState, useRef, useEffect, useMemo } 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'; import TypingUsers from '../TypingUsers/TypingUsers'; import useSearchMentionUser from '../../hooks/useSearchMentionUser'; +const DEFAULT_CHAR_LIMIT = 5000; + 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; + + // Get configurable limit from settings if present + const settingsMsgLimit = useSettingsStore((s) => s?.messageLimit); + const msgMaxLength = useMemo( + () => + typeof settingsMsgLimit === 'number' && settingsMsgLimit > 0 + ? settingsMsgLimit + : DEFAULT_CHAR_LIMIT, + [settingsMsgLimit] + ); - const { members } = useMemberStore((state) => ({ - members: state.members, - })); + const isOverLimit = charCount > msgMaxLength; + + const threadId = useMessageStore((state) => state.threadMainMessage?._id); + const { members } = useMemberStore((state) => ({ members: state.members })); const searchMentionUser = useSearchMentionUser( members, @@ -46,92 +64,74 @@ 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 (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 ( - {' '} + File Upload + - + - + + + {/* FILE NAME */} - + File name { - handleFileName(e); - }} + onChange={handleFileName} value={fileName} type="text" css={styles.input} @@ -140,16 +140,12 @@ const AttachmentPreview = () => { + {/* FILE DESCRIPTION */} - + File description + {showMembersList && ( @@ -161,41 +157,80 @@ 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 */} + + {/* 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 */} + + + - + - From e103420b397e31c84c5550cf2df3e1e7d120448b Mon Sep 17 00:00:00 2001 From: srijna Date: Mon, 19 Jan 2026 00:08:56 +0530 Subject: [PATCH 2/3] fetch message limit from RC --- packages/react/src/store/settingsStore.js | 2 +- .../AttachmentPreview/AttachmentPreview.js | 94 +++++++++---------- 2 files changed, 45 insertions(+), 51 deletions(-) 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 5c09efb0e..09303ddf4 100644 --- a/packages/react/src/views/AttachmentPreview/AttachmentPreview.js +++ b/packages/react/src/views/AttachmentPreview/AttachmentPreview.js @@ -1,4 +1,4 @@ -import React, { useContext, useState, useRef, useEffect, useMemo } from 'react'; +import React, { useContext, useState, useRef, useEffect } from 'react'; import { css } from '@emotion/react'; import { Box, Icon, Button, Input, Modal, useTheme } from '@embeddedchat/ui-elements'; import useAttachmentWindowStore from '../../store/attachmentwindow'; @@ -12,8 +12,6 @@ import MembersList from '../Mentions/MembersList'; import TypingUsers from '../TypingUsers/TypingUsers'; import useSearchMentionUser from '../../hooks/useSearchMentionUser'; -const DEFAULT_CHAR_LIMIT = 5000; - const AttachmentPreview = () => { const { RCInstance, ECOptions } = useContext(RCContext); const { theme } = useTheme(); @@ -40,17 +38,11 @@ const AttachmentPreview = () => { const [description, setDescription] = useState(''); const charCount = description.length; - // Get configurable limit from settings if present - const settingsMsgLimit = useSettingsStore((s) => s?.messageLimit); - const msgMaxLength = useMemo( - () => - typeof settingsMsgLimit === 'number' && settingsMsgLimit > 0 - ? settingsMsgLimit - : DEFAULT_CHAR_LIMIT, - [settingsMsgLimit] - ); + // Character limit is fetched from RC server (Message_MaxAllowedSize) + // via ChatHeader and stored in settingsStore. + const msgMaxLength = useSettingsStore((s) => s?.messageLimit); - const isOverLimit = charCount > msgMaxLength; + const isOverLimit = msgMaxLength && charCount > msgMaxLength; const threadId = useMessageStore((state) => state.threadMainMessage?._id); const { members } = useMemberStore((state) => ({ members: state.members })); @@ -84,7 +76,7 @@ const AttachmentPreview = () => { const submit = async () => { if (isPending) return; - if (description.length > msgMaxLength) return; + if (msgMaxLength && description.length > msgMaxLength) return; setIsPending(true); try { @@ -177,47 +169,49 @@ const AttachmentPreview = () => { /> {/* ALERT (left) and COUNTER (right) on the same row below the input */} - - {/* ALERT: left aligned (starts at left of the box). Only visible when over limit. */} + {msgMaxLength && ( - {isOverLimit ? `Cannot upload file, description is over the ${msgMaxLength} character limit` : ''} + {/* 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 */} + - - {/* COUNTER: right aligned */} - - + )} From 9cb42c1d57957311ae3efa13566fcd1a4cef2022 Mon Sep 17 00:00:00 2001 From: srijna Date: Mon, 19 Jan 2026 00:27:14 +0530 Subject: [PATCH 3/3] fixed lint format --- .../AttachmentPreview/AttachmentPreview.js | 69 +++++++++++++++---- 1 file changed, 57 insertions(+), 12 deletions(-) diff --git a/packages/react/src/views/AttachmentPreview/AttachmentPreview.js b/packages/react/src/views/AttachmentPreview/AttachmentPreview.js index 09303ddf4..fe9fdc4ba 100644 --- a/packages/react/src/views/AttachmentPreview/AttachmentPreview.js +++ b/packages/react/src/views/AttachmentPreview/AttachmentPreview.js @@ -1,6 +1,13 @@ import React, { useContext, useState, useRef, useEffect } from 'react'; import { css } from '@emotion/react'; -import { Box, Icon, Button, Input, Modal, useTheme } 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'; @@ -104,7 +111,13 @@ const AttachmentPreview = () => { - + File Upload @@ -112,14 +125,29 @@ const AttachmentPreview = () => { - + - + {/* FILE NAME */} - + File name { {/* FILE DESCRIPTION */} - + File description @@ -163,7 +197,9 @@ const AttachmentPreview = () => { value={description} css={css` ${styles.input}; - border-color: ${isOverLimit ? theme.colors.destructive : null}; + border-color: ${isOverLimit + ? theme.colors.destructive + : null}; color: ${isOverLimit ? theme.colors.destructive : null}; `} /> @@ -184,7 +220,9 @@ const AttachmentPreview = () => { {/* ALERT: left aligned (starts at left of the box). Only visible when over limit. */} { aria-hidden={!isOverLimit} role={isOverLimit ? 'alert' : undefined} > - {isOverLimit ? `Cannot upload file, description is over the ${msgMaxLength} character limit` : ''} + {isOverLimit + ? `Cannot upload file, description is over the ${msgMaxLength} character limit` + : ''} {/* COUNTER: right aligned */} { )} - - +