use std::sync::mpsc::Receiver; use std::time::Duration; use eframe::{ egui::{self, RichText, ScrollArea, Window}, emath::Align, epaint::Color32, }; use egui_toast::Toasts; use serde::Deserialize; use tungstenite::Message; pub struct App { rx: Receiver, history: Vec, autoscroll: bool, description: String, selected_channel: String, channels: Vec, description_shown: bool, } #[derive(Deserialize)] struct MessageLog { name: String, description: String, message: String, channel: String, } #[derive(Deserialize)] struct InteractionLog { name: String, command: String, arguments: String, channel: String, } #[derive(Deserialize)] #[serde(tag = "type")] enum Loggers { InteractionLog(InteractionLog), MessageLog(MessageLog), } impl App { pub fn new(rx: Receiver) -> Self { Self { rx, history: Vec::new(), autoscroll: false, description: "".to_string(), selected_channel: "".to_string(), channels: Vec::new(), description_shown: false, } } } impl eframe::App for App { fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) { let mut toasts = Toasts::new() .anchor((10.0, 10.0)) .direction(egui::Direction::TopDown) .align_to_end(false); let message = self.rx.try_recv(); if let Ok(message) = message { let text = message.to_string(); let json = serde_json::from_str(&text); if let Ok(json) = json { let json: Loggers = json; match json { Loggers::MessageLog(msg) => { self.history.push(msg); } Loggers::InteractionLog(msg) => { if self.selected_channel == msg.channel { toasts.info( format!( "User {} called command /{} with arguments {}", msg.name, msg.command, msg.arguments ), Duration::from_secs(5), ); } } } } else if let Err(json) = json { println!("{}", json); } } egui::CentralPanel::default().show(ctx, |ui| { Window::new("Description") .open(&mut self.description_shown) .collapsible(false) .show(ctx, |ui| { ScrollArea::vertical() .id_source("description_box") .show(ui, |ui| { ui.label(&self.description); }); }); ui.horizontal(|ui| { ui.checkbox(&mut self.autoscroll, "Autoscroll"); if ui.button("Reset").clicked() { self.history.clear(); self.channels.clear(); self.selected_channel.clear(); self.description.clear(); } if ui.button("Description").clicked() { self.description_shown = true; } }); ui.separator(); ui.horizontal(|ui| { ScrollArea::horizontal() .id_source("channel_bar") .show(ui, |ui| { for channel in &self.channels { if ui.button(channel).clicked() { self.selected_channel = channel.to_owned(); } } }); }); ui.separator(); ScrollArea::vertical() .id_source("messages_list") .show(ui, |ui| { for msg in &self.history { if !self.channels.contains(&msg.channel) { self.channels.push((&msg.channel).to_owned()); } if msg.channel == self.selected_channel { ui.horizontal(|ui| { ui.label(RichText::new(&msg.name).color(Color32::LIGHT_GRAY)); if !&msg.description.is_empty() { self.description = (&msg.description).to_owned(); ui.label(RichText::new("[AI]").color(Color32::DARK_GRAY)); } }); ui.label(&msg.message); ui.separator(); } if self.autoscroll { ui.scroll_to_cursor(Some(Align::BOTTOM)); } } }); }); ctx.request_repaint(); toasts.show(ctx); } }