1- import  React ,  {  useMemo ,  useState  }  from  'react' 
1+ import  React ,  {  useEffect ,   useMemo ,   useRef ,  useState  }  from  'react' 
22import  cn  from  "classnames" ; 
33import  ReactMarkdown ,  {  Components  }  from  "react-markdown" ; 
44import  rehypeRaw  from  "rehype-raw" ; 
@@ -9,8 +9,9 @@ import { icons } from "@postgres.ai/shared/styles/icons";
99import  {  DebugDialog  }  from  "../../DebugDialog/DebugDialog" ; 
1010import  {  CodeBlock  }  from  "./CodeBlock" ; 
1111import  {  disallowedHtmlTagsForMarkdown ,  permalinkLinkBuilder  }  from  "../../utils" ; 
12- import  {  StateMessage  }  from  "../../../../types/api/entities/bot" ; 
12+ import  {  MessageStatus ,   StateMessage  }  from  "../../../../types/api/entities/bot" ; 
1313import  {  MermaidDiagram  }  from  "./MermaidDiagram" ; 
14+ import  {  useAiBot  }  from  "../../hooks" ; 
1415
1516
1617type  BaseMessageProps  =  { 
@@ -20,17 +21,19 @@ type BaseMessageProps = {
2021  name ?: string ; 
2122  isLoading ?: boolean ; 
2223  formattedTime ?: string ; 
23-   aiModel ?: string 
24-   stateMessage ?: StateMessage  |  null 
25-   isCurrentStreamMessage ?: boolean 
24+   aiModel ?: string ; 
25+   stateMessage ?: StateMessage  |  null ; 
26+   isCurrentStreamMessage ?: boolean ; 
2627  isPublic ?: boolean ; 
28+   threadId ?: string ; 
29+   status ?: MessageStatus 
2730} 
2831
2932type  AiMessageProps  =  BaseMessageProps  &  { 
3033  isAi : true ; 
3134  content : string ; 
32-   aiModel : string 
33-   isCurrentStreamMessage ?: boolean 
35+   aiModel : string ; 
36+   isCurrentStreamMessage ?: boolean ; 
3437} 
3538
3639type  HumanMessageProps  =  BaseMessageProps  &  { 
@@ -42,8 +45,8 @@ type HumanMessageProps = BaseMessageProps & {
4245type  LoadingMessageProps  =  BaseMessageProps  &  { 
4346  isLoading : true ; 
4447  isAi : true ; 
45-   content ?: undefined 
46-   stateMessage : StateMessage  |  null 
48+   content ?: undefined ; 
49+   stateMessage : StateMessage  |  null ; 
4750} 
4851
4952type  MessageProps  =  AiMessageProps  |  HumanMessageProps  |  LoadingMessageProps ; 
@@ -261,14 +264,44 @@ export const Message = React.memo((props: MessageProps) => {
261264    aiModel, 
262265    stateMessage, 
263266    isCurrentStreamMessage, 
264-     isPublic
267+     isPublic, 
268+     threadId, 
269+     status
265270  }  =  props ; 
266271
272+   const  {  updateMessageStatus }  =  useAiBot ( ) 
273+ 
274+   const  elementRef  =  useRef < HTMLDivElement  |  null > ( null ) ; 
275+ 
276+ 
267277  const  [ isDebugVisible ,  setDebugVisible ]  =  useState ( false ) ; 
268278
269279
270280  const  classes  =  useStyles ( ) ; 
271281
282+   useEffect ( ( )  =>  { 
283+     if  ( ! isAi  ||  isCurrentStreamMessage  ||  status  ===  'read' )  return ; 
284+ 
285+     const  observer  =  new  IntersectionObserver ( 
286+       ( entries )  =>  { 
287+         const  entry  =  entries [ 0 ] ; 
288+         if  ( entry . isIntersecting  &&  threadId  &&  id )  { 
289+           updateMessageStatus ( threadId ,  id ,  'read' ) ; 
290+           observer . disconnect ( ) ; 
291+         } 
292+       } , 
293+       {  threshold : 0.1  } 
294+     ) ; 
295+ 
296+     if  ( elementRef . current )  { 
297+       observer . observe ( elementRef . current ) ; 
298+     } 
299+ 
300+     return  ( )  =>  { 
301+       observer . disconnect ( ) ; 
302+     } ; 
303+   } ,  [ id ,  updateMessageStatus ,  isCurrentStreamMessage ,  isAi ,  threadId ,  status ] ) ; 
304+ 
272305  const  contentToRender : string  =  content ?. replace ( / \n / g,  '  \n' )  ||  '' 
273306
274307  const  toggleDebugDialog  =  ( )  =>  { 
@@ -301,7 +334,7 @@ export const Message = React.memo((props: MessageProps) => {
301334        onClose = { toggleDebugDialog } 
302335        messageId = { id } 
303336      /> } 
304-       < div  className = { classes . message } > 
337+       < div  ref = { elementRef }   className = { classes . message } > 
305338        < div  className = { classes . messageAvatar } > 
306339          { isAi 
307340            ? < img 
0 commit comments