detach-context.js 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. import {
  2. createContext,
  3. useContext,
  4. useCallback,
  5. useMemo,
  6. useEffect,
  7. useState,
  8. } from 'react'
  9. import PropTypes from 'prop-types'
  10. import sysend from 'sysend'
  11. import getMeta from '../../utils/meta'
  12. import { buildUrlWithDetachRole } from '../utils/url-helper'
  13. import useCallbackHandlers from '../hooks/use-callback-handlers'
  14. export const DetachContext = createContext()
  15. DetachContext.Provider.propTypes = {
  16. value: PropTypes.shape({
  17. role: PropTypes.oneOf(['detacher', 'detached', null]),
  18. setRole: PropTypes.func.isRequired,
  19. broadcastEvent: PropTypes.func.isRequired,
  20. addEventHandler: PropTypes.func.isRequired,
  21. deleteEventHandler: PropTypes.func.isRequired,
  22. }).isRequired,
  23. }
  24. const debugPdfDetach = getMeta('ol-debugPdfDetach')
  25. const SYSEND_CHANNEL = `detach-${getMeta('ol-project_id')}`
  26. export function DetachProvider({ children }) {
  27. const [lastDetachedConnectedAt, setLastDetachedConnectedAt] = useState()
  28. const [role, setRole] = useState(() => getMeta('ol-detachRole') || null)
  29. const {
  30. addHandler: addEventHandler,
  31. deleteHandler: deleteEventHandler,
  32. callHandlers: callEventHandlers,
  33. } = useCallbackHandlers()
  34. useEffect(() => {
  35. if (debugPdfDetach) {
  36. console.log('Effect', { role })
  37. }
  38. window.history.replaceState({}, '', buildUrlWithDetachRole(role).toString())
  39. }, [role])
  40. useEffect(() => {
  41. sysend.on(SYSEND_CHANNEL, message => {
  42. if (debugPdfDetach) {
  43. console.log(`Receiving:`, message)
  44. }
  45. callEventHandlers(message)
  46. })
  47. return () => sysend.off(SYSEND_CHANNEL)
  48. }, [callEventHandlers])
  49. const broadcastEvent = useCallback(
  50. (event, data) => {
  51. if (!role) {
  52. if (debugPdfDetach) {
  53. console.log('Not Broadcasting (no role)', {
  54. role,
  55. event,
  56. data,
  57. })
  58. }
  59. return
  60. }
  61. if (debugPdfDetach) {
  62. console.log('Broadcasting', {
  63. role,
  64. event,
  65. data,
  66. })
  67. }
  68. const message = {
  69. role,
  70. event,
  71. }
  72. if (data) {
  73. message.data = data
  74. }
  75. sysend.broadcast(SYSEND_CHANNEL, message)
  76. },
  77. [role]
  78. )
  79. useEffect(() => {
  80. broadcastEvent('connected')
  81. }, [broadcastEvent])
  82. useEffect(() => {
  83. const onBeforeUnload = () => broadcastEvent('closed')
  84. window.addEventListener('beforeunload', onBeforeUnload)
  85. return () => window.removeEventListener('beforeunload', onBeforeUnload)
  86. }, [broadcastEvent])
  87. useEffect(() => {
  88. const updateLastDetachedConnectedAt = message => {
  89. if (message.role === 'detached' && message.event === 'connected') {
  90. setLastDetachedConnectedAt(new Date())
  91. }
  92. }
  93. addEventHandler(updateLastDetachedConnectedAt)
  94. return () => deleteEventHandler(updateLastDetachedConnectedAt)
  95. }, [addEventHandler, deleteEventHandler])
  96. const value = useMemo(
  97. () => ({
  98. role,
  99. setRole,
  100. broadcastEvent,
  101. lastDetachedConnectedAt,
  102. addEventHandler,
  103. deleteEventHandler,
  104. }),
  105. [
  106. role,
  107. setRole,
  108. broadcastEvent,
  109. lastDetachedConnectedAt,
  110. addEventHandler,
  111. deleteEventHandler,
  112. ]
  113. )
  114. return (
  115. <DetachContext.Provider value={value}>{children}</DetachContext.Provider>
  116. )
  117. }
  118. DetachProvider.propTypes = {
  119. children: PropTypes.any,
  120. }
  121. export function useDetachContext(propTypes) {
  122. const data = useContext(DetachContext)
  123. PropTypes.checkPropTypes(propTypes, data, 'data', 'DetachContext.Provider')
  124. return data
  125. }