📄 File detail

hooks/useNotifyAfterTimeout.ts

🧩 .ts📏 66 lines💾 2,471 bytes📝 text
← Back to All Files

🎯 Use case

This file lives under “hooks/”, which covers reusable UI or integration hooks. On the API surface it exposes DEFAULT_INTERACTION_THRESHOLD_MS and useNotifyAfterTimeout — mainly functions, hooks, or classes. Dependencies touch React UI. It composes internal code from bootstrap, ink, and services (relative imports).

Generated from folder role, exports, dependency roots, and inline comments — not hand-reviewed for every path.

🧠 Inline summary

import { useEffect } from 'react' import { getLastInteractionTime, updateLastInteractionTime, } from '../bootstrap/state.js'

📤 Exports (heuristic)

  • DEFAULT_INTERACTION_THRESHOLD_MS
  • useNotifyAfterTimeout

📚 External import roots

Package roots from from "…" (relative paths omitted).

  • react

🖥️ Source preview

import { useEffect } from 'react'
import {
  getLastInteractionTime,
  updateLastInteractionTime,
} from '../bootstrap/state.js'
import { useTerminalNotification } from '../ink/useTerminalNotification.js'
import { sendNotification } from '../services/notifier.js'
// The time threshold in milliseconds for considering an interaction "recent" (6 seconds)
export const DEFAULT_INTERACTION_THRESHOLD_MS = 6000

function getTimeSinceLastInteraction(): number {
  return Date.now() - getLastInteractionTime()
}

function hasRecentInteraction(threshold: number): boolean {
  return getTimeSinceLastInteraction() < threshold
}

function shouldNotify(threshold: number): boolean {
  return process.env.NODE_ENV !== 'test' && !hasRecentInteraction(threshold)
}

// NOTE: User interaction tracking is now done in App.tsx's processKeysInBatch
// function, which calls updateLastInteractionTime() when any input is received.
// This avoids having a separate stdin 'data' listener that would compete with
// the main 'readable' listener and cause dropped input characters.

/**
 * Hook that manages desktop notifications after a timeout period.
 *
 * Shows a notification in two cases:
 * 1. Immediately if the app has been idle for longer than the threshold
 * 2. After the specified timeout if the user doesn't interact within that time
 *
 * @param message - The notification message to display
 * @param timeout - The timeout in milliseconds (defaults to 6000ms)
 */
export function useNotifyAfterTimeout(
  message: string,
  notificationType: string,
): void {
  const terminal = useTerminalNotification()

  // Reset interaction time when hook is called to make sure that requests
  // that took a long time to complete don't pop up a notification right away.
  // Must be immediate because useEffect runs after Ink's render cycle has
  // already flushed; without it the timestamp stays stale and a premature
  // notification fires if the user is idle (no subsequent renders to flush).
  useEffect(() => {
    updateLastInteractionTime(true)
  }, [])

  useEffect(() => {
    let hasNotified = false
    const timer = setInterval(() => {
      if (shouldNotify(DEFAULT_INTERACTION_THRESHOLD_MS) && !hasNotified) {
        hasNotified = true
        clearInterval(timer)
        void sendNotification({ message, notificationType }, terminal)
      }
    }, DEFAULT_INTERACTION_THRESHOLD_MS)

    return () => clearInterval(timer)
  }, [message, notificationType, terminal])
}