@esvndev/es-react-template-chat 0.0.88 → 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.js CHANGED
@@ -19,9 +19,9 @@ var isNullOrUndefined$2 = require('@src/utility/hooks/isNullOrUndefined');
19
19
  var avatarDefault = require('@core/assets/images/avatars/avatar-blank.png');
20
20
  require('@draft-js-plugins/mention/lib/plugin.css');
21
21
  require('@draft-js-plugins/emoji/lib/plugin.css');
22
+ var reactRedux = require('react-redux');
22
23
  var configureStore$1 = require('@store/configureStore');
23
24
  var reactRouterDom = require('react-router-dom');
24
- var reactRedux = require('react-redux');
25
25
 
26
26
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
27
27
 
@@ -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,44 +72111,73 @@ 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);
72125
- React.useState(false);
72123
+ const [imgCopy, setImgCopy] = React.useState([]);
72124
+ const [isOpen, setIsOpen] = React.useState(false);
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);
72136
- React.useState(null);
72137
- React.useState(false);
72138
- React.useState(true);
72139
- React.useRef(0);
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
+ const [selectedMessage, setSelectedMessage] = React.useState(null);
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
72142
  React.useState(new Set());
72143
- React.useState(null);
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');
72149
72179
  messagesEndEle?.scrollIntoView({ block: 'end', behavior: "instant" });
72150
72180
  };
72151
- //từ đây
72152
72181
  // Load initial messages
72153
72182
  React.useEffect(() => {
72154
72183
  if (!checkScroll) {
@@ -72173,340 +72202,139 @@ const ChatLog = (props) => {
72173
72202
  });
72174
72203
  return [{ id: "all", name: "All" }, ...mappedUsers];
72175
72204
  }, [groupChatUsers]);
