mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2025-07-29 23:00:08 +02:00

* Import changes from Hackathon https://github.com/overleaf/internal/pull/24501 * Update compile status: allow errors * Update favicons. Use the ones from Figma * Optimize and reuse path from favicon.svg * Clear status favicon after 5s on active tab * Rename hook from useCompileNotification to useStatusFavicon * Add tests * Revert changes to favicon.svg * Query favicon on document.head GitOrigin-RevId: 3972b1981abaf6c80273e0ed5b1bc05eb51bd689
75 lines
2.3 KiB
TypeScript
75 lines
2.3 KiB
TypeScript
import { useDetachCompileContext as useCompileContext } from '@/shared/context/detach-compile-context'
|
|
import { useEffect, useState } from 'react'
|
|
import usePreviousValue from '@/shared/hooks/use-previous-value'
|
|
|
|
const RESET_AFTER_MS = 5_000
|
|
|
|
const COMPILE_ICONS = {
|
|
ERROR: '/favicon-error.svg',
|
|
COMPILING: '/favicon-compiling.svg',
|
|
COMPILED: '/favicon-compiled.svg',
|
|
UNCOMPILED: '/favicon.svg',
|
|
}
|
|
|
|
type CompileStatus = keyof typeof COMPILE_ICONS
|
|
|
|
const useCompileStatus = (): CompileStatus => {
|
|
const compileContext = useCompileContext()
|
|
if (compileContext.uncompiled) return 'UNCOMPILED'
|
|
if (compileContext.compiling) return 'COMPILING'
|
|
if (compileContext.error) return 'ERROR'
|
|
return 'COMPILED'
|
|
}
|
|
|
|
const removeFavicon = () => {
|
|
const existingFavicons = document.head.querySelectorAll(
|
|
"link[rel='icon']"
|
|
) as NodeListOf<HTMLLinkElement>
|
|
existingFavicons.forEach(favicon => {
|
|
if (favicon.href.endsWith('.svg')) favicon.parentNode?.removeChild(favicon)
|
|
})
|
|
}
|
|
|
|
const updateFavicon = (status: CompileStatus = 'UNCOMPILED') => {
|
|
removeFavicon()
|
|
const linkElement = document.createElement('link')
|
|
linkElement.rel = 'icon'
|
|
linkElement.href = COMPILE_ICONS[status]
|
|
linkElement.type = 'image/svg+xml'
|
|
linkElement.setAttribute('data-compile-status', 'true')
|
|
document.head.appendChild(linkElement)
|
|
}
|
|
|
|
const isActive = () => !document.hidden
|
|
|
|
const useIsWindowActive = () => {
|
|
const [isWindowActive, setIsWindowActive] = useState(isActive())
|
|
useEffect(() => {
|
|
const handleVisibilityChange = () => setIsWindowActive(isActive())
|
|
document.addEventListener('visibilitychange', handleVisibilityChange)
|
|
return () => {
|
|
document.removeEventListener('visibilitychange', handleVisibilityChange)
|
|
}
|
|
}, [])
|
|
return isWindowActive
|
|
}
|
|
|
|
export const useStatusFavicon = () => {
|
|
const compileStatus = useCompileStatus()
|
|
const previousCompileStatus = usePreviousValue(compileStatus)
|
|
const isWindowActive = useIsWindowActive()
|
|
|
|
useEffect(() => {
|
|
if (previousCompileStatus !== compileStatus) {
|
|
return updateFavicon(compileStatus)
|
|
}
|
|
|
|
if (
|
|
isWindowActive &&
|
|
(compileStatus === 'COMPILED' || compileStatus === 'ERROR')
|
|
) {
|
|
const timeout = setTimeout(updateFavicon, RESET_AFTER_MS)
|
|
return () => clearTimeout(timeout)
|
|
}
|
|
}, [compileStatus, isWindowActive, previousCompileStatus])
|
|
}
|