feat: melhorias visuais na janela de chat do desktop
- Desabilita sombra da janela para transparência funcionar corretamente - Adiciona badge de mensagens não lidas no chip minimizado - Ajusta tamanho da janela minimizada para acomodar badge e texto offline - Mostra chat minimizado com badge quando há novas mensagens (menos intrusivo) - Adiciona listener para atualização de unread count em tempo real 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
24dee5d5eb
commit
d2b8c27206
2 changed files with 44 additions and 8 deletions
|
|
@ -802,7 +802,7 @@ async fn process_chat_update(
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Notificar novas mensagens (sem abrir janela automaticamente - so na nova sessao)
|
// Notificar novas mensagens - mostrar chat minimizado com badge
|
||||||
if new_messages && total_unread > 0 {
|
if new_messages && total_unread > 0 {
|
||||||
let new_count = total_unread - prev_unread;
|
let new_count = total_unread - prev_unread;
|
||||||
|
|
||||||
|
|
@ -817,7 +817,22 @@ async fn process_chat_update(
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Notificacao nativa (sem abrir janela - usuario pode clicar para abrir)
|
// Mostrar janela de chat minimizada (menos intrusivo que abrir completo)
|
||||||
|
if let Some(session) = current_sessions.first() {
|
||||||
|
// Abrir janela se nao existir
|
||||||
|
let label = format!("chat-{}", session.ticket_id);
|
||||||
|
if app.get_webview_window(&label).is_none() {
|
||||||
|
let _ = open_chat_window(app, &session.ticket_id);
|
||||||
|
// Minimizar imediatamente apos abrir
|
||||||
|
let _ = set_chat_minimized(app, &session.ticket_id, true);
|
||||||
|
}
|
||||||
|
// Se ja existe, apenas garantir que esta visivel (pode estar escondida)
|
||||||
|
else if let Some(window) = app.get_webview_window(&label) {
|
||||||
|
let _ = window.show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notificacao nativa
|
||||||
let notification_title = "Nova mensagem de suporte";
|
let notification_title = "Nova mensagem de suporte";
|
||||||
let notification_body = if new_count == 1 {
|
let notification_body = if new_count == 1 {
|
||||||
"Voce recebeu 1 nova mensagem no chat".to_string()
|
"Voce recebeu 1 nova mensagem no chat".to_string()
|
||||||
|
|
@ -875,10 +890,11 @@ fn open_chat_window_internal(app: &tauri::AppHandle, ticket_id: &str) -> Result<
|
||||||
)
|
)
|
||||||
.title("Chat de Suporte")
|
.title("Chat de Suporte")
|
||||||
.inner_size(380.0, 520.0)
|
.inner_size(380.0, 520.0)
|
||||||
.min_inner_size(168.0, 36.0) // Tamanho minimo para modo minimizado
|
.min_inner_size(200.0, 44.0) // Tamanho minimo para modo minimizado com badge
|
||||||
.position(x, y)
|
.position(x, y)
|
||||||
.decorations(false) // Sem decoracoes nativas - usa header customizado
|
.decorations(false) // Sem decoracoes nativas - usa header customizado
|
||||||
.transparent(true) // Permite fundo transparente
|
.transparent(true) // Permite fundo transparente
|
||||||
|
.shadow(false) // Desabilitar sombra para transparencia funcionar corretamente
|
||||||
.always_on_top(true)
|
.always_on_top(true)
|
||||||
.skip_taskbar(true)
|
.skip_taskbar(true)
|
||||||
.focused(true)
|
.focused(true)
|
||||||
|
|
@ -915,9 +931,9 @@ pub fn set_chat_minimized(app: &tauri::AppHandle, ticket_id: &str, minimized: bo
|
||||||
let label = format!("chat-{}", ticket_id);
|
let label = format!("chat-{}", ticket_id);
|
||||||
let window = app.get_webview_window(&label).ok_or("Janela nao encontrada")?;
|
let window = app.get_webview_window(&label).ok_or("Janela nao encontrada")?;
|
||||||
|
|
||||||
// Tamanhos - chip minimizado precisa ser exato para transparencia funcionar
|
// Tamanhos - chip minimizado com margem extra para badge e modo offline
|
||||||
let (width, height) = if minimized {
|
let (width, height) = if minimized {
|
||||||
(168.0, 36.0) // Tamanho exato do chip rounded-full
|
(200.0, 44.0) // Tamanho com folga para badge e texto "Offline"
|
||||||
} else {
|
} else {
|
||||||
(380.0, 520.0) // Tamanho expandido
|
(380.0, 520.0) // Tamanho expandido
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,7 @@ export function ChatWidget({ ticketId }: ChatWidgetProps) {
|
||||||
const [hasSession, setHasSession] = useState(false)
|
const [hasSession, setHasSession] = useState(false)
|
||||||
const [pendingAttachments, setPendingAttachments] = useState<UploadedAttachment[]>([])
|
const [pendingAttachments, setPendingAttachments] = useState<UploadedAttachment[]>([])
|
||||||
const [isMinimized, setIsMinimized] = useState(false)
|
const [isMinimized, setIsMinimized] = useState(false)
|
||||||
|
const [unreadCount, setUnreadCount] = useState(0)
|
||||||
|
|
||||||
const messagesEndRef = useRef<HTMLDivElement>(null)
|
const messagesEndRef = useRef<HTMLDivElement>(null)
|
||||||
const lastFetchRef = useRef<number>(0)
|
const lastFetchRef = useRef<number>(0)
|
||||||
|
|
@ -186,7 +187,7 @@ export function ChatWidget({ ticketId }: ChatWidgetProps) {
|
||||||
init()
|
init()
|
||||||
|
|
||||||
// Listener para eventos de nova mensagem do Tauri
|
// Listener para eventos de nova mensagem do Tauri
|
||||||
const unlistenPromise = listen<{ ticketId: string; message: ChatMessage }>(
|
const unlistenNewMessage = listen<{ ticketId: string; message: ChatMessage }>(
|
||||||
"raven://chat/new-message",
|
"raven://chat/new-message",
|
||||||
(event) => {
|
(event) => {
|
||||||
if (event.payload.ticketId === ticketId) {
|
if (event.payload.ticketId === ticketId) {
|
||||||
|
|
@ -202,12 +203,25 @@ export function ChatWidget({ ticketId }: ChatWidgetProps) {
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Listener para atualização de mensagens não lidas
|
||||||
|
const unlistenUnread = listen<{ totalUnread: number; sessions: Array<{ ticketId: string; unreadCount: number }> }>(
|
||||||
|
"raven://chat/unread-update",
|
||||||
|
(event) => {
|
||||||
|
// Encontrar o unread count para este ticket
|
||||||
|
const session = event.payload.sessions?.find(s => s.ticketId === ticketId)
|
||||||
|
if (session) {
|
||||||
|
setUnreadCount(session.unreadCount ?? 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
mounted = false
|
mounted = false
|
||||||
if (pollIntervalRef.current) {
|
if (pollIntervalRef.current) {
|
||||||
clearInterval(pollIntervalRef.current)
|
clearInterval(pollIntervalRef.current)
|
||||||
}
|
}
|
||||||
unlistenPromise.then(unlisten => unlisten())
|
unlistenNewMessage.then(unlisten => unlisten())
|
||||||
|
unlistenUnread.then(unlisten => unlisten())
|
||||||
}
|
}
|
||||||
}, [ticketId, loadConfig, fetchMessages, fetchSessionInfo])
|
}, [ticketId, loadConfig, fetchMessages, fetchSessionInfo])
|
||||||
|
|
||||||
|
|
@ -378,7 +392,7 @@ export function ChatWidget({ ticketId }: ChatWidgetProps) {
|
||||||
<div className="flex h-full w-full items-end justify-end bg-transparent">
|
<div className="flex h-full w-full items-end justify-end bg-transparent">
|
||||||
<button
|
<button
|
||||||
onClick={handleExpand}
|
onClick={handleExpand}
|
||||||
className="flex items-center gap-2 rounded-full bg-black px-4 py-2 text-white shadow-lg hover:bg-black/90"
|
className="relative flex items-center gap-2 rounded-full bg-black px-4 py-2 text-white shadow-lg hover:bg-black/90"
|
||||||
>
|
>
|
||||||
<MessageCircle className="size-4" />
|
<MessageCircle className="size-4" />
|
||||||
<span className="text-sm font-medium">
|
<span className="text-sm font-medium">
|
||||||
|
|
@ -386,6 +400,12 @@ export function ChatWidget({ ticketId }: ChatWidgetProps) {
|
||||||
</span>
|
</span>
|
||||||
<span className="size-2 rounded-full bg-emerald-400" />
|
<span className="size-2 rounded-full bg-emerald-400" />
|
||||||
<ChevronUp className="size-4" />
|
<ChevronUp className="size-4" />
|
||||||
|
{/* Badge de mensagens não lidas */}
|
||||||
|
{unreadCount > 0 && (
|
||||||
|
<span className="absolute -right-1 -top-1 flex size-5 items-center justify-center rounded-full bg-red-500 text-xs font-bold">
|
||||||
|
{unreadCount > 9 ? "9+" : unreadCount}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue