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' import { debugConsole } from '@/utils/debugging' 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>() useEffect(() => { if (debugPdfDetach) { debugConsole.warn('Effect', { isLinked }) } setIsLinking(false) }, [isLinked, setIsLinking]) useEffect(() => { if (debugPdfDetach) { debugConsole.warn('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) { debugConsole.warn('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) { debugConsole.warn( '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, } }