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

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
- useState(new Set());
72114
- useState(null);
72113
+ const [expandedMessages, setExpandedMessages] = useState(new Set());
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');
@@ -72125,7 +72155,7 @@ const ChatLog = (props) => {
72125
72155
  scrollToBottom();
72126
72156
  }
72127
72157
  }, [messageByGroup, checkScroll]);
72128
- useMemo(() => {
72158
+ const mentionItems = useMemo(() => {
72129
72159
  if (!groupChatUsers) {
72130
72160
  return [];
72131
72161
  }
@@ -72143,375 +72173,230 @@ const ChatLog = (props) => {
72143
72173
  });
72144
72174
  return [{ id: "all", name: "All" }, ...mappedUsers];
72145
72175
  }, [groupChatUsers]);
72146
- useCallback((mes) => {
72176
+ const handlePreview = useCallback((mes) => {
72147
72177
  setSelectedMessage(mes);
72148
72178
  setOpenModalPreview(true);
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
- // }, [])
72334
- // const onKeyDown = (event: any) => {
72335
- // if (event.code === 'NumpadEnter') {
72336
- // event.preventDefault()
72337
- // const baseOptions = {
72338
- // key: 'Enter',
72339
- // code: 'Enter',
72340
- // which: 13,
72341
- // keyCode: 13,
72342
- // bubbles: true,
72343
- // shiftKey: event.shiftKey,
72344
- // altKey: event.altKey,
72345
- // ctrlKey: event.ctrlKey,
72346
- // metaKey: event.metaKey
72347
- // }
72348
- // const newEvent = new KeyboardEvent('keydown', baseOptions)
72349
- // event.target.dispatchEvent(newEvent)
72350
- // }
72351
- // }
72352
- // const useParsedChatData = (chatMsg: string) => {
72353
- // if (!chatMsg) { return {} }
72354
- // try {
72355
- // return typeof chatMsg === 'string' ? JSON.parse(chatMsg) : chatMsg
72356
- // } catch (err) {
72357
- // console.error('JSON parse error:', err)
72358
- // return {}
72359
- // }
72360
- // }
72361
- // const renderChatText = useCallback((chat: any) => {
72362
- // const isPath = Array.isArray(chat?.path) && typeof chat.path[0]?.path === "string" ? chat.path[0].path.trim() : ""
72363
- // const img = isPath ? `${CDN_URL_VIEW}/${isPath}` : ""
72364
- // const toggleExpanded = (messageId: string) => {
72365
- // const newExpanded = new Set(expandedMessages)
72366
- // if (newExpanded.has(messageId)) {
72367
- // newExpanded.delete(messageId)
72368
- // } else {
72369
- // newExpanded.add(messageId)
72370
- // }
72371
- // setExpandedMessages(newExpanded)
72372
- // }
72373
- // const formatMentionsReact = (text: string, mentions: any[], messageId: string) => {
72374
- // if (!text || !mentions || mentions.length === 0) { return text }
72375
- // const maxLength = 200
72376
- // const isExpanded = expandedMessages.has(messageId)
72377
- // let displayText = text
72378
- // let isTruncated = false
72379
- // if (!isExpanded && text.length > maxLength) {
72380
- // displayText = text.substring(0, maxLength)
72381
- // isTruncated = true
72382
- // }
72383
- // // Chỉ highlight những mention có trong danh sách
72384
- // let result: any = displayText
72385
- // // Hàm helper để xử lý mention trong cả string và array
72386
- // const processMention = (text: string, mention: any): any => {
72387
- // const escapedName = mention?.name?.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
72388
- // const mentionRegex = new RegExp(`@${escapedName}(?=\\s|$|[.,;:!?()[\\]{}'"\`~@])`, 'g')
72389
- // const parts = text.split(mentionRegex)
72390
- // const matches = text.match(mentionRegex) || []
72391
- // if (matches.length === 0) {
72392
- // return text
72393
- // }
72394
- // const newParts: any[] = []
72395
- // parts.forEach((part: any, index: any) => {
72396
- // newParts.push(part)
72397
- // if (matches[index]) {
72398
- // newParts.push(
72399
- // <span key={`mention-${messageId}-${mention.id || mention.name}-${index}`} className="chat-mention">
72400
- // {matches[index]}
72401
- // </span>
72402
- // )
72403
- // }
72404
- // })
72405
- // return newParts
72406
- // }
72407
- // mentions.forEach((mention: any) => {
72408
- // if (typeof result === 'string') {
72409
- // result = processMention(result, mention)
72410
- // } else if (Array.isArray(result)) {
72411
- // // Xử lý từng phần tử trong array
72412
- // const processedArray: any[] = []
72413
- // result.forEach((item: any) => {
72414
- // if (typeof item === 'string') {
72415
- // const processed = processMention(item, mention)
72416
- // if (Array.isArray(processed)) {
72417
- // processedArray.push(...processed)
72418
- // } else {
72419
- // processedArray.push(processed)
72420
- // }
72421
- // } else {
72422
- // // Giữ nguyên các React elements đã được xử lý
72423
- // processedArray.push(item)
72424
- // }
72425
- // })
72426
- // result = processedArray
72427
- // }
72428
- // })
72429
- // // Add expand/collapse functionality
72430
- // if (isTruncated || (isExpanded && text.length > maxLength)) {
72431
- // const expandButton = (
72432
- // <span
72433
- // key={`expand-${messageId}`}
72434
- // onClick={() => toggleExpanded(messageId)}
72435
- // style={{
72436
- // color: '#006edc',
72437
- // cursor: 'pointer',
72438
- // fontWeight: 500,
72439
- // marginLeft: 5
72440
- // }}
72441
- // >
72442
- // {isExpanded ? ' Thu gọn' : '... Mở rộng'}
72443
- // </span>
72444
- // )
72445
- // if (Array.isArray(result)) {
72446
- // result.push(expandButton)
72447
- // } else {
72448
- // result = [result, expandButton]
72449
- // }
72450
- // }
72451
- // return result
72452
- // }
72453
- // return (
72454
- // <>
72455
- // <div>
72456
- // <div>
72457
- // {chat.path && chat.path.length === 1 && (
72458
- // <div className="image-container">
72459
- // {img && <img src={img} alt="" />}
72460
- // </div>
72461
- // )}
72462
- // {isValidUrl(chat.msg) && renderLinkPreview(chat.msg)}
72463
- // {chat.msg && !isValidUrl(chat.msg) ? (
72464
- // <div>{formatMentionsReact(chat.msg, mentionItems, chat.id)}</div>
72465
- // ) : (
72466
- // <></>
72467
- // )}
72468
- // {chat.path && chat.path.length > 1 && (
72469
- // <div className="group-image">
72470
- // {chat.path.map((it: any, index: any) => {
72471
- // return (
72472
- // <div
72473
- // key={index}
72474
- // className="group-image-item image-container"
72475
- // >
72476
- // <img
72477
- // style={{ maxWidth: "100%", maxHeight: 360 }}
72478
- // src={`${CDN_URL_VIEW}/${it.path.trim()}`}
72479
- // onClick={() => handlePreview(it)}
72480
- // alt=""
72481
- // />
72482
- // </div>
72483
- // )
72484
- // })}
72485
- // </div>
72486
- // )}
72487
- // <div className="" style={{ fontSize: "12px", color: "#476285" }}>
72488
- // {moment(chat.time).format("HH:mm")}
72489
- // </div>
72490
- // </div>
72491
- // </div>
72492
- // </>
72493
- // )
72494
- // }, [expandedMessages, handlePreview, mentionItems])
72495
- // const getLinkPreviewData = (urlString: string) => {
72496
- // try {
72497
- // const hasProtocol = /^https?:\/\//i.test(urlString)
72498
- // const href = hasProtocol ? urlString : `https://${urlString}`
72499
- // const url = new URL(href)
72500
- // const displayUrl =
72501
- // urlString.length > 80 ? `${urlString.slice(0, 77)}...` : urlString
72502
- // return {
72503
- // href,
72504
- // displayUrl,
72505
- // host: url.host
72506
- // }
72507
- // } catch (e) {
72508
- // return {
72509
- // href: urlString,
72510
- // displayUrl: urlString,
72511
- // host: ''
72512
- // }
72513
- // }
72514
- // }
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
+ }, []);
72309
+ useCallback((chat) => {
72310
+ const isPath = Array.isArray(chat?.path) && typeof chat.path[0]?.path === "string" ? chat.path[0].path.trim() : "";
72311
+ const img = isPath ? `${CDN_URL_VIEW}/${isPath}` : "";
72312
+ const toggleExpanded = (messageId) => {
72313
+ const newExpanded = new Set(expandedMessages);
72314
+ if (newExpanded.has(messageId)) {
72315
+ newExpanded.delete(messageId);
72316
+ }
72317
+ else {
72318
+ newExpanded.add(messageId);
72319
+ }
72320
+ setExpandedMessages(newExpanded);
72321
+ };
72322
+ const formatMentionsReact = (text, mentions, messageId) => {
72323
+ if (!text || !mentions || mentions.length === 0) {
72324
+ return text;
72325
+ }
72326
+ const maxLength = 200;
72327
+ const isExpanded = expandedMessages.has(messageId);
72328
+ let displayText = text;
72329
+ let isTruncated = false;
72330
+ if (!isExpanded && text.length > maxLength) {
72331
+ displayText = text.substring(0, maxLength);
72332
+ isTruncated = true;
72333
+ }
72334
+ // Chỉ highlight những mention có trong danh sách
72335
+ let result = displayText;
72336
+ // Hàm helper để xử lý mention trong cả string và array
72337
+ const processMention = (text, mention) => {
72338
+ const escapedName = mention?.name?.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
72339
+ const mentionRegex = new RegExp(`@${escapedName}(?=\\s|$|[.,;:!?()[\\]{}'"\`~@])`, 'g');
72340
+ const parts = text.split(mentionRegex);
72341
+ const matches = text.match(mentionRegex) || [];
72342
+ if (matches.length === 0) {
72343
+ return text;
72344
+ }
72345
+ const newParts = [];
72346
+ parts.forEach((part, index) => {
72347
+ newParts.push(part);
72348
+ if (matches[index]) {
72349
+ newParts.push(jsx("span", { className: "chat-mention", children: matches[index] }, `mention-${messageId}-${mention.id || mention.name}-${index}`));
72350
+ }
72351
+ });
72352
+ return newParts;
72353
+ };
72354
+ mentions.forEach((mention) => {
72355
+ if (typeof result === 'string') {
72356
+ result = processMention(result, mention);
72357
+ }
72358
+ else if (Array.isArray(result)) {
72359
+ // Xử lý từng phần tử trong array
72360
+ const processedArray = [];
72361
+ result.forEach((item) => {
72362
+ if (typeof item === 'string') {
72363
+ const processed = processMention(item, mention);
72364
+ if (Array.isArray(processed)) {
72365
+ processedArray.push(...processed);
72366
+ }
72367
+ else {
72368
+ processedArray.push(processed);
72369
+ }
72370
+ }
72371
+ else {
72372
+ // Giữ nguyên các React elements đã được xử lý
72373
+ processedArray.push(item);
72374
+ }
72375
+ });
72376
+ result = processedArray;
72377
+ }
72378
+ });
72379
+ // Add expand/collapse functionality
72380
+ if (isTruncated || (isExpanded && text.length > maxLength)) {
72381
+ const expandButton = (jsx("span", { onClick: () => toggleExpanded(messageId), style: {
72382
+ color: '#006edc',
72383
+ cursor: 'pointer',
72384
+ fontWeight: 500,
72385
+ marginLeft: 5
72386
+ }, children: isExpanded ? ' Thu gọn' : '... Mở rộng' }, `expand-${messageId}`));
72387
+ if (Array.isArray(result)) {
72388
+ result.push(expandButton);
72389
+ }
72390
+ else {
72391
+ result = [result, expandButton];
72392
+ }
72393
+ }
72394
+ return result;
72395
+ };
72396
+ return (jsx(Fragment$1, { children: jsx("div", { children: jsxs("div", { children: [chat.path && chat.path.length === 1 && (jsx("div", { className: "image-container", children: img && jsx("img", { src: img, alt: "" }) })), isValidUrl(chat.msg) && renderLinkPreview(chat.msg), chat.msg && !isValidUrl(chat.msg) ? (jsx("div", { children: formatMentionsReact(chat.msg, mentionItems, chat.id) })) : (jsx(Fragment$1, {})), chat.path && chat.path.length > 1 && (jsx("div", { className: "group-image", children: chat.path.map((it, index) => {
72397
+ return (jsx("div", { className: "group-image-item image-container", children: jsx("img", { style: { maxWidth: "100%", maxHeight: 360 }, src: `${CDN_URL_VIEW}/${it.path.trim()}`, onClick: () => handlePreview(it), alt: "" }) }, index));
72398
+ }) })), jsx("div", { className: "", style: { fontSize: "12px", color: "#476285" }, children: moment(chat.time).format("HH:mm") })] }) }) }));
72399
+ }, [expandedMessages, handlePreview, mentionItems]);
72515
72400
  // const renderLinkPreview = (urlString: string) => {
72516
72401
  // const { href, displayUrl, host } = getLinkPreviewData(urlString)
72517
72402
  // return (