From 431736e967b9f7c11ff5b5e1f62bb0126fc2d208 Mon Sep 17 00:00:00 2001 From: MeexReay Date: Wed, 3 Sep 2025 20:14:33 +0300 Subject: [PATCH] refactor: add server list to sidebar --- src/chat/config.rs | 9 + src/chat/gui/images/servers.png | Bin 0 -> 26593 bytes src/chat/gui/mod.rs | 345 ++++++++++++++++++++++++-------- src/chat/gui/preferences.rs | 214 +++++++++----------- 4 files changed, 369 insertions(+), 199 deletions(-) create mode 100644 src/chat/gui/images/servers.png diff --git a/src/chat/config.rs b/src/chat/config.rs index 079720f..5c74470 100644 --- a/src/chat/config.rs +++ b/src/chat/config.rs @@ -3,6 +3,8 @@ use serde_default::DefaultFromSerde; use serde_yml; use std::{error::Error, fs, path::PathBuf}; +use super::SERVER_LIST; + const MESSAGE_FORMAT: &str = "\u{B9AC}\u{3E70}<{name}> {text}"; fn default_true() -> bool { @@ -30,6 +32,10 @@ pub fn default_message_format() -> String { MESSAGE_FORMAT.to_string() } +pub fn default_servers() -> Vec { + SERVER_LIST.to_vec() +} + #[derive(serde::Serialize, serde::Deserialize, DefaultFromSerde, Clone)] pub struct Config { #[serde(default = "default_host")] @@ -70,6 +76,8 @@ pub struct Config { pub debug_logs: bool, #[serde(default)] pub avatar: Option, + #[serde(default = "default_servers")] + pub servers: Vec, } #[cfg(target_os = "windows")] @@ -183,6 +191,7 @@ pub struct Args { pub avatar: Option, #[arg(long)] pub debug_logs: bool, + // TODO: add servers } impl Args { diff --git a/src/chat/gui/images/servers.png b/src/chat/gui/images/servers.png new file mode 100644 index 0000000000000000000000000000000000000000..544c24819a7eb1f17f1245d96fdf2bf9dad88fcb GIT binary patch literal 26593 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 {}, + Ok(_) => {} Err(e) => { println!("save config error: {e}") } @@ -71,7 +63,7 @@ struct UiModel { #[cfg(all(not(feature = "libnotify"), not(feature = "notify-rust")))] notifications: Arc>>, avatars: Arc>>>, - latest_sign: Arc + latest_sign: Arc, } thread_local!( @@ -163,9 +155,175 @@ fn build_menu(ctx: Arc, app: &Application) -> Menu { menu } -fn build_sidebar(_ctx: Arc, _app: &Application) -> GtkBox { - let sidebar = GtkBox::new(Orientation::Vertical, 0); - sidebar.append(&Label::new(Some("hello worlding"))); +fn build_sidebar_button( + ctx: Arc, + split_view: &OverlaySplitView, + server: String, + servers_list: &GtkBox, +) -> GtkBox { + let hbox = GtkBox::new(Orientation::Horizontal, 5); + + let button = Button::builder().label(&server).hexpand(true).build(); + + button.connect_clicked(clone!( + #[weak] + split_view, + #[weak] + ctx, + #[strong] + server, + move |_| { + let mut config = ctx.config.read().unwrap().clone(); + config.host = server.clone(); + ctx.set_config(&config); + try_save_config(get_config_path(), &config); + update_window_title(ctx.clone()); + if split_view.is_collapsed() { + split_view.set_show_sidebar(false); + } + } + )); + + hbox.append(&button); + + let delete_button = Button::from_icon_name("user-trash-symbolic"); + + delete_button.connect_clicked(clone!( + #[weak] + ctx, + #[weak] + hbox, + #[weak] + servers_list, + #[strong] + server, + move |_| { + servers_list.remove(&hbox); + let mut config = ctx.config.read().unwrap().clone(); + let index = config.servers.iter().position(|x| *x == server).unwrap(); + config.servers.remove(index); + ctx.set_config(&config); + try_save_config(get_config_path(), &config); + } + )); + + hbox.append(&delete_button); + + hbox +} + +fn build_sidebar(ctx: Arc, app: &Application, split_view: &OverlaySplitView) -> GtkBox { + let sidebar = GtkBox::new(Orientation::Vertical, 5); + + sidebar.append( + &Picture::builder() + .paintable(&Texture::for_pixbuf( + &load_pixbuf(include_bytes!("images/servers.png")).unwrap(), + )) + .build(), + ); + + let servers_list = GtkBox::new(Orientation::Vertical, 5); + + for server in ctx.config(|o| o.servers.clone()) { + servers_list.append(&build_sidebar_button( + ctx.clone(), + &split_view, + server, + &servers_list, + )); + } + + sidebar.append(&servers_list); + + let add_server = Button::builder() + .label("Add Server") + // .start_icon_name("list-add-symbolic") + .margin_top(10) + .build(); + + add_server.connect_clicked(clone!( + #[weak] + app, + #[weak] + servers_list, + #[weak] + ctx, + #[weak] + split_view, + move |_| { + let dialog = Dialog::new(); + + let vbox = GtkBox::new(Orientation::Vertical, 5); + + vbox.set_margin_bottom(20); + vbox.set_margin_top(20); + vbox.set_margin_end(20); + vbox.set_margin_start(20); + + vbox.append(&Label::builder().label("Add server").build()); + + let entry = Entry::builder().placeholder_text("Server host").build(); + + vbox.append(&entry); + + let hbox = GtkBox::new(Orientation::Horizontal, 5); + + let confirm = Button::builder().label("Confirm").build(); + + confirm.connect_clicked(clone!( + #[weak] + dialog, + #[weak] + servers_list, + #[weak] + ctx, + #[weak] + split_view, + #[weak] + entry, + move |_| { + let server: String = entry.text().into(); + + let mut config = ctx.config.read().unwrap().clone(); + config.servers.push(server.clone()); + ctx.set_config(&config); + try_save_config(get_config_path(), &config); + + servers_list.append(&build_sidebar_button( + ctx.clone(), + &split_view, + server, + &servers_list, + )); + dialog.close(); + } + )); + + hbox.append(&confirm); + + let cancel = Button::builder().label("Cancel").build(); + + cancel.connect_clicked(clone!( + #[weak] + dialog, + move |_| { + dialog.close(); + } + )); + + hbox.append(&cancel); + + vbox.append(&hbox); + + dialog.set_child(Some(&vbox)); + + dialog.present(app.active_window().as_ref()); + } + )); + + sidebar.append(&add_server); + sidebar } @@ -182,43 +340,43 @@ fn build_ui(ctx: Arc, app: &Application) -> UiModel { #[cfg(target_os = "windows")] let is_dark_theme = true; - - let main_box = GtkBox::new(Orientation::Vertical, 0); - let title = format!( - "bRAC - Connected to {} as {}", - ctx.config(|o| o.host.clone()), - &ctx.name() - ); + let main_box = GtkBox::new(Orientation::Vertical, 0); let (header, page, chat_box, chat_scrolled) = build_page(ctx.clone(), app); - let sidebar = build_sidebar(ctx.clone(), &app); - let split_view = OverlaySplitView::builder() - .sidebar(&sidebar) .content(&page) .enable_hide_gesture(true) .enable_show_gesture(true) .collapsed(true) .build(); - + + let sidebar = build_sidebar(ctx.clone(), &app, &split_view); + + split_view.set_sidebar(Some(&sidebar)); + main_box.append(&split_view); let toggle_button = Button::from_icon_name("go-previous-symbolic"); toggle_button.connect_clicked(clone!( - #[weak] split_view, + #[weak] + split_view, move |_| { split_view.set_show_sidebar(!split_view.shows_sidebar()); } )); - + header.pack_start(&toggle_button); let window = ApplicationWindow::builder() .application(app) - .title(&title) + .title(&format!( + "bRAC - Connected to {} as {}", + ctx.config(|o| o.host.clone()), + &ctx.name() + )) .default_width(500) .default_height(500) .resizable(true) @@ -226,17 +384,15 @@ fn build_ui(ctx: Arc, app: &Application) -> UiModel { .content(&main_box) .build(); - let breakpoint = Breakpoint::new( - BreakpointCondition::new_length( - libadwaita::BreakpointConditionLengthType::MinWidth, - 700.0, - libadwaita::LengthUnit::Px - ) - ); + let breakpoint = Breakpoint::new(BreakpointCondition::new_length( + libadwaita::BreakpointConditionLengthType::MinWidth, + 700.0, + libadwaita::LengthUnit::Px, + )); breakpoint.add_setter(&split_view, "collapsed", Some(&false.into())); breakpoint.add_setter(&toggle_button, "visible", Some(&false.into())); - + window.add_breakpoint(breakpoint); window.present(); @@ -252,7 +408,7 @@ fn build_ui(ctx: Arc, app: &Application) -> UiModel { #[cfg(all(not(feature = "libnotify"), not(feature = "notify-rust")))] notifications: Arc::new(RwLock::new(Vec::::new())), avatars: Arc::new(Mutex::new(HashMap::new())), - latest_sign: Arc::new(AtomicU64::new(0)) + latest_sign: Arc::new(AtomicU64::new(0)), } } @@ -329,22 +485,41 @@ fn setup(_: &Application, ctx: Arc, ui: UiModel) { if ctx.config(|o| !o.new_ui_enabled) { return; } - + thread::spawn(move || { for message in messages.iter() { - let Some(avatar_url) = grab_avatar(message) else { continue }; + let Some(avatar_url) = grab_avatar(message) else { + continue; + }; let avatar_id = get_avatar_id(&avatar_url); - let Some(avatar) = load_avatar(&avatar_url, ctx.config(|o| o.proxy.clone()), ctx.config(|o| o.max_avatar_size as usize)) else { println!("cant load avatar: {avatar_url} request error"); continue }; - let Ok(pixbuf) = load_pixbuf(&avatar) else { println!("cant load avatar: {avatar_url} pixbuf error"); continue; }; - let Some(pixbuf) = pixbuf.scale_simple(32, 32, InterpType::Bilinear) else { println!("cant load avatar: {avatar_url} scale image error"); continue }; + let Some(avatar) = load_avatar( + &avatar_url, + ctx.config(|o| o.proxy.clone()), + ctx.config(|o| o.max_avatar_size as usize), + ) else { + println!("cant load avatar: {avatar_url} request error"); + continue; + }; + let Ok(pixbuf) = load_pixbuf(&avatar) else { + println!("cant load avatar: {avatar_url} pixbuf error"); + continue; + }; + let Some(pixbuf) = + pixbuf.scale_simple(32, 32, InterpType::Bilinear) + else { + println!("cant load avatar: {avatar_url} scale image error"); + continue; + }; let texture = Texture::for_pixbuf(&pixbuf); timeout_add_once(Duration::ZERO, { move || { GLOBAL.with(|global| { if let Some(ui) = &*global.borrow() { - if let Some(pics) = ui.avatars.lock().unwrap().remove(&avatar_id) { + if let Some(pics) = + ui.avatars.lock().unwrap().remove(&avatar_id) + { for pic in pics { pic.set_custom_image(Some(&texture)); } @@ -454,42 +629,42 @@ fn load_avatar(url: &str, proxy: Option, response_limit: usize) -> Optio } else { format!("socks5://{proxy}") }; - + reqwest::blocking::Client::builder() .proxy(reqwest::Proxy::all(&proxy).ok()?) - .build().ok()? + .build() + .ok()? } else { reqwest::blocking::Client::new() }; - - client.get(url).send().ok() - .and_then(|mut resp| { - let mut data = Vec::new(); - let mut length = 0; - - loop { - if length >= response_limit { - break; - } - let mut buf = vec![0; (response_limit - length).min(1024)]; - let now_len = resp.read(&mut buf).ok()?; - if now_len == 0 { - break; - } - buf.truncate(now_len); - length += now_len; - data.append(&mut buf); - } - Some(data) - }) + client.get(url).send().ok().and_then(|mut resp| { + let mut data = Vec::new(); + let mut length = 0; + + loop { + if length >= response_limit { + break; + } + let mut buf = vec![0; (response_limit - length).min(1024)]; + let now_len = resp.read(&mut buf).ok()?; + if now_len == 0 { + break; + } + buf.truncate(now_len); + length += now_len; + data.append(&mut buf); + } + + Some(data) + }) } // creates sign that expires in 0-20 minutes fn get_message_sign(name: &str, date: &str) -> u64 { let mut hasher = DefaultHasher::new(); hasher.write(name.as_bytes()); - hasher.write(date[..date.len()-2].as_bytes()); + hasher.write(date[..date.len() - 2].as_bytes()); hasher.finish() } @@ -512,9 +687,21 @@ fn on_add_message(ctx: Arc, ui: &UiModel, message: String, notify: bool } if ctx.config(|o| o.new_ui_enabled) { - ui.chat_box.append(&get_new_message_box(ctx.clone(), ui, message, notify, formatting_enabled)); + ui.chat_box.append(&get_new_message_box( + ctx.clone(), + ui, + message, + notify, + formatting_enabled, + )); } else { - ui.chat_box.append(&get_message_box(ctx.clone(), ui, message, notify, formatting_enabled)); + ui.chat_box.append(&get_message_box( + ctx.clone(), + ui, + message, + notify, + formatting_enabled, + )); }; timeout_add_local_once(Duration::from_millis(1000), move || { diff --git a/src/chat/gui/preferences.rs b/src/chat/gui/preferences.rs index 949a362..3135577 100644 --- a/src/chat/gui/preferences.rs +++ b/src/chat/gui/preferences.rs @@ -1,35 +1,29 @@ use std::sync::Arc; -use libadwaita::gtk::Adjustment; -use libadwaita::{ - self as adw, ActionRow, ButtonRow, EntryRow, PreferencesDialog, PreferencesGroup, PreferencesPage, SpinRow, SwitchRow -}; use adw::gdk::Display; use adw::glib::clone; -use adw::glib::{ - self, -}; +use adw::glib::{self}; use adw::prelude::*; use adw::Application; +use libadwaita::gtk::Adjustment; +use libadwaita::{ + self as adw, ActionRow, ButtonRow, EntryRow, PreferencesDialog, PreferencesGroup, + PreferencesPage, SpinRow, SwitchRow, +}; use adw::gtk; use gtk::Button; - use crate::chat::{ - config::{ - get_config_path, Config, - }, + config::{get_config_path, Config}, ctx::Context, }; use super::{try_save_config, update_window_title}; - pub fn open_settings(ctx: Arc, app: &Application) { let dialog = PreferencesDialog::builder().build(); - let page = PreferencesPage::builder() .title("General") .icon_name("avatar-default-symbolic") @@ -40,7 +34,6 @@ pub fn open_settings(ctx: Arc, app: &Application) { .description("Profile preferences") .build(); - // Name preference let name = EntryRow::builder() @@ -50,27 +43,22 @@ pub fn open_settings(ctx: Arc, app: &Application) { group.add(&name); - // Avatar preference - + let avatar = EntryRow::builder() .title("Avatar Link") .text(ctx.config(|o| o.avatar.clone()).unwrap_or_default()) .build(); group.add(&avatar); - page.add(&group); - - let group = PreferencesGroup::builder() .title("Server") .description("Connection preferences") .build(); - // Host preference let host = EntryRow::builder() @@ -80,60 +68,61 @@ pub fn open_settings(ctx: Arc, app: &Application) { group.add(&host); - // Messages limit preference - + let messages_limit = SpinRow::builder() .title("Messages limit") - .adjustment(&Adjustment::builder() - .lower(1.0) - .upper(1048576.0) - .page_increment(10.0) - .step_increment(10.0) - .value(ctx.config(|o| o.max_messages) as f64) - .build()) + .adjustment( + &Adjustment::builder() + .lower(1.0) + .upper(1048576.0) + .page_increment(10.0) + .step_increment(10.0) + .value(ctx.config(|o| o.max_messages) as f64) + .build(), + ) .build(); - + group.add(&messages_limit); - // Update interval preference - + let update_interval = SpinRow::builder() .title("Update interval") .subtitle("In milliseconds") - .adjustment(&Adjustment::builder() - .lower(10.0) - .upper(1048576.0) - .page_increment(10.0) - .step_increment(10.0) - .value(ctx.config(|o| o.update_time) as f64) - .build()) + .adjustment( + &Adjustment::builder() + .lower(10.0) + .upper(1048576.0) + .page_increment(10.0) + .step_increment(10.0) + .value(ctx.config(|o| o.update_time) as f64) + .build(), + ) .build(); - + group.add(&update_interval); - // Update interval OOF preference - + let update_interval_oof = SpinRow::builder() .title("Update interval when unfocused") .subtitle("In milliseconds") - .adjustment(&Adjustment::builder() - .lower(10.0) - .upper(1048576.0) - .page_increment(10.0) - .step_increment(10.0) - .value(ctx.config(|o| o.oof_update_time) as f64) - .build()) + .adjustment( + &Adjustment::builder() + .lower(10.0) + .upper(1048576.0) + .page_increment(10.0) + .step_increment(10.0) + .value(ctx.config(|o| o.oof_update_time) as f64) + .build(), + ) .build(); group.add(&update_interval_oof); page.add(&group); - - let group = PreferencesGroup::builder() .title("Config") .description("Configuration tools") @@ -154,7 +143,8 @@ pub fn open_settings(ctx: Arc, app: &Application) { config_path_copy.set_margin_top(10); config_path_copy.set_margin_bottom(10); config_path_copy.connect_clicked(clone!( - #[weak] clipboard, + #[weak] + clipboard, move |_| { if let Some(text) = get_config_path().to_str() { clipboard.set_text(text); @@ -164,19 +154,20 @@ pub fn open_settings(ctx: Arc, app: &Application) { config_path.add_suffix(&config_path_copy); config_path.set_activatable(false); - + group.add(&config_path); // Reset button - let reset_button = ButtonRow::builder() - .title("Reset all") - .build(); + let reset_button = ButtonRow::builder().title("Reset all").build(); reset_button.connect_activated(clone!( - #[weak] ctx, - #[weak] app, - #[weak] dialog, + #[weak] + ctx, + #[weak] + app, + #[weak] + dialog, move |_| { dialog.close(); let config = Config::default(); @@ -185,15 +176,13 @@ pub fn open_settings(ctx: Arc, app: &Application) { open_settings(ctx, &app); } )); - + group.add(&reset_button); - + page.add(&group); dialog.add(&page); - - let page = PreferencesPage::builder() .title("Protocol") .icon_name("network-wired-symbolic") @@ -204,7 +193,6 @@ pub fn open_settings(ctx: Arc, app: &Application) { .description("Network preferences") .build(); - // Proxy preference let proxy = EntryRow::builder() @@ -214,35 +202,33 @@ pub fn open_settings(ctx: Arc, app: &Application) { group.add(&proxy); - // Max avatar size preference - + let max_avatar_size = SpinRow::builder() .title("Max avatar size") .subtitle("Maximum avatar size in bytes") - .adjustment(&Adjustment::builder() - .lower(0.0) - .upper(1074790400.0) - .page_increment(1024.0) - .step_increment(1024.0) - .value(ctx.config(|o| o.max_avatar_size) as f64) - .build()) + .adjustment( + &Adjustment::builder() + .lower(0.0) + .upper(1074790400.0) + .page_increment(1024.0) + .step_increment(1024.0) + .value(ctx.config(|o| o.max_avatar_size) as f64) + .build(), + ) .build(); group.add(&max_avatar_size); - page.add(&group); - let group = PreferencesGroup::builder() .title("Protocol") .description("Rac protocol preferences") .build(); - // Message format preference - + let message_format = EntryRow::builder() .title("Message format") .text(ctx.config(|o| o.message_format.clone())) @@ -252,9 +238,8 @@ pub fn open_settings(ctx: Arc, app: &Application) { page.add(&group); - // Hide IP preference - + let hide_my_ip = SwitchRow::builder() .title("Hide IP") .subtitle("Hides only for clRAC and other dummy clients") @@ -263,9 +248,8 @@ pub fn open_settings(ctx: Arc, app: &Application) { group.add(&hide_my_ip); - // Chunked reading preference - + let chunked_reading = SwitchRow::builder() .title("Chunked reading") .subtitle("Read messages in chunks (less traffic usage, less compatibility)") @@ -274,9 +258,8 @@ pub fn open_settings(ctx: Arc, app: &Application) { group.add(&chunked_reading); - // Enable commands preference - + let enable_commands = SwitchRow::builder() .title("Enable commands") .subtitle("Enable slash commands (eg. /login) on client-side") @@ -285,11 +268,9 @@ pub fn open_settings(ctx: Arc, app: &Application) { group.add(&enable_commands); - page.add(&group); - - dialog.add(&page); + dialog.add(&page); let page = PreferencesPage::builder() .title("Interface") @@ -301,102 +282,96 @@ pub fn open_settings(ctx: Arc, app: &Application) { .description("Messages render preferences") .build(); - // Debug logs preference - + let debug_logs = SwitchRow::builder() .title("Debug logs") .subtitle("Print debug logs to the chat") .active(ctx.config(|o| o.debug_logs)) .build(); - + group.add(&debug_logs); - // Show IPs preference - + let show_ips = SwitchRow::builder() .title("Show IPs") .subtitle("Show authors IP addresses if possible") .active(ctx.config(|o| o.show_other_ip)) .build(); - + group.add(&show_ips); - // Format messages preference - + let format_messages = SwitchRow::builder() .title("Format messages") .subtitle("Disable to see raw messages") .active(ctx.config(|o| o.formatting_enabled)) .build(); - + group.add(&format_messages); - // Show avatars preference - + let show_avatars = SwitchRow::builder() .title("Show avatars") .subtitle("Enables new messages UI") .active(ctx.config(|o| o.new_ui_enabled)) .build(); - + group.add(&show_avatars); page.add(&group); - let group = PreferencesGroup::builder() .title("Interface") .description("General interface preferences (restart after changing)") .build(); - // Remove GUI shit preference - + let remove_gui_shit = SwitchRow::builder() .title("Remove GUI shit") .subtitle("Removes calendar, konata and clock") .active(ctx.config(|o| o.remove_gui_shit)) .build(); - + group.add(&remove_gui_shit); - // Konata size preference - + let konata_size = SpinRow::builder() .title("Konata size") .subtitle("Set konata size percent") - .adjustment(&Adjustment::builder() - .lower(0.0) - .upper(200.0) - .page_increment(10.0) - .step_increment(10.0) - .value(ctx.config(|o| o.konata_size) as f64) - .build()) + .adjustment( + &Adjustment::builder() + .lower(0.0) + .upper(200.0) + .page_increment(10.0) + .step_increment(10.0) + .value(ctx.config(|o| o.konata_size) as f64) + .build(), + ) .build(); group.add(&konata_size); - - + // Enable notifications preference - + let enable_notifications = SwitchRow::builder() .title("Enable notifications") .subtitle("Send notifications on chat and system messages") .active(ctx.config(|o| o.notifications_enabled)) .build(); - + group.add(&enable_notifications); page.add(&group); - - + dialog.add(&page); - dialog.connect_closed(move |_| { + let old_config = ctx.config.read().unwrap().clone(); + let config = Config { host: host.text().to_string(), name: { @@ -441,6 +416,7 @@ pub fn open_settings(ctx: Arc, app: &Application) { Some(proxy) } }, + servers: old_config.servers, }; ctx.set_config(&config); try_save_config(get_config_path(), &config); @@ -449,5 +425,3 @@ pub fn open_settings(ctx: Arc, app: &Application) { dialog.present(app.active_window().as_ref()); } - -