overleaf/services/web/frontend/js/shared/hooks/use-detach-layout.ts
ilkin-overleaf d959dbc236 Merge pull request #11105 from overleaf/ii-shared-hooks-js-to-ts-conversion
[web] Shared React hooks JS to TS conversion

GitOrigin-RevId: 0ccdebff236c7424b1a73cd7d6646a9d01a20eb1
2023-01-10 09:05:52 +00:00

190 lines
5.1 KiB
TypeScript

import { useCallback, useState, useEffect, useRef } from 'react'
import { useDetachContext } from '../context/detach-context'
import getMeta from '../../utils/meta'
import { buildUrlWithDetachRole } from '../utils/url-helper'
import * as eventTracking from '../../infrastructure/event-tracking'
import usePreviousValue from './use-previous-value'
const debugPdfDetach = getMeta('ol-debugPdfDetach')
const LINKING_TIMEOUT = 60000
const RELINK_TIMEOUT = 10000
export default function useDetachLayout() {
const { role, setRole, broadcastEvent, addEventHandler, deleteEventHandler } =
useDetachContext()
// isLinking: when the tab expects to be linked soon (e.g. on detach)
const [isLinking, setIsLinking] = useState(false)
// isLinked: when the tab is linked to another tab (of different role)
const [isLinked, setIsLinked] = useState(false)
// isRedundant: when a second detacher tab is opened, the first becomes
// redundant
const [isRedundant, setIsRedundant] = useState(false)
const uiTimeoutRef = useRef<ReturnType<typeof setTimeout>>()
useEffect(() => {
if (debugPdfDetach) {
console.log('Effect', { isLinked })
}
setIsLinking(false)
}, [isLinked, setIsLinking])
useEffect(() => {
if (debugPdfDetach) {
console.log('Effect', { role, isLinked })
}
if (role === 'detached' && isLinked) {
eventTracking.sendMB('project-layout-detached')
}
}, [role, isLinked])
useEffect(() => {
if (uiTimeoutRef.current) {
clearTimeout(uiTimeoutRef.current)
}
if (role === 'detacher' && isLinked === false) {
// the detacher tab either a) disconnected from its detached tab(s), b)is
// loading and no detached tab(s) is connected yet or c) is detaching and
// waiting for the detached tab to connect. Start a timeout to put
// the tab back in non-detacher role in case no detached tab are connected
uiTimeoutRef.current = setTimeout(
() => {
setRole(null)
},
isLinking ? LINKING_TIMEOUT : RELINK_TIMEOUT
)
}
}, [role, isLinking, isLinked, setRole])
useEffect(() => {
if (debugPdfDetach) {
console.log('Effect', { isLinking })
}
}, [isLinking])
const previousRole = usePreviousValue(role)
useEffect(() => {
if (previousRole && !role) {
eventTracking.sendMB('project-layout-reattached')
}
}, [previousRole, role])
const reattach = useCallback(() => {
broadcastEvent('reattach')
setRole(null)
setIsLinked(false)
}, [setRole, setIsLinked, broadcastEvent])
const detach = useCallback(() => {
setIsRedundant(false)
setRole('detacher')
setIsLinking(true)
window.open(buildUrlWithDetachRole('detached').toString(), '_blank')
}, [setRole, setIsLinking])
const handleEventForDetacherFromDetacher = useCallback(() => {
if (debugPdfDetach) {
console.log(
'Duplicate detacher detected, turning into a regular editor again'
)
}
setIsRedundant(true)
setIsLinked(false)
setRole(null)
}, [setRole, setIsLinked])
const handleEventForDetacherFromDetached = useCallback(
message => {
switch (message.event) {
case 'connected':
broadcastEvent('up')
setIsLinked(true)
break
case 'up':
setIsLinked(true)
break
case 'closed':
setIsLinked(false)
break
}
},
[setIsLinked, broadcastEvent]
)
const handleEventForDetachedFromDetacher = useCallback(
message => {
switch (message.event) {
case 'connected':
broadcastEvent('up')
setIsLinked(true)
break
case 'up':
setIsLinked(true)
break
case 'closed':
setIsLinked(false)
break
case 'reattach':
setIsLinked(false) // set as unlinked, in case closing is not allowed
window.close()
break
}
},
[setIsLinked, broadcastEvent]
)
const handleEventForDetachedFromDetached = useCallback(
message => {
switch (message.event) {
case 'closed':
broadcastEvent('up')
break
}
},
[broadcastEvent]
)
const handleEvent = useCallback(
message => {
if (role === 'detacher') {
if (message.role === 'detacher') {
handleEventForDetacherFromDetacher()
} else if (message.role === 'detached') {
handleEventForDetacherFromDetached(message)
}
} else if (role === 'detached') {
if (message.role === 'detacher') {
handleEventForDetachedFromDetacher(message)
} else if (message.role === 'detached') {
handleEventForDetachedFromDetached(message)
}
}
},
[
role,
handleEventForDetacherFromDetacher,
handleEventForDetacherFromDetached,
handleEventForDetachedFromDetacher,
handleEventForDetachedFromDetached,
]
)
useEffect(() => {
addEventHandler(handleEvent)
return () => deleteEventHandler(handleEvent)
}, [addEventHandler, deleteEventHandler, handleEvent])
return {
reattach,
detach,
isLinked,
isLinking,
role,
isRedundant,
}
}