72176
- // const handlePreview = useCallback((mes: any) => {
72177
- // setSelectedMessage(mes)
72178
- // setOpenModalPreview(true)
72179
- // }, [])
72180
- // const [isFetching, setIsFetching] = useState(false)
72181
- // const dispatch = useDispatch()
72182
- // const handleStartConversation = () => {
72183
- // if (
72184
- // !Object.keys({}).length &&
72185
- // !userSidebarLeft &&
72186
- // window.innerWidth < 992
72187
- // ) {
72188
- // handleSidebar()
72189
- // }
72190
- // }
72191
- // const uploadGroup = async (files: File[], type: 'image' | 'file') => {
72192
- // if (files.length === 0) { return }
72193
- // const batchId = newGuid()
72194
- // const errors: Array<{ file: File; error: any }> = []
72195
- // // Upload gửi từng file một
72196
- // for (const file of files) {
72197
- // try {
72198
- // const result = await uploadWithRetry(file)
72199
- // if (result.success && chatRoomId) {
72200
- // const infoFiles = result.data?.infoFiles || []
72201
- // const items = type === 'image' ? infoFiles.map((x: any) => ({ ...x, batchId, createdDate: new Date().toISOString() })) : infoFiles
72202
- // // Gửi message ngay sau khi upload thành công
72203
- // connectHub(chatRoomId, {
72204
- // sendTo: dataInfo.id,
72205
- // content: '',
72206
- // type,
72207
- // path: JSON.stringify(items),
72208
- // typeChat
72209
- // })
72210
- // } else {
72211
- // errors.push({ file, error: result.error })
72212
- // }
72213
- // } catch (error) {
72214
- // errors.push({ file, error })
72215
- // }
72216
- // }
72217
- // // Thông báo lỗi nếu có
72218
- // if (errors.length > 0) {
72219
- // notificationError(`${errors.length}/${files.length} ${type === 'image' ? 'ảnh' : 'file'} upload thất bại`)
72220
- // }
72221
- // }
72222
- // const dataURLToFile = (dataUrl: string, fileName: string) => {
72223
- // const [header, data] = dataUrl.split(',')
72224
- // const mime = header.match(/data:(.*?);/)?.[1] || 'image/png'
72225
- // const binary = atob(data)
72226
- // const len = binary.length
72227
- // const u8 = new Uint8Array(len)
72228
- // for (let i = 0; i < len; i++) { u8[i] = binary.charCodeAt(i) }
72229
- // return new File([u8], fileName, { type: mime })
72230
- // }
72231
- // // ** Extract tagUserIds from editor mentions
72232
- // const extractTagUserIds = (): string[] => {
72233
- // const tagUserIds: string[] = []
72234
- // let hasAllMention = false
72235
- // editor.getEditorState().read(() => {
72236
- // const root = $getRoot()
72237
- // const nodes = root.getAllTextNodes()
72238
- // nodes.forEach((node) => {
72239
- // if ($isMentionNode(node)) {
72240
- // const userId = (node as any).__userId
72241
- // if (userId === 'all') {
72242
- // hasAllMention = true
72243
- // } else if (userId && !tagUserIds.includes(userId)) {
72244
- // tagUserIds.push(userId)
72245
- // }
72246
- // }
72247
- // })
72248
- // })
72249
- // // Nếu có @All, lấy tất cả userId trong nhóm
72250
- // if (hasAllMention && groupChatUsers?.users) {
72251
- // const allUserIds = groupChatUsers.users
72252
- // .map((user: any) => user.id)
72253
- // .filter((id: string) => id !== dataProfile?.id) // Loại bỏ chính mình
72254
- // return allUserIds
72255
- // }
72256
- // return tagUserIds
72257
- // }
72258
- // // ** Sends New Msg
72259
- // const handleSendMsg = (message: any) => {
72260
- // if (imgCopy.length > 0) {
72261
- // const imageFiles: File[] = []
72262
- // const otherFiles: File[] = []
72263
- // imgCopy.forEach((item: any, idx: number) => {
72264
- // const data = item?.data || item
72265
- // if (typeof data === 'string' && data.startsWith('data:image')) {
72266
- // imageFiles.push(dataURLToFile(data, item.name || `pasted_${Date.now()}_${idx}.png`))
72267
- // } else if (data instanceof File) {
72268
- // if (data.type?.startsWith('image')) {
72269
- // imageFiles.push(data)
72270
- // } else {
72271
- // otherFiles.push(data)
72272
- // }
72273
- // }
72274
- // })
72275
- // // Gửi ảnh trước rồi file
72276
- // uploadGroup(imageFiles, 'image')
72277
- // uploadGroup(otherFiles, 'file')
72278
- // }
72279
- // if (message.trim().length) {
72280
- // if (typingTimeoutRef.current) {
72281
- // clearTimeout(typingTimeoutRef.current)
72282
- // }
72283
- // isTypingRef.current = false
72284
- // if (connection && chatRoomId) {
72285
- // connection.invoke('StopTyping', chatRoomId).catch(console.error)
72286
- // }
72287
- // const tagUserIds = extractTagUserIds()
72288
- // const id = typeChat === 'CHAT' ? chatRoomId : contactId
72289
- // if (id) {
72290
- // connectHub(id, {
72291
- // sendTo: dataInfo.id,
72292
- // content: message.trim(),
72293
- // replyContent: replyMessage?.currentMessage ? JSON.stringify(replyMessage.currentMessage) : null,
72294
- // path: null,
72295
- // type: 'text',
72296
- // typeChat,
72297
- // tagUserIds
72298
- // })
72299
- // }
72300
- // }
72301
- // setImgCopy([])
72302
- // }
72303
- // const uploadWithRetry = async (file: File, maxRetries = 2) => {
72304
- // let lastError
72305
- // for (let attempt = 0; attempt <= maxRetries; attempt++) {
72306
- // try {
72307
- // const formDataFile = new FormData()
72308
- // formDataFile.append('files', file, file.name)
72309
- // const rs = await uploadMultiFileApi(formDataFile).unwrap()
72310
- // return { success: true, data: rs }
72311
- // } catch (error: any) {
72312
- // lastError = error
72313
- // // Không retry nếu là lỗi validation từ server
72314
- // if (error?.status === 400 || error?.status === 413) {
72315
- // break
72316
- // }
72317
- // if (attempt < maxRetries) {
72318
- // // Exponential backoff: 1s, 2s, 4s...
72319
- // await new Promise(resolve => setTimeout(resolve, 1000 * Math.pow(2, attempt))
72320
- // )
72321
- // }
72322
- // }
72323
- // }
72324
- // return {
72325
- // success: false,
72326
- // error: lastError?.message || 'Upload thất bại'
72327
- // }
72328
- // }
72329
- // const sendFile = async (event: any) => {
72330
- // if (event.target.files && event.target.files[0]) {
72331
- // const filesToUpload: File[] = Array.from(event.target.files)
72332
- // const errors: Array<{ file: File, error: any }> = []
72333
- // for (const file of filesToUpload) {
72334
- // const result = await uploadWithRetry(file)
72335
- // if (result.success && chatRoomId) {
72336
- // connectHub(chatRoomId, {
72337
- // sendTo: dataInfo.id,
72338
- // content: '',
72339
- // type: 'file',
72340
- // path: JSON.stringify(result.data?.infoFiles),
72341
- // typeChat
72342
- // })
72343
- // } else {
72344
- // errors.push({ file, error: result.error })
72345
- // }
72346
- // }
72347
- // if (errors.length > 0) {
72348
- // notificationError(`${errors.length} file upload thất bại`)
72349
- // }
72350
- // event.target.value = ''
72351
- // }
72352
- // }
72353
- // const sendImage = async (event: any) => {
72354
- // if (event.target.files && event.target.files[0]) {
72355
- // const filesToUpload: File[] = Array.from(event.target.files)
72356
- // const errors: Array<{ file: File, error: any }> = []
72357
- // const batchId = newGuid()
72358
- // for (const file of filesToUpload) {
72359
- // const result = await uploadWithRetry(file)
72360
- // const item = result.data?.infoFiles?.map((x: any) => ({ ...x, batchId, createdDate: new Date().toISOString() })) || []
72361
- // if (result.success && chatRoomId && result?.data?.status) {
72362
- // connectHub(chatRoomId, {
72363
- // sendTo: dataInfo.id,
72364
- // content: '',
72365
- // type: 'image',
72366
- // path: JSON.stringify(item),
72367
- // typeChat
72368
- // })
72369
- // } else {
72370
- // errors.push({ file, error: result.error })
72371
- // }
72372
- // }
72373
- // if (errors.length > 0) {
72374
- // notificationError(`${errors.length} ảnh upload thất bại`)
72375
- // }
72376
- // event.target.value = ''
72377
- // }
72378
- // }
72379
- // const handleRemoveImage = useCallback((idx: number) => {
72380
- // const newArr = [...imgCopy]
72381
- // newArr.splice(idx, 1)
72382
- // setImgCopy(newArr)
72383
- // }, [imgCopy])
72384
- // const onContextMenu = useCallback((e: any, chatMessage: any) => {
72385
- // const selection: any = window.document.getSelection()
72386
- // setIsOpen(true)
72387
- // setPosition({ x: e.clientX, y: e.clientY })
72388
- // if (e.target) {
72389
- // setCurrentMessage(chatMessage)
72390
- // }
72391
- // // Detect ảnh được click từ data attribute
72392
- // const fileIndex = e.target?.dataset?.fileIndex
72393
- // if (fileIndex !== undefined && chatMessage?.path) {
72394
- // setClickedImageFile(chatMessage.path[parseInt(fileIndex)])
72395
- // } else {
72396
- // setClickedImageFile(null)
72397
- // }
72398
- // e.preventDefault()
72399
- // const contextType = selection.toString() ? 'selection' : chatMessage?.type ? chatMessage.type : 'default'
72400
- // const config = CONTEXT_CONFIG[contextType]
72401
- // setListContextShow({ list: config.show, type: "SHOW" })
72402
- // setListContextHide({ list: config.hide, type: "HIDE" })
72403
- // }, [])
72404
- // const clearReply = () => {
72405
- // setReplyMessage(null)
72406
- // }
72407
- // const handleTypingStart = () => {
72408
- // if (!isTypingRef.current && connection && chatRoomId) {
72409
- // connection.invoke('StartTyping', chatRoomId).catch(console.error)
72410
- // isTypingRef.current = true
72411
- // }
72412
- // }
72413
- // const handleTypingStop = () => {
72414
- // if (typingTimeoutRef.current) { clearTimeout(typingTimeoutRef.current) }
72415
- // typingTimeoutRef.current = setTimeout(() => {
72416
- // if (isTypingRef.current && connection && chatRoomId) {
72417
- // connection.invoke('StopTyping', chatRoomId).catch(console.error)
72418
- // isTypingRef.current = false
72419
- // }
72420
- // }, 2000)
72421
- // }
72422
- // const handleChange = useCallback(
72423
- // (editorState: EditorState) => {
72424
- // if (
72425
- // inputContainer &&
72426
- // inputContainer.current &&
72427
- // inputContainer.current.clientHeight > 50
72428
- // ) {
72429
- // setIsMultiLine(true)
72430
- // } else {
72431
- // setIsMultiLine(false)
72432
- // }
72433
- // editorState.read(() => {
72434
- // handleTypingStart()
72435
- // handleTypingStop()
72436
- // })
72437
- // },
72438
- // [inputContainer, connection, chatRoomId]
72439
- // )
72440
- // const onEnter = (val: any) => {
72441
- // handleSendMsg(val)
72442
- // clearReply()
72443
- // setReplyMessage(null)
72444
- // }
72445
- // useEffect(() => {
72446
- // if (active) {
72447
- // setReplyMessage(null)
72448
- // setImgCopy([])
72449
- // setIsAtBottom(true)
72450
- // prevListLength.current = 0
72451
- // }
72452
- // }, [
72453
- // active,
72454
- // dataInfo
72455
- // //chatRoomId,
72456
- // //chatGetType
72457
- // ])
72458
- // // Chuyển phòng thì load lại message + pinned messages
72459
- // useEffect(() => {
72460
- // if (!chatRoomId && handleSidebar) { return }
72461
- // chatGetType(chatRoomId, undefined, "", "", undefined, undefined, 0, 0)
72462
- // if (chatRoomId) {
72463
- // getPinMessageChatRoomApi(chatRoomId)
72464
- // }
72465
- // }, [chatRoomId])
72466
- // const handleUnpinMessage = useCallback(async (message: any) => {
72467
- // const pinId = message?.pinId || message?.id
72468
- // if (!pinId) { return }
72469
- // await deletePinMessage(pinId)
72470
- // }, [deletePinMessage, chatRoomId])
72471
- // const handleViewPinnedMessage = useCallback((message: any) => {
72472
- // if (message?.id) {
72473
- // //setViewingPinnedMessage(message.id)
72474
- // // Tìm message element
72475
- // const element = document.getElementById(`msg-${message.id}`)
72476
- // const chatContainer = document.getElementById('scrollableDivChat')
72477
- // if (element && chatContainer) {
72478
- // // Lấy vị trí của element và container
72479
- // const elementRect = element.getBoundingClientRect()
72480
- // const containerRect = chatContainer.getBoundingClientRect()
72481
- // // Tính toán scroll position để đưa element vào giữa container
72482
- // const scrollTop = chatContainer.scrollTop
72483
- // const offset = elementRect.top - containerRect.top - (containerRect.height / 2) + (elementRect.height / 2)
72484
- // // Scroll trong container, không scroll toàn page
72485
- // chatContainer.scrollTo({
72486
- // top: scrollTop + offset,
72487
- // behavior: 'smooth'
72488
- // })
72489
- // // Highlight message
72490
- // const originalBg = element.style.backgroundColor
72491
- // const originalZIndex = element.style.zIndex
72492
- // element.style.backgroundColor = '#fff3cd'
72493
- // element.style.zIndex = '1000'
72494
- // element.style.position = 'relative'
72495
- // element.style.transition = 'background-color 0.3s'
72496
- // // Auto close sau 500ms
72497
- // setTimeout(() => {
72498
- // element.style.backgroundColor = originalBg || ''
72499
- // element.style.zIndex = originalZIndex || ''
72500
- // //setViewingPinnedMessage(null)
72501
- // }, 500)
72502
- // } else {
72503
- // console.log('Message not found in current view, may need to load more messages')
72504
- // setTimeout(() => {
72505
- // //setViewingPinnedMessage(null)
72506
- // }, 2000)
72507
- // }
72508
- // }
72509
- // }, [])
72205
+ React.useCallback((mes) => {
72206
+ setSelectedMessage(mes);
72207
+ setOpenModalPreview(true);
72208
+ }, []);
72209
+ React.useState(false);
72210
+ reactRedux.useDispatch();
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
+ }, []);
72510
72338
  // const onKeyDown = (event: any) => {
72511
72339
  // if (event.code === 'NumpadEnter') {
72512
72340
  // event.preventDefault()