@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.js CHANGED
@@ -72070,7 +72070,7 @@ const ChatLog = (props) => {
72070
72070
  const { typeChat, contactId, chatRoomId, active, messageByGroup, pinnedMessages, setScroll, checkScroll, pinMessageChatRoom, deleteMessageApi, recallMessageApi, get_message_by_group_cursor_api, getPinMessageChatRoomApi, deletePinMessage,
72071
72071
  //updateGroupTagApi,
72072
72072
  createReminderApi, senMessageApi, typingUsers } = useChat();
72073
- configureStore.useAppSelector((state) => state.hubNotification);
72073
+ const { connection } = configureStore.useAppSelector((state) => state.hubNotification);
72074
72074
  const [dynamicMenuItems, setDynamicMenuItems] = React.useState([]);
72075
72075
  React.useEffect(() => {
72076
72076
  const fetchFormConfig = async () => {
@@ -72111,38 +72111,68 @@ const ChatLog = (props) => {
72111
72111
  ], [dynamicMenuItems]);
72112
72112
  // Menu items cho Hành chính (chỉ administrative)
72113
72113
  React.useMemo(() => ADMINISTRATIVE_ITEMS, []);
72114
- React.useRef(false);
72115
- React.useRef(null);
72116
- React.useRef(null);
72114
+ const isTypingRef = React.useRef(false);
72115
+ const typingTimeoutRef = React.useRef(null);
72117
72116
  React.useRef(null);
72118
72117
  React.useRef(null);
72118
+ const inputContainer = React.useRef(null);
72119
72119
  const containerChatRef = React.useRef(null);
72120
72120
  const { connectHub, dataInfo, dataProfile, handleUser, handleUserSidebarRight, handleSidebar, handleSidebarRight, userSidebarLeft, dataHistory, chatGetType, approveUpdateStatus, handleModalAdministrative, setDataItem, handleModalAddUserGroup, handleModalGroup, typeOpenModalAdd, setTypeOpenModalAdd, unpinMessage, getPinnedMessages } = props;
72121
72121
  const groupChatUsers = dataHistory?.find((dt) => dt.id === active);
72122
72122
  //const dataInfoAvatar = dataInfo && dataInfo.avatar ? dataInfo.avatar : DEFAULT_AVATAR
72123
- React.useState([]);
72124
- React.useState(false);
72123
+ const [imgCopy, setImgCopy] = React.useState([]);
72124
+ const [isOpen, setIsOpen] = React.useState(false);
72125
72125
  const [openModalPreview, setOpenModalPreview] = React.useState(false);
72126
72126
  React.useState(false);
72127
72127
  React.useState(false);
72128
72128
  React.useState(false);
72129
72129
  React.useState('');
72130
72130
  React.useState(null);
72131
- React.useState(null);
72132
- React.useState(null);
72133
- React.useState({ x: 0, y: 0 });
72134
- React.useState(undefined);
72135
- React.useState(undefined);
72131
+ const [currentMessage, setCurrentMessage] = React.useState(null);
72132
+ const [replyMessage, setReplyMessage] = React.useState(null);
72133
+ const [position, setPosition] = React.useState({ x: 0, y: 0 });
72134
+ const [listContextShow, setListContextShow] = React.useState(undefined);
72135
+ const [listContextHide, setListContextHide] = React.useState(undefined);
72136
72136
  const [selectedMessage, setSelectedMessage] = React.useState(null);
72137
- React.useState(false);
72138
- React.useState(true);
72139
- React.useRef(0);
72137
+ const [isMultiLine, setIsMultiLine] = React.useState(false);
72138
+ const [isAtBottom, setIsAtBottom] = React.useState(true);
72139
+ const prevListLength = React.useRef(0);
72140
72140
  React.useState(0);
72141
72141
  React.useState(true);
72142
- React.useState(new Set());
72143
- React.useState(null);
72142
+ const [expandedMessages, setExpandedMessages] = React.useState(new Set());
72143
+ const [clickedImageFile, setClickedImageFile] = React.useState(null);
72144
72144
  React.useState(false);
72145
72145
  React.useState(new Set());
72146
+ const BASE_CONTEXT_ITEMS = [
72147
+ "reply", "share", "pin", "bookmark",
72148
+ "selectMultiMessage", "detail", "other", "recall", "delete"
72149
+ ];
72150
+ const CONTEXT_CONFIG = {
72151
+ selection: {
72152
+ show: ["copyRangeText", ...BASE_CONTEXT_ITEMS, "save"],
72153
+ hide: ["copyText", "save", "copyImage"]
72154
+ },
72155
+ image: {
72156
+ show: ["copyImage", ...BASE_CONTEXT_ITEMS, "save"],
72157
+ hide: ["copyText", "copyRangeText"]
72158
+ },
72159
+ file: {
72160
+ show: [...BASE_CONTEXT_ITEMS, "save"],
72161
+ hide: ["copyText", "copyRangeText", "copyImage"]
72162
+ },
72163
+ text: {
72164
+ show: ["copyText", ...BASE_CONTEXT_ITEMS],
72165
+ hide: ["copyRangeText", "save", "copyImage"]
72166
+ },
72167
+ voucher: {
72168
+ show: ["copyText", ...BASE_CONTEXT_ITEMS],
72169
+ hide: ["copyRangeText", "save", "copyImage"]
72170
+ },
72171
+ default: {
72172
+ show: ["copyText", ...BASE_CONTEXT_ITEMS],
72173
+ hide: ["copyRangeText", "save", "copyImage"]
72174
+ }
72175
+ };
72146
72176
  React.useState(true);
72147
72177
  const scrollToBottom = () => {
72148
72178
  const messagesEndEle = document.getElementById('messagesEnd');
@@ -72154,7 +72184,7 @@ const ChatLog = (props) => {
72154
72184
  scrollToBottom();
72155
72185
  }
72156
72186
  }, [messageByGroup, checkScroll]);
72157
- React.useMemo(() => {
72187
+ const mentionItems = React.useMemo(() => {
72158
72188
  if (!groupChatUsers) {
72159
72189
  return [];
72160
72190
  }
@@ -72172,375 +72202,230 @@ const ChatLog = (props) => {
72172
72202
  });
72173
72203
  return [{ id: "all", name: "All" }, ...mappedUsers];
72174
72204
  }, [groupChatUsers]);
72175
- React.useCallback((mes) => {
72205
+ const handlePreview = React.useCallback((mes) => {
72176
72206
  setSelectedMessage(mes);
72177
72207
  setOpenModalPreview(true);
72178
72208
  }, []);
72179
72209
  React.useState(false);
72180
72210
  reactRedux.useDispatch();
72181
- //từ đây
72182
- // const sendFile = async (event: any) => {
72183
- // if (event.target.files && event.target.files[0]) {
72184
- // const filesToUpload: File[] = Array.from(event.target.files)
72185
- // const errors: Array<{ file: File, error: any }> = []
72186
- // for (const file of filesToUpload) {
72187
- // const result = await uploadWithRetry(file)
72188
- // if (result.success && chatRoomId) {
72189
- // connectHub(chatRoomId, {
72190
- // sendTo: dataInfo.id,
72191
- // content: '',
72192
- // type: 'file',
72193
- // path: JSON.stringify(result.data?.infoFiles),
72194
- // typeChat
72195
- // })
72196
- // } else {
72197
- // errors.push({ file, error: result.error })
72198
- // }
72199
- // }
72200
- // if (errors.length > 0) {
72201
- // notificationError(`${errors.length} file upload thất bại`)
72202
- // }
72203
- // event.target.value = ''
72204
- // }
72205
- // }
72206
- // const sendImage = async (event: any) => {
72207
- // if (event.target.files && event.target.files[0]) {
72208
- // const filesToUpload: File[] = Array.from(event.target.files)
72209
- // const errors: Array<{ file: File, error: any }> = []
72210
- // const batchId = newGuid()
72211
- // for (const file of filesToUpload) {
72212
- // const result = await uploadWithRetry(file)
72213
- // const item = result.data?.infoFiles?.map((x: any) => ({ ...x, batchId, createdDate: new Date().toISOString() })) || []
72214
- // if (result.success && chatRoomId && result?.data?.status) {
72215
- // connectHub(chatRoomId, {
72216
- // sendTo: dataInfo.id,
72217
- // content: '',
72218
- // type: 'image',
72219
- // path: JSON.stringify(item),
72220
- // typeChat
72221
- // })
72222
- // } else {
72223
- // errors.push({ file, error: result.error })
72224
- // }
72225
- // }
72226
- // if (errors.length > 0) {
72227
- // notificationError(`${errors.length} ảnh upload thất bại`)
72228
- // }
72229
- // event.target.value = ''
72230
- // }
72231
- // }
72232
- // const handleRemoveImage = useCallback((idx: number) => {
72233
- // const newArr = [...imgCopy]
72234
- // newArr.splice(idx, 1)
72235
- // setImgCopy(newArr)
72236
- // }, [imgCopy])
72237
- // const onContextMenu = useCallback((e: any, chatMessage: any) => {
72238
- // const selection: any = window.document.getSelection()
72239
- // setIsOpen(true)
72240
- // setPosition({ x: e.clientX, y: e.clientY })
72241
- // if (e.target) {
72242
- // setCurrentMessage(chatMessage)
72243
- // }
72244
- // // Detect ảnh được click từ data attribute
72245
- // const fileIndex = e.target?.dataset?.fileIndex
72246
- // if (fileIndex !== undefined && chatMessage?.path) {
72247
- // setClickedImageFile(chatMessage.path[parseInt(fileIndex)])
72248
- // } else {
72249
- // setClickedImageFile(null)
72250
- // }
72251
- // e.preventDefault()
72252
- // const contextType = selection.toString() ? 'selection' : chatMessage?.type ? chatMessage.type : 'default'
72253
- // const config = CONTEXT_CONFIG[contextType]
72254
- // setListContextShow({ list: config.show, type: "SHOW" })
72255
- // setListContextHide({ list: config.hide, type: "HIDE" })
72256
- // }, [])
72257
- // const clearReply = () => {
72258
- // setReplyMessage(null)
72259
- // }
72260
- // const handleTypingStart = () => {
72261
- // if (!isTypingRef.current && connection && chatRoomId) {
72262
- // connection.invoke('StartTyping', chatRoomId).catch(console.error)
72263
- // isTypingRef.current = true
72264
- // }
72265
- // }
72266
- // const handleTypingStop = () => {
72267
- // if (typingTimeoutRef.current) { clearTimeout(typingTimeoutRef.current) }
72268
- // typingTimeoutRef.current = setTimeout(() => {
72269
- // if (isTypingRef.current && connection && chatRoomId) {
72270
- // connection.invoke('StopTyping', chatRoomId).catch(console.error)
72271
- // isTypingRef.current = false
72272
- // }
72273
- // }, 2000)
72274
- // }
72275
- // const handleChange = useCallback(
72276
- // (editorState: EditorState) => {
72277
- // if (
72278
- // inputContainer &&
72279
- // inputContainer.current &&
72280
- // inputContainer.current.clientHeight > 50
72281
- // ) {
72282
- // setIsMultiLine(true)
72283
- // } else {
72284
- // setIsMultiLine(false)
72285
- // }
72286
- // editorState.read(() => {
72287
- // handleTypingStart()
72288
- // handleTypingStop()
72289
- // })
72290
- // },
72291
- // [inputContainer, connection, chatRoomId]
72292
- // )
72293
- // const onEnter = (val: any) => {
72294
- // handleSendMsg(val)
72295
- // clearReply()
72296
- // setReplyMessage(null)
72297
- // }
72298
- // useEffect(() => {
72299
- // if (active) {
72300
- // setReplyMessage(null)
72301
- // setImgCopy([])
72302
- // setIsAtBottom(true)
72303
- // prevListLength.current = 0
72304
- // }
72305
- // }, [
72306
- // active,
72307
- // dataInfo
72308
- // //chatRoomId,
72309
- // //chatGetType
72310
- // ])
72311
- // // Chuyển phòng thì load lại message + pinned messages
72312
- // useEffect(() => {
72313
- // if (!chatRoomId && handleSidebar) { return }
72314
- // chatGetType(chatRoomId, undefined, "", "", undefined, undefined, 0, 0)
72315
- // if (chatRoomId) {
72316
- // getPinMessageChatRoomApi(chatRoomId)
72317
- // }
72318
- // }, [chatRoomId])
72319
- // const handleUnpinMessage = useCallback(async (message: any) => {
72320
- // const pinId = message?.pinId || message?.id
72321
- // if (!pinId) { return }
72322
- // await deletePinMessage(pinId)
72323
- // }, [deletePinMessage, chatRoomId])
72324
- // const handleViewPinnedMessage = useCallback((message: any) => {
72325
- // if (message?.id) {
72326
- // //setViewingPinnedMessage(message.id)
72327
- // // Tìm message element
72328
- // const element = document.getElementById(`msg-${message.id}`)
72329
- // const chatContainer = document.getElementById('scrollableDivChat')
72330
- // if (element && chatContainer) {
72331
- // // Lấy vị trí của element và container
72332
- // const elementRect = element.getBoundingClientRect()
72333
- // const containerRect = chatContainer.getBoundingClientRect()
72334
- // // Tính toán scroll position để đưa element vào giữa container
72335
- // const scrollTop = chatContainer.scrollTop
72336
- // const offset = elementRect.top - containerRect.top - (containerRect.height / 2) + (elementRect.height / 2)
72337
- // // Scroll trong container, không scroll toàn page
72338
- // chatContainer.scrollTo({
72339
- // top: scrollTop + offset,
72340
- // behavior: 'smooth'
72341
- // })
72342
- // // Highlight message
72343
- // const originalBg = element.style.backgroundColor
72344
- // const originalZIndex = element.style.zIndex
72345
- // element.style.backgroundColor = '#fff3cd'
72346
- // element.style.zIndex = '1000'
72347
- // element.style.position = 'relative'
72348
- // element.style.transition = 'background-color 0.3s'
72349
- // // Auto close sau 500ms
72350
- // setTimeout(() => {
72351
- // element.style.backgroundColor = originalBg || ''
72352
- // element.style.zIndex = originalZIndex || ''
72353
- // //setViewingPinnedMessage(null)
72354
- // }, 500)
72355
- // } else {
72356
- // console.log('Message not found in current view, may need to load more messages')
72357
- // setTimeout(() => {
72358
- // //setViewingPinnedMessage(null)
72359
- // }, 2000)
72360
- // }
72361
- // }
72362
- // }, [])
72363
- // const onKeyDown = (event: any) => {
72364
- // if (event.code === 'NumpadEnter') {
72365
- // event.preventDefault()
72366
- // const baseOptions = {
72367
- // key: 'Enter',
72368
- // code: 'Enter',
72369
- // which: 13,
72370
- // keyCode: 13,
72371
- // bubbles: true,
72372
- // shiftKey: event.shiftKey,
72373
- // altKey: event.altKey,
72374
- // ctrlKey: event.ctrlKey,
72375
- // metaKey: event.metaKey
72376
- // }
72377
- // const newEvent = new KeyboardEvent('keydown', baseOptions)
72378
- // event.target.dispatchEvent(newEvent)
72379
- // }
72380
- // }
72381
- // const useParsedChatData = (chatMsg: string) => {
72382
- // if (!chatMsg) { return {} }
72383
- // try {
72384
- // return typeof chatMsg === 'string' ? JSON.parse(chatMsg) : chatMsg
72385
- // } catch (err) {
72386
- // console.error('JSON parse error:', err)
72387
- // return {}
72388
- // }
72389
- // }
72390
- // const renderChatText = useCallback((chat: any) => {
72391
- // const isPath = Array.isArray(chat?.path) && typeof chat.path[0]?.path === "string" ? chat.path[0].path.trim() : ""
72392
- // const img = isPath ? `${CDN_URL_VIEW}/${isPath}` : ""
72393
- // const toggleExpanded = (messageId: string) => {
72394
- // const newExpanded = new Set(expandedMessages)
72395
- // if (newExpanded.has(messageId)) {
72396
- // newExpanded.delete(messageId)
72397
- // } else {
72398
- // newExpanded.add(messageId)
72399
- // }
72400
- // setExpandedMessages(newExpanded)
72401
- // }
72402
- // const formatMentionsReact = (text: string, mentions: any[], messageId: string) => {
72403
- // if (!text || !mentions || mentions.length === 0) { return text }
72404
- // const maxLength = 200
72405
- // const isExpanded = expandedMessages.has(messageId)
72406
- // let displayText = text
72407
- // let isTruncated = false
72408
- // if (!isExpanded && text.length > maxLength) {
72409
- // displayText = text.substring(0, maxLength)
72410
- // isTruncated = true
72411
- // }
72412
- // // Chỉ highlight những mention có trong danh sách
72413
- // let result: any = displayText
72414
- // // Hàm helper để xử lý mention trong cả string và array
72415
- // const processMention = (text: string, mention: any): any => {
72416
- // const escapedName = mention?.name?.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
72417
- // const mentionRegex = new RegExp(`@${escapedName}(?=\\s|$|[.,;:!?()[\\]{}'"\`~@])`, 'g')
72418
- // const parts = text.split(mentionRegex)
72419
- // const matches = text.match(mentionRegex) || []
72420
- // if (matches.length === 0) {
72421
- // return text
72422
- // }
72423
- // const newParts: any[] = []
72424
- // parts.forEach((part: any, index: any) => {
72425
- // newParts.push(part)
72426
- // if (matches[index]) {
72427
- // newParts.push(
72428
- // <span key={`mention-${messageId}-${mention.id || mention.name}-${index}`} className="chat-mention">
72429
- // {matches[index]}
72430
- // </span>
72431
- // )
72432
- // }
72433
- // })
72434
- // return newParts
72435
- // }
72436
- // mentions.forEach((mention: any) => {
72437
- // if (typeof result === 'string') {
72438
- // result = processMention(result, mention)
72439
- // } else if (Array.isArray(result)) {
72440
- // // Xử lý từng phần tử trong array
72441
- // const processedArray: any[] = []
72442
- // result.forEach((item: any) => {
72443
- // if (typeof item === 'string') {
72444
- // const processed = processMention(item, mention)
72445
- // if (Array.isArray(processed)) {
72446
- // processedArray.push(...processed)
72447
- // } else {
72448
- // processedArray.push(processed)
72449
- // }
72450
- // } else {
72451
- // // Giữ nguyên các React elements đã được xử lý
72452
- // processedArray.push(item)
72453
- // }
72454
- // })
72455
- // result = processedArray
72456
- // }
72457
- // })
72458
- // // Add expand/collapse functionality
72459
- // if (isTruncated || (isExpanded && text.length > maxLength)) {
72460
- // const expandButton = (
72461
- // <span
72462
- // key={`expand-${messageId}`}
72463
- // onClick={() => toggleExpanded(messageId)}
72464
- // style={{
72465
- // color: '#006edc',
72466
- // cursor: 'pointer',
72467
- // fontWeight: 500,
72468
- // marginLeft: 5
72469
- // }}
72470
- // >
72471
- // {isExpanded ? ' Thu gọn' : '... Mở rộng'}
72472
- // </span>
72473
- // )
72474
- // if (Array.isArray(result)) {
72475
- // result.push(expandButton)
72476
- // } else {
72477
- // result = [result, expandButton]
72478
- // }
72479
- // }
72480
- // return result
72481
- // }
72482
- // return (
72483
- // <>
72484
- // <div>
72485
- // <div>
72486
- // {chat.path && chat.path.length === 1 && (
72487
- // <div className="image-container">
72488
- // {img && <img src={img} alt="" />}
72489
- // </div>
72490
- // )}
72491
- // {isValidUrl(chat.msg) && renderLinkPreview(chat.msg)}
72492
- // {chat.msg && !isValidUrl(chat.msg) ? (
72493
- // <div>{formatMentionsReact(chat.msg, mentionItems, chat.id)}</div>
72494
- // ) : (
72495
- // <></>
72496
- // )}
72497
- // {chat.path && chat.path.length > 1 && (
72498
- // <div className="group-image">
72499
- // {chat.path.map((it: any, index: any) => {
72500
- // return (
72501
- // <div
72502
- // key={index}
72503
- // className="group-image-item image-container"
72504
- // >
72505
- // <img
72506
- // style={{ maxWidth: "100%", maxHeight: 360 }}
72507
- // src={`${CDN_URL_VIEW}/${it.path.trim()}`}
72508
- // onClick={() => handlePreview(it)}
72509
- // alt=""
72510
- // />
72511
- // </div>
72512
- // )
72513
- // })}
72514
- // </div>
72515
- // )}
72516
- // <div className="" style={{ fontSize: "12px", color: "#476285" }}>
72517
- // {moment(chat.time).format("HH:mm")}
72518
- // </div>
72519
- // </div>
72520
- // </div>
72521
- // </>
72522
- // )
72523
- // }, [expandedMessages, handlePreview, mentionItems])
72524
- // const getLinkPreviewData = (urlString: string) => {
72525
- // try {
72526
- // const hasProtocol = /^https?:\/\//i.test(urlString)
72527
- // const href = hasProtocol ? urlString : `https://${urlString}`
72528
- // const url = new URL(href)
72529
- // const displayUrl =
72530
- // urlString.length > 80 ? `${urlString.slice(0, 77)}...` : urlString
72531
- // return {
72532
- // href,
72533
- // displayUrl,
72534
- // host: url.host
72535
- // }
72536
- // } catch (e) {
72537
- // return {
72538
- // href: urlString,
72539
- // displayUrl: urlString,
72540
- // host: ''
72541
- // }
72542
- // }
72543
- // }
72211
+ React.useCallback((idx) => {
72212
+ const newArr = [...imgCopy];
72213
+ newArr.splice(idx, 1);
72214
+ setImgCopy(newArr);
72215
+ }, [imgCopy]);
72216
+ React.useCallback((e, chatMessage) => {
72217
+ const selection = window.document.getSelection();
72218
+ setIsOpen(true);
72219
+ setPosition({ x: e.clientX, y: e.clientY });
72220
+ if (e.target) {
72221
+ setCurrentMessage(chatMessage);
72222
+ }
72223
+ // Detect ảnh được click từ data attribute
72224
+ const fileIndex = e.target?.dataset?.fileIndex;
72225
+ if (fileIndex !== undefined && chatMessage?.path) {
72226
+ setClickedImageFile(chatMessage.path[parseInt(fileIndex)]);
72227
+ }
72228
+ else {
72229
+ setClickedImageFile(null);
72230
+ }
72231
+ e.preventDefault();
72232
+ const contextType = selection.toString() ? 'selection' : chatMessage?.type ? chatMessage.type : 'default';
72233
+ const config = CONTEXT_CONFIG[contextType];
72234
+ setListContextShow({ list: config.show, type: "SHOW" });
72235
+ setListContextHide({ list: config.hide, type: "HIDE" });
72236
+ }, []);
72237
+ const handleTypingStart = () => {
72238
+ if (!isTypingRef.current && connection && chatRoomId) {
72239
+ connection.invoke('StartTyping', chatRoomId).catch(console.error);
72240
+ isTypingRef.current = true;
72241
+ }
72242
+ };
72243
+ const handleTypingStop = () => {
72244
+ if (typingTimeoutRef.current) {
72245
+ clearTimeout(typingTimeoutRef.current);
72246
+ }
72247
+ typingTimeoutRef.current = setTimeout(() => {
72248
+ if (isTypingRef.current && connection && chatRoomId) {
72249
+ connection.invoke('StopTyping', chatRoomId).catch(console.error);
72250
+ isTypingRef.current = false;
72251
+ }
72252
+ }, 2000);
72253
+ };
72254
+ React.useCallback((editorState) => {
72255
+ if (inputContainer &&
72256
+ inputContainer.current &&
72257
+ inputContainer.current.clientHeight > 50) {
72258
+ setIsMultiLine(true);
72259
+ }
72260
+ else {
72261
+ setIsMultiLine(false);
72262
+ }
72263
+ editorState.read(() => {
72264
+ handleTypingStart();
72265
+ handleTypingStop();
72266
+ });
72267
+ }, [inputContainer, connection, chatRoomId]);
72268
+ React.useEffect(() => {
72269
+ if (active) {
72270
+ setReplyMessage(null);
72271
+ setImgCopy([]);
72272
+ setIsAtBottom(true);
72273
+ prevListLength.current = 0;
72274
+ }
72275
+ }, [
72276
+ active,
72277
+ dataInfo
72278
+ //chatRoomId,
72279
+ //chatGetType
72280
+ ]);
72281
+ // Chuyển phòng thì load lại message + pinned messages
72282
+ React.useEffect(() => {
72283
+ if (!chatRoomId && handleSidebar) {
72284
+ return;
72285
+ }
72286
+ chatGetType(chatRoomId, undefined, "", "", undefined, undefined, 0, 0);
72287
+ if (chatRoomId) {
72288
+ getPinMessageChatRoomApi(chatRoomId);
72289
+ }
72290
+ }, [chatRoomId]);
72291
+ React.useCallback(async (message) => {
72292
+ const pinId = message?.pinId || message?.id;
72293
+ if (!pinId) {
72294
+ return;
72295
+ }
72296
+ await deletePinMessage(pinId);
72297
+ }, [deletePinMessage, chatRoomId]);
72298
+ React.useCallback((message) => {
72299
+ if (message?.id) {
72300
+ //setViewingPinnedMessage(message.id)
72301
+ // Tìm message element
72302
+ const element = document.getElementById(`msg-${message.id}`);
72303
+ const chatContainer = document.getElementById('scrollableDivChat');
72304
+ if (element && chatContainer) {
72305
+ // Lấy vị trí của element và container
72306
+ const elementRect = element.getBoundingClientRect();
72307
+ const containerRect = chatContainer.getBoundingClientRect();
72308
+ // Tính toán scroll position để đưa element vào giữa container
72309
+ const scrollTop = chatContainer.scrollTop;
72310
+ const offset = elementRect.top - containerRect.top - (containerRect.height / 2) + (elementRect.height / 2);
72311
+ // Scroll trong container, không scroll toàn page
72312
+ chatContainer.scrollTo({
72313
+ top: scrollTop + offset,
72314
+ behavior: 'smooth'
72315
+ });
72316
+ // Highlight message
72317
+ const originalBg = element.style.backgroundColor;
72318
+ const originalZIndex = element.style.zIndex;
72319
+ element.style.backgroundColor = '#fff3cd';
72320
+ element.style.zIndex = '1000';
72321
+ element.style.position = 'relative';
72322
+ element.style.transition = 'background-color 0.3s';
72323
+ // Auto close sau 500ms
72324
+ setTimeout(() => {
72325
+ element.style.backgroundColor = originalBg || '';
72326
+ element.style.zIndex = originalZIndex || '';
72327
+ //setViewingPinnedMessage(null)
72328
+ }, 500);
72329
+ }
72330
+ else {
72331
+ console.log('Message not found in current view, may need to load more messages');
72332
+ setTimeout(() => {
72333
+ //setViewingPinnedMessage(null)
72334
+ }, 2000);
72335
+ }
72336
+ }
72337
+ }, []);
72338
+ React.useCallback((chat) => {
72339
+ const isPath = Array.isArray(chat?.path) && typeof chat.path[0]?.path === "string" ? chat.path[0].path.trim() : "";
72340
+ const img = isPath ? `${CDN_URL_VIEW}/${isPath}` : "";
72341
+ const toggleExpanded = (messageId) => {
72342
+ const newExpanded = new Set(expandedMessages);
72343
+ if (newExpanded.has(messageId)) {
72344
+ newExpanded.delete(messageId);
72345
+ }
72346
+ else {
72347
+ newExpanded.add(messageId);
72348
+ }
72349
+ setExpandedMessages(newExpanded);
72350
+ };
72351
+ const formatMentionsReact = (text, mentions, messageId) => {
72352
+ if (!text || !mentions || mentions.length === 0) {
72353
+ return text;
72354
+ }
72355
+ const maxLength = 200;
72356
+ const isExpanded = expandedMessages.has(messageId);
72357
+ let displayText = text;
72358
+ let isTruncated = false;
72359
+ if (!isExpanded && text.length > maxLength) {
72360
+ displayText = text.substring(0, maxLength);
72361
+ isTruncated = true;
72362
+ }
72363
+ // Chỉ highlight những mention có trong danh sách
72364
+ let result = displayText;
72365
+ // Hàm helper để xử lý mention trong cả string và array
72366
+ const processMention = (text, mention) => {
72367
+ const escapedName = mention?.name?.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
72368
+ const mentionRegex = new RegExp(`@${escapedName}(?=\\s|$|[.,;:!?()[\\]{}'"\`~@])`, 'g');
72369
+ const parts = text.split(mentionRegex);
72370
+ const matches = text.match(mentionRegex) || [];
72371
+ if (matches.length === 0) {
72372
+ return text;
72373
+ }
72374
+ const newParts = [];
72375
+ parts.forEach((part, index) => {
72376
+ newParts.push(part);
72377
+ if (matches[index]) {
72378
+ newParts.push(jsxRuntime.jsx("span", { className: "chat-mention", children: matches[index] }, `mention-${messageId}-${mention.id || mention.name}-${index}`));
72379
+ }
72380
+ });
72381
+ return newParts;
72382
+ };
72383
+ mentions.forEach((mention) => {
72384
+ if (typeof result === 'string') {
72385
+ result = processMention(result, mention);
72386
+ }
72387
+ else if (Array.isArray(result)) {
72388
+ // Xử lý từng phần tử trong array
72389
+ const processedArray = [];
72390
+ result.forEach((item) => {
72391
+ if (typeof item === 'string') {
72392
+ const processed = processMention(item, mention);
72393
+ if (Array.isArray(processed)) {
72394
+ processedArray.push(...processed);
72395
+ }
72396
+ else {
72397
+ processedArray.push(processed);
72398
+ }
72399
+ }
72400
+ else {
72401
+ // Giữ nguyên các React elements đã được xử lý
72402
+ processedArray.push(item);
72403
+ }
72404
+ });
72405
+ result = processedArray;
72406
+ }
72407
+ });
72408
+ // Add expand/collapse functionality
72409
+ if (isTruncated || (isExpanded && text.length > maxLength)) {
72410
+ const expandButton = (jsxRuntime.jsx("span", { onClick: () => toggleExpanded(messageId), style: {
72411
+ color: '#006edc',
72412
+ cursor: 'pointer',
72413
+ fontWeight: 500,
72414
+ marginLeft: 5
72415
+ }, children: isExpanded ? ' Thu gọn' : '... Mở rộng' }, `expand-${messageId}`));
72416
+ if (Array.isArray(result)) {
72417
+ result.push(expandButton);
72418
+ }
72419
+ else {
72420
+ result = [result, expandButton];
72421
+ }
72422
+ }
72423
+ return result;
72424
+ };
72425
+ return (jsxRuntime.jsx(jsxRuntime.Fragment, { children: jsxRuntime.jsx("div", { children: jsxRuntime.jsxs("div", { children: [chat.path && chat.path.length === 1 && (jsxRuntime.jsx("div", { className: "image-container", children: img && jsxRuntime.jsx("img", { src: img, alt: "" }) })), isValidUrl(chat.msg) && renderLinkPreview(chat.msg), chat.msg && !isValidUrl(chat.msg) ? (jsxRuntime.jsx("div", { children: formatMentionsReact(chat.msg, mentionItems, chat.id) })) : (jsxRuntime.jsx(jsxRuntime.Fragment, {})), chat.path && chat.path.length > 1 && (jsxRuntime.jsx("div", { className: "group-image", children: chat.path.map((it, index) => {
72426
+ return (jsxRuntime.jsx("div", { className: "group-image-item image-container", children: jsxRuntime.jsx("img", { style: { maxWidth: "100%", maxHeight: 360 }, src: `${CDN_URL_VIEW}/${it.path.trim()}`, onClick: () => handlePreview(it), alt: "" }) }, index));
72427
+ }) })), jsxRuntime.jsx("div", { className: "", style: { fontSize: "12px", color: "#476285" }, children: moment(chat.time).format("HH:mm") })] }) }) }));
72428
+ }, [expandedMessages, handlePreview, mentionItems]);
72544
72429
  // const renderLinkPreview = (urlString: string) => {
72545
72430
  // const { href, displayUrl, host } = getLinkPreviewData(urlString)
72546
72431
  // return (