Files
clube67_newwhats.local/clube67/newwhats.local/frontend/hooks/inbox/useChatWindow.ts
T

145 lines
5.0 KiB
TypeScript

/**
* useChatWindow — gerencia o estado e handlers da área de chat ativa.
*
* Extrai de inbox/index.tsx:
* - scroll + carregamento de histórico
* - indicador de digitação
* - reply / reaction
* - envio de mídia e áudio
* - sync de avatar
* - abertura de perfil / menu de chat
*/
import { useState, useRef, useCallback } from 'react'
import { mediaApi } from '../../services/chatApiService'
import type { NewWhatsChat } from '../../types/inboxTypes'
import type { Message } from '../../types/inboxTypes'
interface UseChatWindowOptions {
selectedChat: NewWhatsChat | null
activeInstanceId: string | null
onLoadMoreHistory: () => Promise<{ hasMore: boolean }>
}
export function useChatWindow({
selectedChat,
activeInstanceId,
onLoadMoreHistory,
}: UseChatWindowOptions) {
// ── Refs ────────────────────────────────────────────────────────────────────
const scrollRef = useRef<HTMLDivElement>(null)
const isTypingRef = useRef(false)
const typingTimeout = useRef<NodeJS.Timeout | null>(null)
// ── Estado ──────────────────────────────────────────────────────────────────
const [isTyping, setIsTyping] = useState(false)
const [replyingTo, setReplyingTo] = useState<Message | null>(null)
const [reactionPickerFor, setReactionPickerFor] = useState<string | null>(null)
const [hasMoreHistory, setHasMoreHistory] = useState(true)
const [loadingMore, setLoadingMore] = useState(false)
const [isProfileOpen, setIsProfileOpen] = useState(false)
const [isSyncingAvatar, setIsSyncingAvatar] = useState(false)
const [isChatMenuOpen, setIsChatMenuOpen] = useState(false)
// ── Handlers ────────────────────────────────────────────────────────────────
const handleTyping = useCallback(() => {
if (!selectedChat) return
if (!isTypingRef.current) {
isTypingRef.current = true
setIsTyping(true)
}
if (typingTimeout.current) clearTimeout(typingTimeout.current)
typingTimeout.current = setTimeout(() => {
isTypingRef.current = false
setIsTyping(false)
}, 3000)
}, [selectedChat])
const handleScroll = useCallback(async () => {
const el = scrollRef.current
if (!el || !hasMoreHistory || loadingMore) return
if (el.scrollTop < 100) {
setLoadingMore(true)
const { hasMore } = await onLoadMoreHistory()
setHasMoreHistory(hasMore)
setLoadingMore(false)
}
}, [hasMoreHistory, loadingMore, onLoadMoreHistory])
const handleSelectChat = useCallback(() => {
setHasMoreHistory(true)
}, [])
const handleReactToMessage = useCallback((_msg: Message, _emoji: string) => {
setReactionPickerFor(null)
// Será implementado via endpoint /api/instances/:id/react
}, [])
const handleSendMedia = useCallback(async (file: File, caption?: string) => {
if (!selectedChat || !activeInstanceId) return
try {
await mediaApi.sendMedia(activeInstanceId, selectedChat.remote_jid, file, caption || undefined, undefined)
} catch (err) {
console.error('Erro ao enviar mídia:', err)
}
}, [selectedChat, activeInstanceId])
const handleSendAudio = useCallback(async (blob: Blob) => {
if (!selectedChat || !activeInstanceId) return
try {
await mediaApi.sendAudio(activeInstanceId, selectedChat.remote_jid, blob)
} catch (err) {
console.error('Erro ao enviar áudio:', err)
}
}, [selectedChat, activeInstanceId])
const handleSyncAvatar = useCallback(async () => {
if (!selectedChat || !activeInstanceId) return
setIsSyncingAvatar(true)
try {
const API = process.env.NEXT_PUBLIC_API_URL ?? 'http://localhost:3003'
const token = localStorage.getItem('token')
await fetch(
`${API}/api/inbox/avatar/${encodeURIComponent(selectedChat.remote_jid)}?instance=${activeInstanceId}`,
{ headers: { Authorization: `Bearer ${token}` } }
)
} finally {
setIsSyncingAvatar(false)
}
}, [selectedChat, activeInstanceId])
const toggleReactionPicker = useCallback((id: string) => {
setReactionPickerFor(prev => prev === id ? null : id)
}, [])
return {
// Refs
scrollRef,
// Estado
isTyping,
replyingTo,
reactionPickerFor,
hasMoreHistory,
loadingMore,
isProfileOpen,
isSyncingAvatar,
isChatMenuOpen,
// Setters simples
setReplyingTo,
setIsProfileOpen,
setIsChatMenuOpen,
// Handlers
handleTyping,
handleScroll,
handleSelectChat,
handleReactToMessage,
handleSendMedia,
handleSendAudio,
handleSyncAvatar,
toggleReactionPicker,
}
}