use-detach-layout.js 4.3 KB

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