chat-context.js 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. import React, {
  2. createContext,
  3. useCallback,
  4. useContext,
  5. useState,
  6. useEffect
  7. } from 'react'
  8. import PropTypes from 'prop-types'
  9. import { useApplicationContext } from '../../../shared/context/application-context'
  10. import { useEditorContext } from '../../../shared/context/editor-context'
  11. import { ChatStore } from '../store/chat-store'
  12. import useBrowserWindow from '../../../infrastructure/browser-window-hook'
  13. import { useLayoutContext } from '../../../shared/context/layout-context'
  14. export const ChatContext = createContext()
  15. ChatContext.Provider.propTypes = {
  16. value: PropTypes.shape({
  17. userId: PropTypes.string.isRequired,
  18. atEnd: PropTypes.bool,
  19. loading: PropTypes.bool,
  20. messages: PropTypes.array.isRequired,
  21. unreadMessageCount: PropTypes.number.isRequired,
  22. resetUnreadMessageCount: PropTypes.func.isRequired,
  23. loadMoreMessages: PropTypes.func.isRequired,
  24. sendMessage: PropTypes.func.isRequired
  25. }).isRequired
  26. }
  27. export function ChatProvider({ children }) {
  28. const {
  29. hasFocus: windowHasFocus,
  30. flashTitle,
  31. stopFlashingTitle
  32. } = useBrowserWindow()
  33. const { user } = useApplicationContext({
  34. user: PropTypes.shape({ id: PropTypes.string.isRequired }.isRequired)
  35. })
  36. const { projectId } = useEditorContext({
  37. projectId: PropTypes.string.isRequired
  38. })
  39. const { chatIsOpen } = useLayoutContext({ chatIsOpen: PropTypes.bool })
  40. const [unreadMessageCount, setUnreadMessageCount] = useState(0)
  41. function resetUnreadMessageCount() {
  42. setUnreadMessageCount(0)
  43. }
  44. const [atEnd, setAtEnd] = useState(false)
  45. const [loading, setLoading] = useState(false)
  46. const [messages, setMessages] = useState([])
  47. const [store] = useState(() => new ChatStore(user, projectId))
  48. useEffect(() => {
  49. if (windowHasFocus) {
  50. stopFlashingTitle()
  51. if (chatIsOpen) {
  52. setUnreadMessageCount(0)
  53. }
  54. }
  55. if (!windowHasFocus && unreadMessageCount > 0) {
  56. flashTitle('New Message')
  57. }
  58. }, [
  59. windowHasFocus,
  60. chatIsOpen,
  61. unreadMessageCount,
  62. flashTitle,
  63. stopFlashingTitle
  64. ])
  65. useEffect(() => {
  66. function updateState() {
  67. setAtEnd(store.atEnd)
  68. setLoading(store.loading)
  69. setMessages(store.messages)
  70. }
  71. function handleNewMessage() {
  72. setUnreadMessageCount(prevCount => prevCount + 1)
  73. }
  74. store.on('updated', updateState)
  75. store.on('message-received', handleNewMessage)
  76. updateState()
  77. return () => store.destroy()
  78. }, [store])
  79. const loadMoreMessages = useCallback(() => store.loadMoreMessages(), [store])
  80. const sendMessage = useCallback(message => store.sendMessage(message), [
  81. store
  82. ])
  83. const value = {
  84. userId: user.id,
  85. atEnd,
  86. loading,
  87. messages,
  88. unreadMessageCount,
  89. resetUnreadMessageCount,
  90. loadMoreMessages,
  91. sendMessage
  92. }
  93. return <ChatContext.Provider value={value}>{children}</ChatContext.Provider>
  94. }
  95. ChatProvider.propTypes = {
  96. children: PropTypes.any
  97. }
  98. export function useChatContext(propTypes) {
  99. const data = useContext(ChatContext)
  100. PropTypes.checkPropTypes(propTypes, data, 'data', 'ChatContext.Provider')
  101. return data
  102. }