@esvndev/es-react-template-chat 0.0.89 → 0.0.90

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -72041,7 +72041,7 @@ const ChatLog = (props) => {
72041
72041
  const { typeChat, contactId, chatRoomId, active, messageByGroup, pinnedMessages, setScroll, checkScroll, pinMessageChatRoom, deleteMessageApi, recallMessageApi, get_message_by_group_cursor_api, getPinMessageChatRoomApi, deletePinMessage,
72042
72042
  //updateGroupTagApi,
72043
72043
  createReminderApi, senMessageApi, typingUsers } = useChat();
72044
- useAppSelector((state) => state.hubNotification);
72044
+ const { connection } = useAppSelector((state) => state.hubNotification);
72045
72045
  const [dynamicMenuItems, setDynamicMenuItems] = useState([]);
72046
72046
  useEffect(() => {
72047
72047
  const fetchFormConfig = async () => {
@@ -72082,38 +72082,68 @@ const ChatLog = (props) => {
72082
72082
  ], [dynamicMenuItems]);
72083
72083
  // Menu items cho Hành chính (chỉ administrative)
72084
72084
  useMemo(() => ADMINISTRATIVE_ITEMS, []);
72085
- useRef(false);
72086
- useRef(null);
72087
- useRef(null);
72085
+ const isTypingRef = useRef(false);
72086
+ const typingTimeoutRef = useRef(null);
72088
72087
  useRef(null);
72089
72088
  useRef(null);
72089
+ const inputContainer = useRef(null);
72090
72090
  const containerChatRef = useRef(null);
72091
72091
  const { connectHub, dataInfo, dataProfile, handleUser, handleUserSidebarRight, handleSidebar, handleSidebarRight, userSidebarLeft, dataHistory, chatGetType, approveUpdateStatus, handleModalAdministrative, setDataItem, handleModalAddUserGroup, handleModalGroup, typeOpenModalAdd, setTypeOpenModalAdd, unpinMessage, getPinnedMessages } = props;
72092
72092
  const groupChatUsers = dataHistory?.find((dt) => dt.id === active);
72093
72093
  //const dataInfoAvatar = dataInfo && dataInfo.avatar ? dataInfo.avatar : DEFAULT_AVATAR
72094
- useState([]);
72095
- useState(false);
72094
+ const [imgCopy, setImgCopy] = useState([]);
72095
+ const [isOpen, setIsOpen] = useState(false);
72096
72096
  const [openModalPreview, setOpenModalPreview] = useState(false);
72097
72097
  useState(false);
72098
72098
  useState(false);
72099
72099
  useState(false);
72100
72100
  useState('');
72101
72101
  useState(null);
72102
- useState(null);
72103
- useState(null);
72104
- useState({ x: 0, y: 0 });
72105
- useState(undefined);
72106
- useState(undefined);
72102
+ const [currentMessage, setCurrentMessage] = useState(null);
72103
+ const [replyMessage, setReplyMessage] = useState(null);
72104
+ const [position, setPosition] = useState({ x: 0, y: 0 });
72105
+ const [listContextShow, setListContextShow] = useState(undefined);
72106
+ const [listContextHide, setListContextHide] = useState(undefined);
72107
72107
  const [selectedMessage, setSelectedMessage] = useState(null);
72108
- useState(false);
72109
- useState(true);
72110
- useRef(0);
72108
+ const [isMultiLine, setIsMultiLine] = useState(false);
72109
+ const [isAtBottom, setIsAtBottom] = useState(true);
72110
+ const prevListLength = useRef(0);
72111
72111
  useState(0);
72112
72112
  useState(true);
72113
72113
  useState(new Set());
72114
- useState(null);
72114
+ const [clickedImageFile, setClickedImageFile] = useState(null);
72115
72115
  useState(false);
72116
72116
  useState(new Set());
72117
+ const BASE_CONTEXT_ITEMS = [
72118
+ "reply", "share", "pin", "bookmark",
72119
+ "selectMultiMessage", "detail", "other", "recall", "delete"
72120
+ ];
72121
+ const CONTEXT_CONFIG = {
72122
+ selection: {
72123
+ show: ["copyRangeText", ...BASE_CONTEXT_ITEMS, "save"],
72124
+ hide: ["copyText", "save", "copyImage"]
72125
+ },
72126
+ image: {
72127
+ show: ["copyImage", ...BASE_CONTEXT_ITEMS, "save"],
72128
+ hide: ["copyText", "copyRangeText"]
72129
+ },
72130
+ file: {
72131
+ show: [...BASE_CONTEXT_ITEMS, "save"],
72132
+ hide: ["copyText", "copyRangeText", "copyImage"]
72133
+ },
72134
+ text: {
72135
+ show: ["copyText", ...BASE_CONTEXT_ITEMS],
72136
+ hide: ["copyRangeText", "save", "copyImage"]
72137
+ },
72138
+ voucher: {
72139
+ show: ["copyText", ...BASE_CONTEXT_ITEMS],
72140
+ hide: ["copyRangeText", "save", "copyImage"]
72141
+ },
72142
+ default: {
72143
+ show: ["copyText", ...BASE_CONTEXT_ITEMS],
72144
+ hide: ["copyRangeText", "save", "copyImage"]
72145
+ }
72146
+ };
72117
72147
  useState(true);
72118
72148
  const scrollToBottom = () => {
72119
72149
  const messagesEndEle = document.getElementById('messagesEnd');
@@ -72149,188 +72179,133 @@ const ChatLog = (props) => {
72149
72179
  }, []);
72150
72180
  useState(false);
72151
72181
  useDispatch();
72152
- //từ đây
72153
- // const sendFile = async (event: any) => {
72154
- // if (event.target.files && event.target.files[0]) {
72155
- // const filesToUpload: File[] = Array.from(event.target.files)
72156
- // const errors: Array<{ file: File, error: any }> = []
72157
- // for (const file of filesToUpload) {
72158
- // const result = await uploadWithRetry(file)
72159
- // if (result.success && chatRoomId) {
72160
- // connectHub(chatRoomId, {
72161
- // sendTo: dataInfo.id,
72162
- // content: '',
72163
- // type: 'file',
72164
- // path: JSON.stringify(result.data?.infoFiles),
72165
- // typeChat
72166
- // })
72167
- // } else {
72168
- // errors.push({ file, error: result.error })
72169
- // }
72170
- // }
72171
- // if (errors.length > 0) {
72172
- // notificationError(`${errors.length} file upload thất bại`)
72173
- // }
72174
- // event.target.value = ''
72175
- // }
72176
- // }
72177
- // const sendImage = async (event: any) => {
72178
- // if (event.target.files && event.target.files[0]) {
72179
- // const filesToUpload: File[] = Array.from(event.target.files)
72180
- // const errors: Array<{ file: File, error: any }> = []
72181
- // const batchId = newGuid()
72182
- // for (const file of filesToUpload) {
72183
- // const result = await uploadWithRetry(file)
72184
- // const item = result.data?.infoFiles?.map((x: any) => ({ ...x, batchId, createdDate: new Date().toISOString() })) || []
72185
- // if (result.success && chatRoomId && result?.data?.status) {
72186
- // connectHub(chatRoomId, {
72187
- // sendTo: dataInfo.id,
72188
- // content: '',
72189
- // type: 'image',
72190
- // path: JSON.stringify(item),
72191
- // typeChat
72192
- // })
72193
- // } else {
72194
- // errors.push({ file, error: result.error })
72195
- // }
72196
- // }
72197
- // if (errors.length > 0) {
72198
- // notificationError(`${errors.length} ảnh upload thất bại`)
72199
- // }
72200
- // event.target.value = ''
72201
- // }
72202
- // }
72203
- // const handleRemoveImage = useCallback((idx: number) => {
72204
- // const newArr = [...imgCopy]
72205
- // newArr.splice(idx, 1)
72206
- // setImgCopy(newArr)
72207
- // }, [imgCopy])
72208
- // const onContextMenu = useCallback((e: any, chatMessage: any) => {
72209
- // const selection: any = window.document.getSelection()
72210
- // setIsOpen(true)
72211
- // setPosition({ x: e.clientX, y: e.clientY })
72212
- // if (e.target) {
72213
- // setCurrentMessage(chatMessage)
72214
- // }
72215
- // // Detect ảnh được click từ data attribute
72216
- // const fileIndex = e.target?.dataset?.fileIndex
72217
- // if (fileIndex !== undefined && chatMessage?.path) {
72218
- // setClickedImageFile(chatMessage.path[parseInt(fileIndex)])
72219
- // } else {
72220
- // setClickedImageFile(null)
72221
- // }
72222
- // e.preventDefault()
72223
- // const contextType = selection.toString() ? 'selection' : chatMessage?.type ? chatMessage.type : 'default'
72224
- // const config = CONTEXT_CONFIG[contextType]
72225
- // setListContextShow({ list: config.show, type: "SHOW" })
72226
- // setListContextHide({ list: config.hide, type: "HIDE" })
72227
- // }, [])
72228
- // const clearReply = () => {
72229
- // setReplyMessage(null)
72230
- // }
72231
- // const handleTypingStart = () => {
72232
- // if (!isTypingRef.current && connection && chatRoomId) {
72233
- // connection.invoke('StartTyping', chatRoomId).catch(console.error)
72234
- // isTypingRef.current = true
72235
- // }
72236
- // }
72237
- // const handleTypingStop = () => {
72238
- // if (typingTimeoutRef.current) { clearTimeout(typingTimeoutRef.current) }
72239
- // typingTimeoutRef.current = setTimeout(() => {
72240
- // if (isTypingRef.current && connection && chatRoomId) {
72241
- // connection.invoke('StopTyping', chatRoomId).catch(console.error)
72242
- // isTypingRef.current = false
72243
- // }
72244
- // }, 2000)
72245
- // }
72246
- // const handleChange = useCallback(
72247
- // (editorState: EditorState) => {
72248
- // if (
72249
- // inputContainer &&
72250
- // inputContainer.current &&
72251
- // inputContainer.current.clientHeight > 50
72252
- // ) {
72253
- // setIsMultiLine(true)
72254
- // } else {
72255
- // setIsMultiLine(false)
72256
- // }
72257
- // editorState.read(() => {
72258
- // handleTypingStart()
72259
- // handleTypingStop()
72260
- // })
72261
- // },
72262
- // [inputContainer, connection, chatRoomId]
72263
- // )
72264
- // const onEnter = (val: any) => {
72265
- // handleSendMsg(val)
72266
- // clearReply()
72267
- // setReplyMessage(null)
72268
- // }
72269
- // useEffect(() => {
72270
- // if (active) {
72271
- // setReplyMessage(null)
72272
- // setImgCopy([])
72273
- // setIsAtBottom(true)
72274
- // prevListLength.current = 0
72275
- // }
72276
- // }, [
72277
- // active,
72278
- // dataInfo
72279
- // //chatRoomId,
72280
- // //chatGetType
72281
- // ])
72282
- // // Chuyển phòng thì load lại message + pinned messages
72283
- // useEffect(() => {
72284
- // if (!chatRoomId && handleSidebar) { return }
72285
- // chatGetType(chatRoomId, undefined, "", "", undefined, undefined, 0, 0)
72286
- // if (chatRoomId) {
72287
- // getPinMessageChatRoomApi(chatRoomId)
72288
- // }
72289
- // }, [chatRoomId])
72290
- // const handleUnpinMessage = useCallback(async (message: any) => {
72291
- // const pinId = message?.pinId || message?.id
72292
- // if (!pinId) { return }
72293
- // await deletePinMessage(pinId)
72294
- // }, [deletePinMessage, chatRoomId])
72295
- // const handleViewPinnedMessage = useCallback((message: any) => {
72296
- // if (message?.id) {
72297
- // //setViewingPinnedMessage(message.id)
72298
- // // Tìm message element
72299
- // const element = document.getElementById(`msg-${message.id}`)
72300
- // const chatContainer = document.getElementById('scrollableDivChat')
72301
- // if (element && chatContainer) {
72302
- // // Lấy vị trí của element và container
72303
- // const elementRect = element.getBoundingClientRect()
72304
- // const containerRect = chatContainer.getBoundingClientRect()
72305
- // // Tính toán scroll position để đưa element vào giữa container
72306
- // const scrollTop = chatContainer.scrollTop
72307
- // const offset = elementRect.top - containerRect.top - (containerRect.height / 2) + (elementRect.height / 2)
72308
- // // Scroll trong container, không scroll toàn page
72309
- // chatContainer.scrollTo({
72310
- // top: scrollTop + offset,
72311
- // behavior: 'smooth'
72312
- // })
72313
- // // Highlight message
72314
- // const originalBg = element.style.backgroundColor
72315
- // const originalZIndex = element.style.zIndex
72316
- // element.style.backgroundColor = '#fff3cd'
72317
- // element.style.zIndex = '1000'
72318
- // element.style.position = 'relative'
72319
- // element.style.transition = 'background-color 0.3s'
72320
- // // Auto close sau 500ms
72321
- // setTimeout(() => {
72322
- // element.style.backgroundColor = originalBg || ''
72323
- // element.style.zIndex = originalZIndex || ''
72324
- // //setViewingPinnedMessage(null)
72325
- // }, 500)
72326
- // } else {
72327
- // console.log('Message not found in current view, may need to load more messages')
72328
- // setTimeout(() => {
72329
- // //setViewingPinnedMessage(null)
72330
- // }, 2000)
72331
- // }
72332
- // }
72333
- // }, [])
72182
+ useCallback((idx) => {
72183
+ const newArr = [...imgCopy];
72184
+ newArr.splice(idx, 1);
72185
+ setImgCopy(newArr);
72186
+ }, [imgCopy]);
72187
+ useCallback((e, chatMessage) => {
72188
+ const selection = window.document.getSelection();
72189
+ setIsOpen(true);
72190
+ setPosition({ x: e.clientX, y: e.clientY });
72191
+ if (e.target) {
72192
+ setCurrentMessage(chatMessage);
72193
+ }
72194
+ // Detect ảnh được click từ data attribute
72195
+ const fileIndex = e.target?.dataset?.fileIndex;
72196
+ if (fileIndex !== undefined && chatMessage?.path) {
72197
+ setClickedImageFile(chatMessage.path[parseInt(fileIndex)]);
72198
+ }
72199
+ else {
72200
+ setClickedImageFile(null);
72201
+ }
72202
+ e.preventDefault();
72203
+ const contextType = selection.toString() ? 'selection' : chatMessage?.type ? chatMessage.type : 'default';
72204
+ const config = CONTEXT_CONFIG[contextType];
72205
+ setListContextShow({ list: config.show, type: "SHOW" });
72206
+ setListContextHide({ list: config.hide, type: "HIDE" });
72207
+ }, []);
72208
+ const handleTypingStart = () => {
72209
+ if (!isTypingRef.current && connection && chatRoomId) {
72210
+ connection.invoke('StartTyping', chatRoomId).catch(console.error);
72211
+ isTypingRef.current = true;
72212
+ }
72213
+ };
72214
+ const handleTypingStop = () => {
72215
+ if (typingTimeoutRef.current) {
72216
+ clearTimeout(typingTimeoutRef.current);
72217
+ }
72218
+ typingTimeoutRef.current = setTimeout(() => {
72219
+ if (isTypingRef.current && connection && chatRoomId) {
72220
+ connection.invoke('StopTyping', chatRoomId).catch(console.error);
72221
+ isTypingRef.current = false;
72222
+ }
72223
+ }, 2000);
72224
+ };
72225
+ useCallback((editorState) => {
72226
+ if (inputContainer &&
72227
+ inputContainer.current &&
72228
+ inputContainer.current.clientHeight > 50) {
72229
+ setIsMultiLine(true);
72230
+ }
72231
+ else {
72232
+ setIsMultiLine(false);
72233
+ }
72234
+ editorState.read(() => {
72235
+ handleTypingStart();
72236
+ handleTypingStop();
72237
+ });
72238
+ }, [inputContainer, connection, chatRoomId]);
72239
+ useEffect(() => {
72240
+ if (active) {
72241
+ setReplyMessage(null);
72242
+ setImgCopy([]);
72243
+ setIsAtBottom(true);
72244
+ prevListLength.current = 0;
72245
+ }
72246
+ }, [
72247
+ active,
72248
+ dataInfo
72249
+ //chatRoomId,
72250
+ //chatGetType
72251
+ ]);
72252
+ // Chuyển phòng thì load lại message + pinned messages
72253
+ useEffect(() => {
72254
+ if (!chatRoomId && handleSidebar) {
72255
+ return;
72256
+ }
72257
+ chatGetType(chatRoomId, undefined, "", "", undefined, undefined, 0, 0);
72258
+ if (chatRoomId) {
72259
+ getPinMessageChatRoomApi(chatRoomId);
72260
+ }
72261
+ }, [chatRoomId]);
72262
+ useCallback(async (message) => {
72263
+ const pinId = message?.pinId || message?.id;
72264
+ if (!pinId) {
72265
+ return;
72266
+ }
72267
+ await deletePinMessage(pinId);
72268
+ }, [deletePinMessage, chatRoomId]);
72269
+ useCallback((message) => {
72270
+ if (message?.id) {
72271
+ //setViewingPinnedMessage(message.id)
72272
+ // Tìm message element
72273
+ const element = document.getElementById(`msg-${message.id}`);
72274
+ const chatContainer = document.getElementById('scrollableDivChat');
72275
+ if (element && chatContainer) {
72276
+ // Lấy vị trí của element và container
72277
+ const elementRect = element.getBoundingClientRect();
72278
+ const containerRect = chatContainer.getBoundingClientRect();
72279
+ // Tính toán scroll position để đưa element vào giữa container
72280
+ const scrollTop = chatContainer.scrollTop;
72281
+ const offset = elementRect.top - containerRect.top - (containerRect.height / 2) + (elementRect.height / 2);
72282
+ // Scroll trong container, không scroll toàn page
72283
+ chatContainer.scrollTo({
72284
+ top: scrollTop + offset,
72285
+ behavior: 'smooth'
72286
+ });
72287
+ // Highlight message
72288
+ const originalBg = element.style.backgroundColor;
72289
+ const originalZIndex = element.style.zIndex;
72290
+ element.style.backgroundColor = '#fff3cd';
72291
+ element.style.zIndex = '1000';
72292
+ element.style.position = 'relative';
72293
+ element.style.transition = 'background-color 0.3s';
72294
+ // Auto close sau 500ms
72295
+ setTimeout(() => {
72296
+ element.style.backgroundColor = originalBg || '';
72297
+ element.style.zIndex = originalZIndex || '';
72298
+ //setViewingPinnedMessage(null)
72299
+ }, 500);
72300
+ }
72301
+ else {
72302
+ console.log('Message not found in current view, may need to load more messages');
72303
+ setTimeout(() => {
72304
+ //setViewingPinnedMessage(null)
72305
+ }, 2000);
72306
+ }
72307
+ }
72308
+ }, []);
72334
72309
  // const onKeyDown = (event: any) => {
72335
72310
  // if (event.code === 'NumpadEnter') {
72336
72311
  // event.preventDefault()