feat(desktop): adiciona hub de chats para multiplas sessoes
- Cria ChatSessionList, ChatSessionItem e ChatHubWidget no desktop - Adiciona comandos Rust para gerenciar hub window - Quando ha multiplas sessoes, abre hub ao inves de janela individual - Hub lista todas as sessoes ativas com badge de nao lidos - Clicar em sessao abre/foca janela de chat especifica - Menu do tray abre hub quando ha multiplas sessoes 🤖 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
95ab1b5f0c
commit
29fbbfaa26
6 changed files with 560 additions and 38 deletions
|
|
@ -1000,37 +1000,58 @@ async fn process_chat_update(
|
|||
}
|
||||
}
|
||||
|
||||
// Fallback: se nao conseguimos detectar delta, pega a sessao com mais unread e mais recente.
|
||||
let session_to_show = if best_delta > 0 {
|
||||
best_session
|
||||
} else {
|
||||
current_sessions.iter().max_by(|a, b| {
|
||||
a.unread_count
|
||||
.cmp(&b.unread_count)
|
||||
.then_with(|| a.last_activity_at.cmp(&b.last_activity_at))
|
||||
})
|
||||
};
|
||||
|
||||
// Mostrar janela de chat (se nao existe, cria minimizada; se existe, apenas mostra)
|
||||
if let Some(session) = session_to_show {
|
||||
let label = format!("chat-{}", session.ticket_id);
|
||||
if let Some(window) = app.get_webview_window(&label) {
|
||||
// Janela ja existe - apenas mostrar (NAO minimizar se estiver expandida)
|
||||
// Isso permite que o usuario mantenha o chat aberto enquanto recebe mensagens
|
||||
let _ = window.show();
|
||||
// Verificar se esta expandida (altura > 100px significa expandido)
|
||||
// Se estiver expandida, NAO minimizar - usuario esta usando o chat
|
||||
if let Ok(size) = window.inner_size() {
|
||||
let is_expanded = size.height > 100;
|
||||
if !is_expanded {
|
||||
// Janela esta minimizada, manter minimizada
|
||||
let _ = set_chat_minimized(app, &session.ticket_id, true);
|
||||
}
|
||||
// Se esta expandida, nao faz nada - deixa o usuario continuar usando
|
||||
}
|
||||
// Se ha multiplas sessoes ativas, usar o hub; senao, abrir janela do chat individual
|
||||
if current_sessions.len() > 1 {
|
||||
// Multiplas sessoes - usar hub window
|
||||
if app.get_webview_window(HUB_WINDOW_LABEL).is_none() {
|
||||
// Hub nao existe - criar minimizado
|
||||
let _ = open_hub_window(app);
|
||||
} else {
|
||||
// Criar nova janela ja minimizada (menos intrusivo)
|
||||
let _ = open_chat_window(app, &session.ticket_id, session.ticket_ref);
|
||||
// Hub ja existe - verificar se esta minimizado
|
||||
if let Some(hub) = app.get_webview_window(HUB_WINDOW_LABEL) {
|
||||
let _ = hub.show();
|
||||
if let Ok(size) = hub.inner_size() {
|
||||
if size.height < 100 {
|
||||
// Esta minimizado, manter assim
|
||||
let _ = set_hub_minimized(app, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Uma sessao - abrir janela individual
|
||||
// Fallback: se nao conseguimos detectar delta, pega a sessao com mais unread e mais recente.
|
||||
let session_to_show = if best_delta > 0 {
|
||||
best_session
|
||||
} else {
|
||||
current_sessions.iter().max_by(|a, b| {
|
||||
a.unread_count
|
||||
.cmp(&b.unread_count)
|
||||
.then_with(|| a.last_activity_at.cmp(&b.last_activity_at))
|
||||
})
|
||||
};
|
||||
|
||||
// Mostrar janela de chat (se nao existe, cria minimizada; se existe, apenas mostra)
|
||||
if let Some(session) = session_to_show {
|
||||
let label = format!("chat-{}", session.ticket_id);
|
||||
if let Some(window) = app.get_webview_window(&label) {
|
||||
// Janela ja existe - apenas mostrar (NAO minimizar se estiver expandida)
|
||||
// Isso permite que o usuario mantenha o chat aberto enquanto recebe mensagens
|
||||
let _ = window.show();
|
||||
// Verificar se esta expandida (altura > 100px significa expandido)
|
||||
// Se estiver expandida, NAO minimizar - usuario esta usando o chat
|
||||
if let Ok(size) = window.inner_size() {
|
||||
let is_expanded = size.height > 100;
|
||||
if !is_expanded {
|
||||
// Janela esta minimizada, manter minimizada
|
||||
let _ = set_chat_minimized(app, &session.ticket_id, true);
|
||||
}
|
||||
// Se esta expandida, nao faz nada - deixa o usuario continuar usando
|
||||
}
|
||||
} else {
|
||||
// Criar nova janela ja minimizada (menos intrusivo)
|
||||
let _ = open_chat_window(app, &session.ticket_id, session.ticket_ref);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1201,3 +1222,85 @@ pub fn set_chat_minimized(app: &tauri::AppHandle, ticket_id: &str, minimized: bo
|
|||
crate::log_info!("Chat {} -> minimized={}", ticket_id, minimized);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// HUB WINDOW MANAGEMENT (Lista de todas as sessoes)
|
||||
// ============================================================================
|
||||
|
||||
const HUB_WINDOW_LABEL: &str = "chat-hub";
|
||||
|
||||
pub fn open_hub_window(app: &tauri::AppHandle) -> Result<(), String> {
|
||||
open_hub_window_with_state(app, true) // Por padrao abre minimizada
|
||||
}
|
||||
|
||||
fn open_hub_window_with_state(app: &tauri::AppHandle, start_minimized: bool) -> Result<(), String> {
|
||||
// Verificar se ja existe
|
||||
if let Some(window) = app.get_webview_window(HUB_WINDOW_LABEL) {
|
||||
window.show().map_err(|e| e.to_string())?;
|
||||
window.set_focus().map_err(|e| e.to_string())?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Dimensoes baseadas no estado inicial
|
||||
let (width, height) = if start_minimized {
|
||||
(200.0, 52.0) // Tamanho minimizado (chip)
|
||||
} else {
|
||||
(380.0, 480.0) // Tamanho expandido (lista)
|
||||
};
|
||||
|
||||
// Posicionar no canto inferior direito
|
||||
let (x, y) = resolve_chat_window_position(app, None, width, height);
|
||||
|
||||
// URL para modo hub
|
||||
let url_path = "index.html?view=chat&hub=true";
|
||||
|
||||
WebviewWindowBuilder::new(
|
||||
app,
|
||||
HUB_WINDOW_LABEL,
|
||||
WebviewUrl::App(url_path.into()),
|
||||
)
|
||||
.title("Chats de Suporte")
|
||||
.inner_size(width, height)
|
||||
.min_inner_size(200.0, 52.0)
|
||||
.position(x, y)
|
||||
.decorations(false)
|
||||
.transparent(true)
|
||||
.shadow(false)
|
||||
.always_on_top(true)
|
||||
.skip_taskbar(true)
|
||||
.focused(true)
|
||||
.visible(true)
|
||||
.build()
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
// Reaplica layout/posicao
|
||||
let _ = set_hub_minimized(app, start_minimized);
|
||||
|
||||
crate::log_info!("Hub window aberta (minimizada={})", start_minimized);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn close_hub_window(app: &tauri::AppHandle) -> Result<(), String> {
|
||||
if let Some(window) = app.get_webview_window(HUB_WINDOW_LABEL) {
|
||||
window.close().map_err(|e| e.to_string())?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_hub_minimized(app: &tauri::AppHandle, minimized: bool) -> Result<(), String> {
|
||||
let window = app.get_webview_window(HUB_WINDOW_LABEL).ok_or("Hub window não encontrada")?;
|
||||
|
||||
let (width, height) = if minimized {
|
||||
(200.0, 52.0) // Chip minimizado
|
||||
} else {
|
||||
(380.0, 480.0) // Lista expandida
|
||||
};
|
||||
|
||||
let (x, y) = resolve_chat_window_position(app, Some(&window), width, height);
|
||||
|
||||
window.set_size(tauri::LogicalSize::new(width, height)).map_err(|e| e.to_string())?;
|
||||
window.set_position(tauri::LogicalPosition::new(x, y)).map_err(|e| e.to_string())?;
|
||||
|
||||
crate::log_info!("Hub -> minimized={}", minimized);
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue