use-editing-session-heartbeat.ts 2.8 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  1. import { useIdeReactContext } from '@/features/ide-react/context/ide-react-context'
  2. import { useEditorManagerContext } from '@/features/ide-react/context/editor-manager-context'
  3. import { EditorType } from '@/features/ide-react/editor/types/editor-type'
  4. import { putJSON } from '@/infrastructure/fetch-json'
  5. import { debugConsole } from '@/utils/debugging'
  6. import { useCallback, useEffect, useRef } from 'react'
  7. import useEventListener from '@/shared/hooks/use-event-listener'
  8. import useDomEventListener from '@/shared/hooks/use-dom-event-listener'
  9. import { useIsNewEditorEnabled } from '@/features/ide-redesign/utils/new-editor-utils'
  10. function createEditingSessionHeartbeatData(
  11. editorType: EditorType,
  12. newEditor: boolean
  13. ) {
  14. return {
  15. editorType,
  16. editorRedesign: newEditor,
  17. }
  18. }
  19. function sendEditingSessionHeartbeat(
  20. projectId: string,
  21. segmentation: Record<string, unknown>
  22. ) {
  23. putJSON(`/editingSession/${projectId}`, {
  24. body: { segmentation },
  25. }).catch(debugConsole.error)
  26. }
  27. export function useEditingSessionHeartbeat() {
  28. const { projectId } = useIdeReactContext()
  29. const { getEditorType } = useEditorManagerContext()
  30. const newEditor = useIsNewEditorEnabled()
  31. // Keep track of how many heartbeats we've sent so that we can calculate how
  32. // long to wait until the next one
  33. const heartBeatsSentRef = useRef(0)
  34. const heartBeatSentRecentlyRef = useRef(false)
  35. const heartBeatResetTimerRef = useRef<number>()
  36. useEffect(() => {
  37. return () => {
  38. window.clearTimeout(heartBeatResetTimerRef.current)
  39. }
  40. }, [])
  41. const editingSessionHeartbeat = useCallback(() => {
  42. debugConsole.log('[Event] heartbeat trigger')
  43. const editorType = getEditorType()
  44. if (editorType === null) return
  45. // Heartbeat already sent recently
  46. if (heartBeatSentRecentlyRef.current) return
  47. heartBeatSentRecentlyRef.current = true
  48. const segmentation = createEditingSessionHeartbeatData(
  49. editorType,
  50. newEditor
  51. )
  52. debugConsole.log('[Event] send heartbeat request', segmentation)
  53. sendEditingSessionHeartbeat(projectId, segmentation)
  54. const heartbeatsSent = heartBeatsSentRef.current
  55. heartBeatsSentRef.current++
  56. // Send two first heartbeats at 0 and 30s then increase the backoff time
  57. // 1min per call until we reach 5 min
  58. const backoffSecs =
  59. heartbeatsSent <= 2
  60. ? 30
  61. : heartbeatsSent <= 6
  62. ? (heartbeatsSent - 2) * 60
  63. : 300
  64. heartBeatResetTimerRef.current = window.setTimeout(() => {
  65. heartBeatSentRecentlyRef.current = false
  66. }, backoffSecs * 1000)
  67. }, [getEditorType, projectId, newEditor])
  68. // Hook the heartbeat up to editor events
  69. useEventListener('cursor:editor:update', editingSessionHeartbeat)
  70. useEventListener('scroll:editor:update', editingSessionHeartbeat)
  71. useDomEventListener(document, 'click', editingSessionHeartbeat)
  72. }