use-status-favicon.ts 2.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576
  1. import { useDetachCompileContext as useCompileContext } from '@/shared/context/detach-compile-context'
  2. import { useEffect, useState } from 'react'
  3. import usePreviousValue from '@/shared/hooks/use-previous-value'
  4. import getMeta from '@/utils/meta'
  5. const RESET_AFTER_MS = 5_000
  6. const COMPILE_ICONS = {
  7. ERROR: 'favicon-error.svg',
  8. COMPILING: 'favicon-compiling.svg',
  9. COMPILED: 'favicon-compiled.svg',
  10. UNCOMPILED: 'favicon.svg',
  11. } as const
  12. type CompileStatus = keyof typeof COMPILE_ICONS
  13. const useCompileStatus = (): CompileStatus => {
  14. const compileContext = useCompileContext()
  15. if (compileContext.uncompiled) return 'UNCOMPILED'
  16. if (compileContext.compiling) return 'COMPILING'
  17. if (compileContext.error) return 'ERROR'
  18. return 'COMPILED'
  19. }
  20. const removeFavicon = () => {
  21. const existingFavicons = document.head.querySelectorAll(
  22. "link[rel='icon']"
  23. ) as NodeListOf<HTMLLinkElement>
  24. existingFavicons.forEach(favicon => {
  25. if (favicon.href.endsWith('.svg')) favicon.parentNode?.removeChild(favicon)
  26. })
  27. }
  28. const updateFavicon = (status: CompileStatus = 'UNCOMPILED') => {
  29. removeFavicon()
  30. const linkElement = document.createElement('link')
  31. linkElement.rel = 'icon'
  32. linkElement.href = getMeta('ol-baseAssetPath') + COMPILE_ICONS[status]
  33. linkElement.type = 'image/svg+xml'
  34. linkElement.setAttribute('data-compile-status', 'true')
  35. document.head.appendChild(linkElement)
  36. }
  37. const isActive = () => !document.hidden
  38. const useIsWindowActive = () => {
  39. const [isWindowActive, setIsWindowActive] = useState(isActive())
  40. useEffect(() => {
  41. const handleVisibilityChange = () => setIsWindowActive(isActive())
  42. document.addEventListener('visibilitychange', handleVisibilityChange)
  43. return () => {
  44. document.removeEventListener('visibilitychange', handleVisibilityChange)
  45. }
  46. }, [])
  47. return isWindowActive
  48. }
  49. export const useStatusFavicon = () => {
  50. const compileStatus = useCompileStatus()
  51. const previousCompileStatus = usePreviousValue(compileStatus)
  52. const isWindowActive = useIsWindowActive()
  53. useEffect(() => {
  54. if (previousCompileStatus !== compileStatus) {
  55. return updateFavicon(compileStatus)
  56. }
  57. if (
  58. isWindowActive &&
  59. (compileStatus === 'COMPILED' || compileStatus === 'ERROR')
  60. ) {
  61. const timeout = setTimeout(updateFavicon, RESET_AFTER_MS)
  62. return () => clearTimeout(timeout)
  63. }
  64. }, [compileStatus, isWindowActive, previousCompileStatus])
  65. }