Skip to content

useTypingIndicator Composable

File: src/composables/useTypingIndicator.ts

Overview

Exports

  • useTypingIndicator - function export

Functions

startTyping()

No description available.

Parameters: None

Returns: Unknown

typescript
/**
 * useTypingIndicator - Composable for typing indicators
 * 
 * Provides a simple, DRY way to:
 * - Track typing in channels, threads, or conversations
 * - Display typing indicators
 * - Automatically handle cleanup
 */

import { ref, onUnmounted, watchEffect } from 'vue'
import { typingIndicatorService, type TypingContext, type TypingUser } from '@/services/TypingIndicatorService'
import { useAuthStore } from '@/stores/auth'

/**
 * Composable for tracking and displaying typing indicators
 */
export function useTypingIndicator(context: TypingContext | null | (() => TypingContext | null)) {
  const typingUsers = ref<TypingUser[]>([])
  const authStore = useAuthStore()
  let unsubscribe: (() => void) | null = null
  let currentSubscribedKey = ''
  let isSubscribing = false

  // Handle both computed refs and direct values
  const getContext = () => {
    if (typeof context === 'function') {
      return context()
    }
    return context
  }

  // Get serialized context key for comparison
  const getContextKey = (ctx: TypingContext | null): string => {
    if (!ctx) return 'null'
    if ('channelId' in ctx) return `channel:${ctx.channelId}`
    if ('threadId' in ctx) return `thread:${ctx.threadId}`
    if ('conversationId' in ctx) return `conversation:${ctx.conversationId}`
    return 'unknown'
  }

  // Subscribe to typing updates for a given context
  const subscribeToContext = async (ctx: TypingContext | null, key: string): Promise<void> => {
    // Skip if already subscribed to this exact context
    if (key === currentSubscribedKey && unsubscribe) {
      return
    }
    
    // Prevent concurrent subscription attempts
    if (isSubscribing) {
      return
    }
    
    // Cleanup previous subscription
    if (unsubscribe) {
      unsubscribe()
      unsubscribe = null
      currentSubscribedKey = ''
    }
    
    if (!ctx) {
      typingUsers.value = []
      currentSubscribedKey = ''
      return
    }
    
    // Ensure we have auth before subscribing
    if (!authStore.session?.user?.id) {
      return
    }
    
    isSubscribing = true
    
    try {
      await typingIndicatorService.initialize()
      
      unsubscribe = typingIndicatorService.subscribeToTyping(ctx, (users) => {
        typingUsers.value = users
      })
      currentSubscribedKey = key
    } catch {
      // Silently fail - typing indicators are non-critical
    } finally {
      isSubscribing = false
    }
  }

  // Use watchEffect to automatically track reactive dependencies
  watchEffect(async () => {
    const session = authStore.session
    const userId = session?.user?.id
    const ctx = getContext()
    const key = getContextKey(ctx)
    
    if (ctx && key !== 'null' && userId) {
      await subscribeToContext(ctx, key)
    } else if (!ctx || key === 'null') {
      if (unsubscribe) {
        unsubscribe()
        unsubscribe = null
      }
      typingUsers.value = []
      currentSubscribedKey = ''
    }
  })

  // Cleanup on unmount
  onUnmounted(() => {
    if (unsubscribe) {
      unsubscribe()
      unsubscribe = null
    }
    currentSubscribedKey = ''
    isSubscribing = false
  })

  /**
   * Start tracking typing (call when user types)
   */
  const startTyping = async () =>

stopTyping()

No description available.

Parameters: None

Returns: Unknown

typescript
/**
   * Stop tracking typing (call when user sends message or stops typing)
   */
  const stopTyping = async () =>

formatTypingText(users: TypingUser[], getUserDisplayName: (userId: string)

No description available.

Parameters:

  • users: TypingUser[]
  • getUserDisplayName: (userId: string

Returns: Unknown

typescript
/**
   * Format typing indicator text
   */
  const formatTypingText = (users: TypingUser[], getUserDisplayName: (userId: string) =>

Source Code Insights

File Size: 4492 characters Lines of Code: 158 Imports: 3

Usage Example

typescript
import { useTypingIndicator } from '@/composables/useTypingIndicator'

// Example usage
startTyping()

This documentation was automatically generated from the source code.

Released under the AGPL-3.0 License.