synpad 0.1.0

A full-featured Matrix chat client built with Dioxus
use dioxus::prelude::*;

/// Common emoji shortcodes mapped to their Unicode emoji.
const EMOJI_SHORTCODES: &[(&str, &str)] = &[
    ("smile", "\u{1F604}"), ("laughing", "\u{1F606}"), ("blush", "\u{1F60A}"), ("smiley", "\u{1F603}"),
    ("relaxed", "\u{263A}"), ("smirk", "\u{1F60F}"), ("heart_eyes", "\u{1F60D}"), ("kissing_heart", "\u{1F618}"),
    ("kissing", "\u{1F617}"), ("flushed", "\u{1F633}"), ("relieved", "\u{1F60C}"), ("satisfied", "\u{1F606}"),
    ("grin", "\u{1F601}"), ("wink", "\u{1F609}"), ("stuck_out_tongue_winking_eye", "\u{1F61C}"),
    ("stuck_out_tongue", "\u{1F61B}"), ("sleeping", "\u{1F634}"), ("worried", "\u{1F61F}"),
    ("frowning", "\u{1F626}"), ("anguished", "\u{1F627}"), ("open_mouth", "\u{1F62E}"),
    ("grimacing", "\u{1F62C}"), ("confused", "\u{1F615}"), ("hushed", "\u{1F62F}"),
    ("expressionless", "\u{1F611}"), ("unamused", "\u{1F612}"), ("sweat_smile", "\u{1F605}"),
    ("sweat", "\u{1F613}"), ("disappointed_relieved", "\u{1F625}"), ("weary", "\u{1F629}"),
    ("pensive", "\u{1F614}"), ("disappointed", "\u{1F61E}"), ("confounded", "\u{1F616}"),
    ("fearful", "\u{1F628}"), ("cold_sweat", "\u{1F630}"), ("persevere", "\u{1F623}"),
    ("cry", "\u{1F622}"), ("sob", "\u{1F62D}"), ("joy", "\u{1F602}"), ("astonished", "\u{1F632}"),
    ("scream", "\u{1F631}"), ("tired_face", "\u{1F62B}"), ("angry", "\u{1F620}"), ("rage", "\u{1F621}"),
    ("triumph", "\u{1F624}"), ("sleepy", "\u{1F62A}"), ("yum", "\u{1F60B}"), ("mask", "\u{1F637}"),
    ("sunglasses", "\u{1F60E}"), ("dizzy_face", "\u{1F635}"), ("imp", "\u{1F47F}"),
    ("smiling_imp", "\u{1F608}"), ("neutral_face", "\u{1F610}"), ("no_mouth", "\u{1F636}"),
    ("innocent", "\u{1F607}"), ("alien", "\u{1F47D}"),
    ("heart", "\u{2764}"), ("broken_heart", "\u{1F494}"), ("two_hearts", "\u{1F495}"),
    ("sparkling_heart", "\u{1F496}"), ("heartpulse", "\u{1F497}"), ("blue_heart", "\u{1F499}"),
    ("green_heart", "\u{1F49A}"), ("yellow_heart", "\u{1F49B}"), ("purple_heart", "\u{1F49C}"),
    ("gift_heart", "\u{1F49D}"),
    ("thumbsup", "\u{1F44D}"), ("thumbsdown", "\u{1F44E}"), ("ok_hand", "\u{1F44C}"),
    ("punch", "\u{1F44A}"), ("fist", "\u{270A}"), ("v", "\u{270C}"), ("wave", "\u{1F44B}"),
    ("hand", "\u{270B}"), ("open_hands", "\u{1F450}"), ("point_up", "\u{261D}"),
    ("point_down", "\u{1F447}"), ("point_left", "\u{1F448}"), ("point_right", "\u{1F449}"),
    ("raised_hands", "\u{1F64C}"), ("pray", "\u{1F64F}"), ("clap", "\u{1F44F}"),
    ("muscle", "\u{1F4AA}"), ("nail_care", "\u{1F485}"),
    ("fire", "\u{1F525}"), ("star", "\u{2B50}"), ("star2", "\u{1F31F}"), ("sparkles", "\u{2728}"),
    ("zap", "\u{26A1}"), ("sunny", "\u{2600}"), ("cloud", "\u{2601}"), ("snowflake", "\u{2744}"),
    ("umbrella", "\u{2602}"), ("rainbow", "\u{1F308}"), ("rocket", "\u{1F680}"),
    ("tada", "\u{1F389}"), ("confetti_ball", "\u{1F38A}"), ("balloon", "\u{1F388}"),
    ("christmas_tree", "\u{1F384}"), ("gift", "\u{1F381}"), ("bell", "\u{1F514}"),
    ("100", "\u{1F4AF}"), ("checkmark", "\u{2705}"), ("x", "\u{274C}"), ("warning", "\u{26A0}"),
    ("question", "\u{2753}"), ("exclamation", "\u{2757}"),
    ("dog", "\u{1F436}"), ("cat", "\u{1F431}"), ("mouse", "\u{1F42D}"), ("rabbit", "\u{1F430}"),
    ("bear", "\u{1F43B}"), ("panda_face", "\u{1F43C}"), ("penguin", "\u{1F427}"),
    ("bird", "\u{1F426}"), ("fish", "\u{1F41F}"), ("whale", "\u{1F433}"),
    ("rose", "\u{1F339}"), ("sunflower", "\u{1F33B}"), ("cherry_blossom", "\u{1F338}"),
    ("tulip", "\u{1F337}"), ("seedling", "\u{1F331}"), ("cactus", "\u{1F335}"),
    ("pizza", "\u{1F355}"), ("hamburger", "\u{1F354}"), ("fries", "\u{1F35F}"),
    ("coffee", "\u{2615}"), ("beer", "\u{1F37A}"), ("wine_glass", "\u{1F377}"),
    ("cake", "\u{1F382}"), ("cookie", "\u{1F36A}"), ("apple", "\u{1F34E}"),
    ("eyes", "\u{1F440}"), ("tongue", "\u{1F445}"), ("ear", "\u{1F442}"),
    ("nose", "\u{1F443}"), ("lips", "\u{1F444}"),
    ("skull", "\u{1F480}"), ("ghost", "\u{1F47B}"), ("jack_o_lantern", "\u{1F383}"),
    ("robot", "\u{1F916}"), ("poop", "\u{1F4A9}"),
    ("lock", "\u{1F512}"), ("unlock", "\u{1F513}"), ("key", "\u{1F511}"),
    ("bulb", "\u{1F4A1}"), ("mag", "\u{1F50D}"), ("wrench", "\u{1F527}"),
    ("hammer", "\u{1F528}"), ("gear", "\u{2699}"), ("link", "\u{1F517}"),
    ("hourglass", "\u{23F3}"), ("alarm_clock", "\u{23F0}"),
    ("heavy_check_mark", "\u{2714}"), ("heavy_plus_sign", "\u{2795}"),
    ("heavy_minus_sign", "\u{2796}"), ("heavy_multiplication_x", "\u{2716}"),
    ("arrow_right", "\u{27A1}"), ("arrow_left", "\u{2B05}"),
    ("arrow_up", "\u{2B06}"), ("arrow_down", "\u{2B07}"),
    ("thinking", "\u{1F914}"), ("shushing", "\u{1F92B}"), ("nerd", "\u{1F913}"),
    ("partying_face", "\u{1F973}"), ("pleading", "\u{1F97A}"),
];

/// Emoji shortcode autocomplete popup.
#[component]
pub fn EmojiShortcodeAutocomplete(
    query: String,
    on_select: EventHandler<String>,
) -> Element {
    let query_lower = query.to_lowercase();
    let filtered: Vec<(&str, &str)> = EMOJI_SHORTCODES
        .iter()
        .filter(|(name, _)| name.starts_with(&query_lower))
        .take(8)
        .copied()
        .collect();

    if filtered.is_empty() {
        return rsx! {};
    }

    rsx! {
        div {
            class: "emoji-shortcode-autocomplete",
            for (name, emoji) in filtered.iter() {
                {
                    let emoji_str = emoji.to_string();
                    let name_str = name.to_string();
                    let emoji_for_click = emoji_str.clone();
                    rsx! {
                        button {
                            class: "emoji-shortcode-autocomplete__item",
                            onclick: move |_| on_select.call(emoji_for_click.clone()),
                            span { class: "emoji-shortcode-autocomplete__emoji", "{emoji_str}" }
                            span { class: "emoji-shortcode-autocomplete__name", ":{name_str}:" }
                        }
                    }
                }
            }
        }
    }
}