synpad 0.1.0

A full-featured Matrix chat client built with Dioxus
use crate::theme::colors;

/// Generate a consistent color for a user ID (for avatar backgrounds).
pub fn user_color(user_id: &str) -> &'static str {
    let hash = user_id
        .bytes()
        .fold(0u32, |acc, b| acc.wrapping_add(b as u32));
    let index = (hash as usize) % colors::dark::AVATAR_COLORS.len();
    colors::dark::AVATAR_COLORS[index]
}

/// Get the initials from a display name (up to 2 characters).
pub fn display_name_initials(name: &str) -> String {
    let words: Vec<&str> = name.split_whitespace().collect();
    match words.len() {
        0 => "?".to_string(),
        1 => words[0].chars().next().map(|c| c.to_uppercase().to_string()).unwrap_or_default(),
        _ => {
            let first = words[0].chars().next().unwrap_or('?');
            let last = words.last().unwrap().chars().next().unwrap_or('?');
            format!("{}{}", first.to_uppercase(), last.to_uppercase())
        }
    }
}

/// Format a member count for display.
pub fn format_member_count(count: u64) -> String {
    if count == 1 {
        "1 member".to_string()
    } else {
        format!("{count} members")
    }
}

/// Format file size for display.
pub fn format_file_size(bytes: u64) -> String {
    const KB: u64 = 1024;
    const MB: u64 = KB * 1024;
    const GB: u64 = MB * 1024;

    if bytes < KB {
        format!("{bytes} B")
    } else if bytes < MB {
        format!("{:.1} KB", bytes as f64 / KB as f64)
    } else if bytes < GB {
        format!("{:.1} MB", bytes as f64 / MB as f64)
    } else {
        format!("{:.1} GB", bytes as f64 / GB as f64)
    }
}