use-editing-session-heartbeat.ts 2.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667
  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 moment from 'moment'
  7. import { useCallback, useState } from 'react'
  8. import useEventListener from '@/shared/hooks/use-event-listener'
  9. import useDomEventListener from '@/shared/hooks/use-dom-event-listener'
  10. function createEditingSessionHeartbeatData(editorType: EditorType) {
  11. return {
  12. editorType,
  13. }
  14. }
  15. function sendEditingSessionHeartbeat(
  16. projectId: string,
  17. segmentation: Record<string, unknown>
  18. ) {
  19. putJSON(`/editingSession/${projectId}`, {
  20. body: { segmentation },
  21. }).catch(debugConsole.error)
  22. }
  23. export function useEditingSessionHeartbeat() {
  24. const { projectId } = useIdeReactContext()
  25. const { getEditorType } = useEditorManagerContext()
  26. // Keep track of how many heartbeats we've sent so that we can calculate how
  27. // long wait until the next one
  28. const [heartbeatsSent, setHeartbeatsSent] = useState(0)
  29. const [nextHeartbeatAt, setNextHeartbeatAt] = useState(() => new Date())
  30. const editingSessionHeartbeat = useCallback(() => {
  31. debugConsole.log('[Event] heartbeat trigger')
  32. const editorType = getEditorType()
  33. if (editorType === null) return
  34. // If the next heartbeat is in the future, stop
  35. if (nextHeartbeatAt > new Date()) return
  36. const segmentation = createEditingSessionHeartbeatData(editorType)
  37. debugConsole.log('[Event] send heartbeat request', segmentation)
  38. sendEditingSessionHeartbeat(projectId, segmentation)
  39. setHeartbeatsSent(heartbeatsSent => heartbeatsSent + 1)
  40. // Send two first heartbeats at 0 and 30s then increase the backoff time
  41. // 1min per call until we reach 5 min
  42. const backoffSecs =
  43. heartbeatsSent <= 2
  44. ? 30
  45. : heartbeatsSent <= 6
  46. ? (heartbeatsSent - 2) * 60
  47. : 300
  48. setNextHeartbeatAt(moment().add(backoffSecs, 'seconds').toDate())
  49. }, [getEditorType, heartbeatsSent, nextHeartbeatAt, projectId])
  50. // Hook the heartbeat up to editor events
  51. useEventListener('cursor:editor:update', editingSessionHeartbeat)
  52. useEventListener('scroll:editor:update', editingSessionHeartbeat)
  53. useDomEventListener(document, 'click', editingSessionHeartbeat)
  54. }