connection-context.tsx 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. import {
  2. createContext,
  3. useContext,
  4. useEffect,
  5. useState,
  6. FC,
  7. useCallback,
  8. useMemo,
  9. } from 'react'
  10. import { ConnectionState } from '../connection/types/connection-state'
  11. import { ConnectionManager } from '@/features/ide-react/connection/connection-manager'
  12. import { Socket } from '@/features/ide-react/connection/types/socket'
  13. import { secondsUntil } from '@/features/ide-react/connection/utils'
  14. type ConnectionContextValue = {
  15. socket: Socket
  16. connectionState: ConnectionState
  17. isConnected: boolean
  18. isStillReconnecting: boolean
  19. secondsUntilReconnect: () => number
  20. tryReconnectNow: () => void
  21. registerUserActivity: () => void
  22. }
  23. const ConnectionContext = createContext<ConnectionContextValue | undefined>(
  24. undefined
  25. )
  26. export const ConnectionProvider: FC = ({ children }) => {
  27. const [connectionManager] = useState(() => new ConnectionManager())
  28. const [connectionState, setConnectionState] = useState(
  29. connectionManager.state
  30. )
  31. useEffect(() => {
  32. function handleStateChange(event: { state: ConnectionState }) {
  33. setConnectionState(event.state)
  34. }
  35. connectionManager.on('statechange', handleStateChange)
  36. return () => {
  37. connectionManager.off('statechange', handleStateChange)
  38. }
  39. }, [connectionManager])
  40. const isConnected = connectionState.readyState === WebSocket.OPEN
  41. const isStillReconnecting =
  42. connectionState.readyState === WebSocket.CONNECTING &&
  43. performance.now() - connectionState.lastConnectionAttempt > 1000
  44. const secondsUntilReconnect = useCallback(
  45. () => secondsUntil(connectionState.reconnectAt),
  46. [connectionState.reconnectAt]
  47. )
  48. const tryReconnectNow = useCallback(
  49. () => connectionManager.tryReconnectNow(),
  50. [connectionManager]
  51. )
  52. const registerUserActivity = useCallback(
  53. () => connectionManager.registerUserActivity(),
  54. [connectionManager]
  55. )
  56. const value = useMemo<ConnectionContextValue>(
  57. () => ({
  58. socket: connectionManager.socket,
  59. connectionState,
  60. isConnected,
  61. isStillReconnecting,
  62. secondsUntilReconnect,
  63. tryReconnectNow,
  64. registerUserActivity,
  65. }),
  66. [
  67. connectionManager.socket,
  68. connectionState,
  69. isConnected,
  70. isStillReconnecting,
  71. registerUserActivity,
  72. secondsUntilReconnect,
  73. tryReconnectNow,
  74. ]
  75. )
  76. return (
  77. <ConnectionContext.Provider value={value}>
  78. {children}
  79. </ConnectionContext.Provider>
  80. )
  81. }
  82. export function useConnectionContext(): ConnectionContextValue {
  83. const context = useContext(ConnectionContext)
  84. if (!context) {
  85. throw new Error(
  86. 'useConnectionContext is only available inside ConnectionProvider'
  87. )
  88. }
  89. return context
  90. }