use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
pub struct CollabConfig {
#[serde(default)]
pub username: String,
#[serde(default)]
pub custom_relay: Option<String>,
#[serde(default = "default_action_rate_ms")]
pub action_rate_ms: u64,
#[serde(default = "default_true")]
pub show_pointers: bool,
}
impl Default for CollabConfig {
fn default() -> Self {
Self {
username: String::new(),
custom_relay: None,
action_rate_ms: default_action_rate_ms(),
show_pointers: true,
}
}
}
fn default_true() -> bool {
true
}
fn default_action_rate_ms() -> u64 {
16
}
#[derive(Clone, Debug, Default)]
pub struct CollabUiState {
pub peer_id: Option<String>,
pub sessions: HashMap<gantz_ca::Name, SessionDisplay>,
pub relays: Vec<(String, bool)>,
}
#[derive(Clone, Debug, Default)]
pub struct SessionDisplay {
pub is_host: bool,
pub conn: SessionConn,
pub awaiting_snapshot: bool,
pub peers: Vec<PeerDisplay>,
pub ticket: Option<String>,
pub conflicts: usize,
pub error: Option<String>,
pub pointers: Vec<PointerDisplay>,
}
#[derive(Clone, Debug)]
pub struct PointerDisplay {
pub pos: egui::Pos2,
pub label: String,
pub color: egui::Color32,
}
impl PointerDisplay {
pub fn new(pos: (f32, f32), label: String, peer: &[u8; 32]) -> Self {
Self {
pos: egui::pos2(pos.0, pos.1),
label,
color: peer_color(peer),
}
}
}
pub fn peer_color(peer: &[u8; 32]) -> egui::Color32 {
let hue = u16::from_le_bytes([peer[0], peer[1]]) as f32 / u16::MAX as f32;
egui::ecolor::Hsva::new(hue, 0.75, 0.9, 1.0).into()
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub enum SessionConn {
#[default]
Connecting,
Live,
Degraded,
}
#[derive(Clone, Debug, Default)]
pub struct PeerDisplay {
pub id: String,
pub name: Option<String>,
}
impl SessionDisplay {
pub fn sync_status(&self) -> String {
match self.peers.len() {
0 => "waiting for a peer to connect".to_string(),
1 => "receiving graph from 1 peer".to_string(),
n => format!("receiving graph from {n} peers"),
}
}
pub fn hover_text(&self) -> String {
let mut text = format!("shared session: {}", self.conn.label());
if self.peers.is_empty() {
text.push_str("\nno peers connected");
}
for peer in &self.peers {
match &peer.name {
Some(name) => text.push_str(&format!("\n{name} ({})", peer.id)),
None => text.push_str(&format!("\n{}", peer.id)),
}
}
text
}
}
impl SessionConn {
pub fn color(&self) -> egui::Color32 {
match self {
Self::Connecting => egui::Color32::from_rgb(0xd0, 0xa0, 0x30),
Self::Live => egui::Color32::from_rgb(0x50, 0xc0, 0x50),
Self::Degraded => egui::Color32::from_rgb(0xc0, 0x50, 0x50),
}
}
pub fn label(&self) -> &'static str {
match self {
Self::Connecting => "connecting",
Self::Live => "live",
Self::Degraded => "offline",
}
}
}