connection-context.tsx 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  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. disconnect: () => void
  23. }
  24. const ConnectionContext = createContext<ConnectionContextValue | undefined>(
  25. undefined
  26. )
  27. export const ConnectionProvider: FC = ({ children }) => {
  28. const [connectionManager] = useState(() => new ConnectionManager())
  29. const [connectionState, setConnectionState] = useState(
  30. connectionManager.state
  31. )
  32. useEffect(() => {
  33. function handleStateChange(event: { state: ConnectionState }) {
  34. setConnectionState(event.state)
  35. }
  36. connectionManager.on('statechange', handleStateChange)
  37. return () => {
  38. connectionManager.off('statechange', handleStateChange)
  39. }
  40. }, [connectionManager])
  41. const isConnected = connectionState.readyState === WebSocket.OPEN
  42. const isStillReconnecting =
  43. connectionState.readyState === WebSocket.CONNECTING &&
  44. performance.now() - connectionState.lastConnectionAttempt > 1000
  45. const secondsUntilReconnect = useCallback(
  46. () => secondsUntil(connectionState.reconnectAt),
  47. [connectionState.reconnectAt]
  48. )
  49. const tryReconnectNow = useCallback(
  50. () => connectionManager.tryReconnectNow(),
  51. [connectionManager]
  52. )
  53. const registerUserActivity = useCallback(
  54. () => connectionManager.registerUserActivity(),
  55. [connectionManager]
  56. )
  57. const disconnect = useCallback(() => {
  58. connectionManager.disconnect()
  59. }, [connectionManager])
  60. const value = useMemo<ConnectionContextValue>(
  61. () => ({
  62. socket: connectionManager.socket,
  63. connectionState,
  64. isConnected,
  65. isStillReconnecting,
  66. secondsUntilReconnect,
  67. tryReconnectNow,
  68. registerUserActivity,
  69. disconnect,
  70. }),
  71. [
  72. connectionManager.socket,
  73. connectionState,
  74. isConnected,
  75. isStillReconnecting,
  76. registerUserActivity,
  77. secondsUntilReconnect,
  78. tryReconnectNow,
  79. disconnect,
  80. ]
  81. )
  82. return (
  83. <ConnectionContext.Provider value={value}>
  84. {children}
  85. </ConnectionContext.Provider>
  86. )
  87. }
  88. export function useConnectionContext(): ConnectionContextValue {
  89. const context = useContext(ConnectionContext)
  90. if (!context) {
  91. throw new Error(
  92. 'useConnectionContext is only available inside ConnectionProvider'
  93. )
  94. }
  95. return context
  96. }