use-detach-layout.js 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. import { useCallback, useState, useEffect, useRef } from 'react'
  2. import { useDetachContext } from '../context/detach-context'
  3. import getMeta from '../../utils/meta'
  4. import { buildUrlWithDetachRole } from '../utils/url-helper'
  5. import * as eventTracking from '../../infrastructure/event-tracking'
  6. import usePreviousValue from './use-previous-value'
  7. const debugPdfDetach = getMeta('ol-debugPdfDetach')
  8. const LINKING_TIMEOUT = 60000
  9. const RELINK_TIMEOUT = 10000
  10. export default function useDetachLayout() {
  11. const {
  12. role,
  13. setRole,
  14. broadcastEvent,
  15. addEventHandler,
  16. deleteEventHandler,
  17. } = useDetachContext()
  18. // isLinking: when the tab expects to be linked soon (e.g. on detach)
  19. const [isLinking, setIsLinking] = useState(false)
  20. // isLinked: when the tab is linked to another tab (of different role)
  21. const [isLinked, setIsLinked] = useState(false)
  22. const uiTimeoutRef = useRef()
  23. useEffect(() => {
  24. if (debugPdfDetach) {
  25. console.log('Effect', { isLinked })
  26. }
  27. setIsLinking(false)
  28. }, [isLinked, setIsLinking])
  29. useEffect(() => {
  30. if (uiTimeoutRef.current) {
  31. clearTimeout(uiTimeoutRef.current)
  32. }
  33. if (role === 'detacher' && isLinked === false) {
  34. // the detacher tab either a) disconnected from its detached tab(s), b)is
  35. // loading and no detached tab(s) is connected yet or c) is detaching and
  36. // waiting for the detached tab to connect. Start a timeout to put
  37. // the tab back in non-detacher role in case no detached tab are connected
  38. uiTimeoutRef.current = setTimeout(
  39. () => {
  40. setRole(null)
  41. },
  42. isLinking ? LINKING_TIMEOUT : RELINK_TIMEOUT
  43. )
  44. }
  45. }, [role, isLinking, isLinked, setRole])
  46. useEffect(() => {
  47. if (debugPdfDetach) {
  48. console.log('Effect', { isLinking })
  49. }
  50. }, [isLinking])
  51. const previousRole = usePreviousValue(role)
  52. useEffect(() => {
  53. if (previousRole && !role) {
  54. eventTracking.sendMB('project-layout-reattached')
  55. }
  56. }, [previousRole, role])
  57. const reattach = useCallback(() => {
  58. broadcastEvent('reattach')
  59. setRole(null)
  60. setIsLinked(false)
  61. }, [setRole, setIsLinked, broadcastEvent])
  62. const detach = useCallback(() => {
  63. setRole('detacher')
  64. setIsLinking(true)
  65. window.open(buildUrlWithDetachRole('detached').toString(), '_blank')
  66. }, [setRole, setIsLinking])
  67. const handleEventForDetacherFromDetached = useCallback(
  68. message => {
  69. switch (message.event) {
  70. case 'connected':
  71. broadcastEvent('up')
  72. setIsLinked(true)
  73. break
  74. case 'up':
  75. setIsLinked(true)
  76. break
  77. case 'closed':
  78. setIsLinked(false)
  79. break
  80. }
  81. },
  82. [setIsLinked, broadcastEvent]
  83. )
  84. const handleEventForDetachedFromDetacher = useCallback(
  85. message => {
  86. switch (message.event) {
  87. case 'connected':
  88. broadcastEvent('up')
  89. setIsLinked(true)
  90. break
  91. case 'up':
  92. setIsLinked(true)
  93. break
  94. case 'closed':
  95. setIsLinked(false)
  96. break
  97. case 'reattach':
  98. setIsLinked(false) // set as unlinked, in case closing is not allowed
  99. window.close()
  100. break
  101. }
  102. },
  103. [setIsLinked, broadcastEvent]
  104. )
  105. const handleEventFromSelf = useCallback(
  106. message => {
  107. switch (message.event) {
  108. case 'closed':
  109. broadcastEvent('up')
  110. break
  111. }
  112. },
  113. [broadcastEvent]
  114. )
  115. const handleEvent = useCallback(
  116. message => {
  117. if (role === 'detacher') {
  118. if (message.role === 'detacher') {
  119. handleEventFromSelf(message)
  120. } else if (message.role === 'detached') {
  121. handleEventForDetacherFromDetached(message)
  122. }
  123. } else if (role === 'detached') {
  124. if (message.role === 'detacher') {
  125. handleEventForDetachedFromDetacher(message)
  126. } else if (message.role === 'detached') {
  127. handleEventFromSelf(message)
  128. }
  129. }
  130. },
  131. [
  132. role,
  133. handleEventForDetacherFromDetached,
  134. handleEventForDetachedFromDetacher,
  135. handleEventFromSelf,
  136. ]
  137. )
  138. useEffect(() => {
  139. addEventHandler(handleEvent)
  140. return () => deleteEventHandler(handleEvent)
  141. }, [addEventHandler, deleteEventHandler, handleEvent])
  142. return {
  143. reattach,
  144. detach,
  145. isLinked,
  146. isLinking,
  147. role,
  148. }
  149. }