Просмотр исходного кода

app: reorganize netstatus icon inside chat node to clean up schema and add extra proj assets

darkfi 17 часов назад
Родитель
Сommit
d1a732b7bf

+ 4 - 0
bin/app/Makefile

@@ -88,6 +88,10 @@ data/font/NotoColorEmoji.ttf:
 	mkdir -p data/font/
 	wget -P data/font/ -c https://dark.fi/assets/NotoColorEmoji.ttf
 
+data/font/darkfi-custom-emoji.ttf:
+	mkdir -p data/font/
+	wget -P data/font/ -c https://dark.fi/assets/darkfi-custom-emoji.ttf
+
 # App data
 
 data/forest_1920x1080.ivf:

BIN
bin/app/data/sfx/cloak.oga


+ 1 - 1
bin/app/src/app/schema/menu/channel.rs

@@ -281,7 +281,7 @@ pub async fn make(
     let channel_vis = channel_is_visible.clone();
     let redraw = app.redraw_trigger.clone();
     let menu_node = sg_root.lookup_node("/window/content/chat/menu_layer").unwrap();
-    let netstatus_layer = sg_root.lookup_node("/window/content/netstatus_layer").unwrap();
+    let netstatus_layer = sg_root.lookup_node("/window/content/chat/netstatus_layer").unwrap();
     let content_go = content.clone();
     let goback = async move || {
         info!(target: "app::chat", "clicked back");

+ 1 - 1
bin/app/src/app/schema/menu/contact.rs

@@ -281,7 +281,7 @@ pub async fn make(
     let channel_vis = channel_is_visible.clone();
     let redraw = app.redraw_trigger.clone();
     let menu_node = sg_root.lookup_node("/window/content/chat/menu_layer").unwrap();
-    let netstatus_layer = sg_root.lookup_node("/window/content/netstatus_layer").unwrap();
+    let netstatus_layer = sg_root.lookup_node("/window/content/chat/netstatus_layer").unwrap();
     let content_go = content.clone();
     let goback = async move || {
         info!(target: "app::chat", "clicked back");

+ 6 - 20
bin/app/src/app/schema/menu/mod.rs

@@ -111,7 +111,7 @@ mod edit_switch;
 
 pub async fn make(
     app: &App,
-    content: SceneNodePtr,
+    chat_layer: SceneNodePtr,
     i18n_fish: &I18nBabelFish,
     channels_tree: Tree,
     contacts_tree: Tree,
@@ -128,20 +128,6 @@ pub async fn make(
 
     let atom = &mut PropertyAtomicGuard::none();
 
-    // Create chat container layer
-    let chat_layer = create_layer("chat");
-    let prop = chat_layer.get_property("rect").unwrap();
-    prop.set_f32(atom, Role::App, 0, 0.).unwrap();
-    prop.set_f32(atom, Role::App, 1, 0.).unwrap();
-    prop.set_expr(atom, Role::App, 2, expr::load_var("w")).unwrap();
-    prop.set_expr(atom, Role::App, 3, expr::load_var("h")).unwrap();
-    chat_layer.set_property_bool(atom, Role::App, "is_visible", true).unwrap();
-    chat_layer.set_property_u32(atom, Role::App, "z_index", 1).unwrap();
-    let chat_layer = chat_layer
-        .setup(|me| Layer::new(me, app.renderer.clone(), app.redraw_trigger.clone()))
-        .await;
-    content.link(chat_layer.clone());
-
     // Create contact screen
     let contact_layer = create_layer("contact_screen_layer");
     let prop = contact_layer.get_property("rect").unwrap();
@@ -194,7 +180,7 @@ pub async fn make(
     prop.set_f32(atom, Role::App, 1, 0.).unwrap();
     prop.set_expr(atom, Role::App, 2, expr::load_var("w")).unwrap();
     prop.set_expr(atom, Role::App, 3, expr::load_var("h")).unwrap();
-    layer_node.set_property_bool(atom, Role::App, "is_visible", false).unwrap();
+    layer_node.set_property_bool(atom, Role::App, "is_visible", true).unwrap();
     layer_node.set_property_u32(atom, Role::App, "z_index", 1).unwrap();
     let layer_node = layer_node
         .setup(|me| Layer::new(me, app.renderer.clone(), app.redraw_trigger.clone()))
@@ -526,7 +512,7 @@ pub async fn make(
     btns.connect_edit_handlers(app, &menu_node, Some(mainlayer_is_visible.clone()));
 }
 
-pub async fn setup_wallet_button(app: &App, menu_layer: SceneNodePtr, i18n_fish: &I18nBabelFish) {
+pub async fn setup_wallet_button(app: &App, chat_layer: SceneNodePtr, i18n_fish: &I18nBabelFish) {
     let atom = &mut PropertyAtomicGuard::none();
     let mut cc = expr::Compiler::new();
     cc.add_const_f32("MENU_BTN_W_R", MENU_BTN_W_R);
@@ -541,7 +527,8 @@ pub async fn setup_wallet_button(app: &App, menu_layer: SceneNodePtr, i18n_fish:
     )
     .unwrap();
 
-    let menulayer_is_visible = PropertyBool::wrap(&menu_layer, Role::App, "is_visible", 0).unwrap();
+    let chat_is_visible = PropertyBool::wrap(&chat_layer, Role::App, "is_visible", 0).unwrap();
+    let menu_layer = chat_layer.lookup_node("/menu_layer").unwrap();
 
     // Wallet status icon (blockchain netlogo, all cyan = fully connected)
     // Positioned at center of wallet button area, above write_btn
@@ -587,12 +574,11 @@ pub async fn setup_wallet_button(app: &App, menu_layer: SceneNodePtr, i18n_fish:
     )
     .unwrap();
     let redraw = app.redraw_trigger.clone();
-    let menulayer_is_visible2 = menulayer_is_visible.clone();
     let listen_click = app.ex.spawn(async move {
         while let Ok(_) = recvr.recv().await {
             let atom = &mut redraw.make_guard(gfxtag!("wallet_click"));
             wallet_is_visible.set(atom, true);
-            menulayer_is_visible2.set(atom, false);
+            chat_is_visible.set(atom, false);
         }
     });
     app.tasks.lock().unwrap().push(listen_click);

+ 146 - 129
bin/app/src/app/schema/mod.rs

@@ -32,6 +32,7 @@ use crate::{
     gfx::gfxtag,
     prop::{PropertyAtomicGuard, PropertyFloat32, Role},
     scene::{SceneNodePtr, Slot},
+    sfx,
     shape,
     ui::{emoji_picker, Button, Layer, Text, VectorArt, VectorShape, Video},
     util::i18n::I18nBabelFish,
@@ -423,6 +424,138 @@ pub async fn make(app: &App, window: SceneNodePtr, i18n_fish: &I18nBabelFish, db
         window.link(node);
     }
 
+    // Navbar Settings Button
+
+    /*
+    // Layer
+    let settingslayer_node = create_layer("settings_button_layer");
+    let prop = settingslayer_node.get_property("rect").unwrap();
+    let code = cc.compile("w - NETSTATUS_ICON_SIZE - SETTINGS_ICON_SIZE").unwrap();
+    prop.set_expr(atom, Role::App, 0, code).unwrap();
+    prop.set_f32(atom, Role::App, 1, 0.).unwrap();
+    prop.set_f32(atom, Role::App, 2, 1000.).unwrap();
+    prop.set_f32(atom, Role::App, 3, 1000.).unwrap();
+    settingslayer_node.set_property_bool(atom, Role::App, "is_visible", true).unwrap();
+    settingslayer_node.set_property_u32(atom, Role::App, "z_index", 2).unwrap();
+    let settingslayer_node =
+        settingslayer_node.setup(|me| Layer::new(me, app.renderer.clone(), app.redraw_trigger.clone())).await;
+    content.link(settingslayer_node.clone());
+
+    // Background
+    let node = create_vector_art("settings_btn_bg");
+    let prop = node.get_property("rect").unwrap();
+    prop.set_f32(atom, Role::App, 0, NETSTATUS_ICON_SIZE / 2.).unwrap();
+    prop.set_f32(atom, Role::App, 1, NETSTATUS_ICON_SIZE / 2.).unwrap();
+    prop.set_expr(atom, Role::App, 2, expr::load_var("w")).unwrap();
+    prop.set_expr(atom, Role::App, 3, expr::load_var("h")).unwrap();
+    node.set_property_bool(atom, Role::App, "is_visible", true).unwrap();
+    node.set_property_u32(atom, Role::App, "z_index", 0).unwrap();
+    let shape = shape::create_settings([0., 0.94, 1., 1.]).scaled(20.);
+    let node =
+        node.setup(|me| VectorArt::new(me, shape, app.renderer.clone(), app.redraw_trigger.clone())).await;
+    settingslayer_node.link(node);
+
+    // Button
+    let node = create_button("settings_btn");
+    node.set_property_bool(atom, Role::App, "is_active", true).unwrap();
+    let prop = node.get_property("rect").unwrap();
+    prop.set_f32(atom, Role::App, 0, 0.).unwrap();
+    prop.set_f32(atom, Role::App, 1, 0.).unwrap();
+    prop.set_f32(atom, Role::App, 2, NETSTATUS_ICON_SIZE).unwrap();
+    prop.set_f32(atom, Role::App, 3, NETSTATUS_ICON_SIZE).unwrap();
+
+    let sg_root = app.sg_root.clone();
+    let settings = move || {
+        info!(target: "app::chat", "clicked settings");
+        let atom = &mut PropertyAtomicGuard::new();
+
+        // Hide all relevant window children nodes
+        // Messy.
+        //
+        // Some suggestions:
+        //  1. Something closer to a router, that would be a accessible globally,
+        //  which essentially holds a vector of references to SceneNodes
+        //  representing the app navigation history.
+        //  When the user changes the route, it would make invisible (or later remove
+        //  elements from the tree for optimization purposes) the node of the last SceneNodes
+        //  in the vector and all its children, recursively;
+        //  and append a new SceneNode pointer, which is the new "route" chosen by the user,
+        //  and draw it and its children recursively.
+        //  Note that this would implicitly handle nested routes (like
+        //  /window/somewhere1/view1 to /window/somewhere1/view2, if the last element of the
+        //  router currently points to view1 and we call router.goto("./view2")).
+        //
+        //  2. Support of wildcard in lookups in .get_children() or another method, like this "*_chat_layer".
+        let windows = sg_root.lookup_node("/window/content/chat").unwrap().get_children();
+        let target_substrings = vec!["_chat_layer", "menu_layer"];
+        for node in windows.iter() {
+            if target_substrings.iter().any(|&s| node.name.contains(s)) {
+                if let Err(e) = node.set_property_bool(atom, Role::App, "is_visible", false) {
+                    debug!("Failed to set property 'is_visible' on node: {:?}", e);
+                }
+            }
+        }
+
+        // Show settings
+        let settings_node = sg_root.lookup_node("/window/content/settings_layer").unwrap();
+        settings_node.set_property_bool(atom, Role::App, "is_visible", true).unwrap();
+    };
+
+    let (slot, recvr) = Slot::new("settings_clicked");
+    node.register("click", slot).unwrap();
+    let settings2 = settings.clone();
+    let listen_click = app.ex.spawn(async move {
+        while let Ok(_) = recvr.recv().await {
+            settings2();
+        }
+    });
+    app.tasks.lock().unwrap().push(listen_click);
+
+    let node = node.setup(|me| Button::new(me, app.renderer.clone(), app.redraw_trigger.clone())).await;
+    settingslayer_node.link(node);
+    */
+
+    let emoji_meshes = emoji_picker::EmojiMeshes::new(app.renderer.clone(), EMOJI_PICKER_ICON_SIZE);
+
+    let emoji_meshes2 = emoji_meshes.clone();
+    let _ = std::thread::spawn(move || {
+        for i in (0..500).step_by(20) {
+            for j in i..(i + 20) {
+                emoji_meshes2.lock().get(j);
+            }
+        }
+    });
+
+    let channels_tree = db.open_tree_default("channels").expect("cannot open channels tree");
+    let contacts_tree = db.open_tree_default("contacts").expect("cannot open contacts tree");
+
+    // Initialize default channels if tree is empty
+    if channels_tree.is_empty().unwrap() {
+        for channel_name in DEFAULT_CHANNELS {
+            let channel = Channel { name: channel_name.to_string(), secret: None };
+            let mut val = vec![];
+            channel.encode(&mut val).unwrap();
+            channels_tree.insert(channel_name.to_string().as_bytes(), &val).unwrap();
+        }
+    }
+
+    // Seed the joined-channels file with defaults on first run.
+    ensure_joined_channels_seeded();
+
+    // Create chat container layer
+    let chat_layer = create_layer("chat");
+    let prop = chat_layer.get_property("rect").unwrap();
+    prop.set_f32(atom, Role::App, 0, 0.).unwrap();
+    prop.set_f32(atom, Role::App, 1, 0.).unwrap();
+    prop.set_expr(atom, Role::App, 2, expr::load_var("w")).unwrap();
+    prop.set_expr(atom, Role::App, 3, expr::load_var("h")).unwrap();
+    chat_layer.set_property_bool(atom, Role::App, "is_visible", false).unwrap();
+    chat_layer.set_property_u32(atom, Role::App, "z_index", 1).unwrap();
+    let chat_layer = chat_layer
+        .setup(|me| Layer::new(me, app.renderer.clone(), app.redraw_trigger.clone()))
+        .await;
+    content.link(chat_layer.clone());
+
     let netlayer_node = create_layer("netstatus_layer");
     let prop = netlayer_node.get_property("rect").unwrap();
     let code = cc.compile("w - NETSTATUS_ICON_SIZE").unwrap();
@@ -438,7 +571,7 @@ pub async fn make(app: &App, window: SceneNodePtr, i18n_fish: &I18nBabelFish, db
     let netlayer_node = netlayer_node
         .setup(|me| Layer::new(me, app.renderer.clone(), app.redraw_trigger.clone()))
         .await;
-    content.link(netlayer_node.clone());
+    chat_layer.link(netlayer_node.clone());
 
     let node = create_vector_art("net0");
     let prop = node.get_property("rect").unwrap();
@@ -549,14 +682,16 @@ pub async fn make(app: &App, window: SceneNodePtr, i18n_fish: &I18nBabelFish, db
         while let Ok(_) = recvr.recv().await {
             i!("Reconnect button clicked");
 
-            /*
             // Toggle the overlay layer
-            let overlay = sg_root.lookup_node("/window/content/netstatus_overlay").unwrap();
-            let visible = !overlay.get_property_bool("is_visible").unwrap();
+            let overlay = sg_root.lookup_node("/window/content/chat/netstatus_overlay").unwrap();
+            let is_visible = overlay.get_property_bool("is_visible").unwrap();
+            if !is_visible {
+                sfx::play_cloak();
+            }
             let atom = &mut redraw.make_guard(gfxtag!("netstatus overlay toggle"));
-            overlay.set_property_bool(atom, Role::App, "is_visible", visible).unwrap();
-            */
+            overlay.set_property_bool(atom, Role::App, "is_visible", !is_visible).unwrap();
 
+            /*
             // Show netstat-klik icon
             let netstat_klik =
                 sg_root.lookup_node("/window/content/netstatus_layer/netstat_klik").unwrap();
@@ -591,6 +726,7 @@ pub async fn make(app: &App, window: SceneNodePtr, i18n_fish: &I18nBabelFish, db
                     e!("Failed to hide netstat_klik: {e}");
                 }
             }
+            */
         }
     });
     app.tasks.lock().unwrap().push(reconnect_task);
@@ -614,7 +750,7 @@ pub async fn make(app: &App, window: SceneNodePtr, i18n_fish: &I18nBabelFish, db
     let overlay_node = overlay_node
         .setup(|me| Layer::new(me, app.renderer.clone(), app.redraw_trigger.clone()))
         .await;
-    content.link(overlay_node.clone());
+    chat_layer.link(overlay_node.clone());
 
     // Placeholder single-color background filling the whole overlay
     let node = create_vector_art("overlay_bg");
@@ -714,127 +850,9 @@ pub async fn make(app: &App, window: SceneNodePtr, i18n_fish: &I18nBabelFish, db
         node.setup(|me| Button::new(me, app.renderer.clone(), app.redraw_trigger.clone())).await;
     overlay_node.link(node);
 
-    // Navbar Settings Button
-
-    /*
-    // Layer
-    let settingslayer_node = create_layer("settings_button_layer");
-    let prop = settingslayer_node.get_property("rect").unwrap();
-    let code = cc.compile("w - NETSTATUS_ICON_SIZE - SETTINGS_ICON_SIZE").unwrap();
-    prop.set_expr(atom, Role::App, 0, code).unwrap();
-    prop.set_f32(atom, Role::App, 1, 0.).unwrap();
-    prop.set_f32(atom, Role::App, 2, 1000.).unwrap();
-    prop.set_f32(atom, Role::App, 3, 1000.).unwrap();
-    settingslayer_node.set_property_bool(atom, Role::App, "is_visible", true).unwrap();
-    settingslayer_node.set_property_u32(atom, Role::App, "z_index", 2).unwrap();
-    let settingslayer_node =
-        settingslayer_node.setup(|me| Layer::new(me, app.renderer.clone(), app.redraw_trigger.clone())).await;
-    content.link(settingslayer_node.clone());
-
-    // Background
-    let node = create_vector_art("settings_btn_bg");
-    let prop = node.get_property("rect").unwrap();
-    prop.set_f32(atom, Role::App, 0, NETSTATUS_ICON_SIZE / 2.).unwrap();
-    prop.set_f32(atom, Role::App, 1, NETSTATUS_ICON_SIZE / 2.).unwrap();
-    prop.set_expr(atom, Role::App, 2, expr::load_var("w")).unwrap();
-    prop.set_expr(atom, Role::App, 3, expr::load_var("h")).unwrap();
-    node.set_property_bool(atom, Role::App, "is_visible", true).unwrap();
-    node.set_property_u32(atom, Role::App, "z_index", 0).unwrap();
-    let shape = shape::create_settings([0., 0.94, 1., 1.]).scaled(20.);
-    let node =
-        node.setup(|me| VectorArt::new(me, shape, app.renderer.clone(), app.redraw_trigger.clone())).await;
-    settingslayer_node.link(node);
-
-    // Button
-    let node = create_button("settings_btn");
-    node.set_property_bool(atom, Role::App, "is_active", true).unwrap();
-    let prop = node.get_property("rect").unwrap();
-    prop.set_f32(atom, Role::App, 0, 0.).unwrap();
-    prop.set_f32(atom, Role::App, 1, 0.).unwrap();
-    prop.set_f32(atom, Role::App, 2, NETSTATUS_ICON_SIZE).unwrap();
-    prop.set_f32(atom, Role::App, 3, NETSTATUS_ICON_SIZE).unwrap();
-
-    let sg_root = app.sg_root.clone();
-    let settings = move || {
-        info!(target: "app::chat", "clicked settings");
-        let atom = &mut PropertyAtomicGuard::new();
-
-        // Hide all relevant window children nodes
-        // Messy.
-        //
-        // Some suggestions:
-        //  1. Something closer to a router, that would be a accessible globally,
-        //  which essentially holds a vector of references to SceneNodes
-        //  representing the app navigation history.
-        //  When the user changes the route, it would make invisible (or later remove
-        //  elements from the tree for optimization purposes) the node of the last SceneNodes
-        //  in the vector and all its children, recursively;
-        //  and append a new SceneNode pointer, which is the new "route" chosen by the user,
-        //  and draw it and its children recursively.
-        //  Note that this would implicitly handle nested routes (like
-        //  /window/somewhere1/view1 to /window/somewhere1/view2, if the last element of the
-        //  router currently points to view1 and we call router.goto("./view2")).
-        //
-        //  2. Support of wildcard in lookups in .get_children() or another method, like this "*_chat_layer".
-        let windows = sg_root.lookup_node("/window/content/chat").unwrap().get_children();
-        let target_substrings = vec!["_chat_layer", "menu_layer"];
-        for node in windows.iter() {
-            if target_substrings.iter().any(|&s| node.name.contains(s)) {
-                if let Err(e) = node.set_property_bool(atom, Role::App, "is_visible", false) {
-                    debug!("Failed to set property 'is_visible' on node: {:?}", e);
-                }
-            }
-        }
-
-        // Show settings
-        let settings_node = sg_root.lookup_node("/window/content/settings_layer").unwrap();
-        settings_node.set_property_bool(atom, Role::App, "is_visible", true).unwrap();
-    };
-
-    let (slot, recvr) = Slot::new("settings_clicked");
-    node.register("click", slot).unwrap();
-    let settings2 = settings.clone();
-    let listen_click = app.ex.spawn(async move {
-        while let Ok(_) = recvr.recv().await {
-            settings2();
-        }
-    });
-    app.tasks.lock().unwrap().push(listen_click);
-
-    let node = node.setup(|me| Button::new(me, app.renderer.clone(), app.redraw_trigger.clone())).await;
-    settingslayer_node.link(node);
-    */
-
-    let emoji_meshes = emoji_picker::EmojiMeshes::new(app.renderer.clone(), EMOJI_PICKER_ICON_SIZE);
-
-    let emoji_meshes2 = emoji_meshes.clone();
-    let _ = std::thread::spawn(move || {
-        for i in (0..500).step_by(20) {
-            for j in i..(i + 20) {
-                emoji_meshes2.lock().get(j);
-            }
-        }
-    });
-
-    let channels_tree = db.open_tree_default("channels").expect("cannot open channels tree");
-    let contacts_tree = db.open_tree_default("contacts").expect("cannot open contacts tree");
-
-    // Initialize default channels if tree is empty
-    if channels_tree.is_empty().unwrap() {
-        for channel_name in DEFAULT_CHANNELS {
-            let channel = Channel { name: channel_name.to_string(), secret: None };
-            let mut val = vec![];
-            channel.encode(&mut val).unwrap();
-            channels_tree.insert(channel_name.to_string().as_bytes(), &val).unwrap();
-        }
-    }
-
-    // Seed the joined-channels file with defaults on first run.
-    ensure_joined_channels_seeded();
-
     menu::make(
         app,
-        content.clone(),
+        chat_layer.clone(),
         i18n_fish,
         channels_tree.clone(),
         contacts_tree.clone(),
@@ -842,7 +860,6 @@ pub async fn make(app: &App, window: SceneNodePtr, i18n_fish: &I18nBabelFish, db
         emoji_meshes.clone(),
     )
     .await;
-    let chat_layer = app.sg_root.lookup_node("/window/content/chat").unwrap();
 
     // Create chat layers only for joined channels/contacts, in joined order.
     for name in read_joined_channels() {
@@ -870,11 +887,11 @@ pub async fn make(app: &App, window: SceneNodePtr, i18n_fish: &I18nBabelFish, db
         )
         .await;
     }
+
     wallet::make(app, content.clone(), i18n_fish).await;
 
     // Setup wallet button after wallet layer is created
-    let menu_layer = app.sg_root.lookup_node("/window/content/chat/menu_layer").unwrap();
-    menu::setup_wallet_button(app, menu_layer, i18n_fish).await;
+    menu::setup_wallet_button(app, chat_layer, i18n_fish).await;
 
     // @@@ Debug stuff @@@
     //let chatview_node = app.sg_root.lookup_node("/window/content/chat/dev_chat_layer").unwrap();

+ 1 - 1
bin/app/src/app/schema/wallet/main.rs

@@ -493,7 +493,7 @@ async fn create_chat_btn(
     let sg_root = app.sg_root.clone();
     let redraw = app.redraw_trigger.clone();
     let menu_is_visible = PropertyBool::wrap(
-        &sg_root.lookup_node("/window/content/chat/menu_layer").unwrap(),
+        &sg_root.lookup_node("/window/content/chat").unwrap(),
         Role::App,
         "is_visible",
         0,

+ 13 - 3
bin/app/src/sfx.rs

@@ -30,6 +30,7 @@ macro_rules! e { ($($arg:tt)*) => { error!(target: "app::sfx", $($arg)*); } }
 
 static CLICK_OGA: &[u8] = include_bytes!("../data/sfx/click.oga");
 static COMMUP_OGA: &[u8] = include_bytes!("../data/sfx/commup.oga");
+static CLOAK_OGA: &[u8] = include_bytes!("../data/sfx/cloak.oga");
 
 struct Sfx {
     /// Keeps the output stream alive for the process lifetime
@@ -40,6 +41,8 @@ struct Sfx {
     click: rodio::buffer::SamplesBuffer,
     /// Decoded startup sound
     commup: rodio::buffer::SamplesBuffer,
+    /// Used for showing p2p overlay
+    cloak: rodio::buffer::SamplesBuffer,
 }
 
 static SFX: LazyLock<Option<Sfx>> = LazyLock::new(|| match init() {
@@ -58,7 +61,8 @@ fn init() -> Result<Sfx, Box<dyn std::error::Error>> {
     let mixer = output.mixer().clone();
     let click = rodio::Decoder::try_from(Cursor::new(CLICK_OGA))?.record();
     let commup = rodio::Decoder::try_from(Cursor::new(COMMUP_OGA))?.record();
-    Ok(Sfx { _output: output, mixer, click, commup })
+    let cloak = rodio::Decoder::try_from(Cursor::new(CLOAK_OGA))?.record();
+    Ok(Sfx { _output: output, mixer, click, commup, cloak })
 }
 
 /// cpal's AAudio backend fetches the JavaVM and Activity from the
@@ -79,13 +83,19 @@ fn init_android_context() {
 }
 
 pub fn play_click() {
-    if let Some(sfx) = SFX.as_ref() {
+    if let Some(sfx) = &*SFX {
         sfx.mixer.add(sfx.click.clone());
     }
 }
 
 pub fn play_commup() {
-    if let Some(sfx) = SFX.as_ref() {
+    if let Some(sfx) = &*SFX {
         sfx.mixer.add(sfx.commup.clone());
     }
 }
+
+pub fn play_cloak() {
+    if let Some(sfx) = &*SFX {
+        sfx.mixer.add(sfx.cloak.clone());
+    }
+}

+ 5 - 5
bin/app/src/text/mod.rs

@@ -42,14 +42,14 @@ pub static GLOBAL_FONT_CTX: LazyLock<parley::FontContext> = LazyLock::new(|| {
         source_cache: SourceCache::new(SourceCacheOptions { shared: true }),
     };
 
-    let font_data = include_bytes!("../../ibm-plex-mono-regular.otf") as &[u8];
+    let font_data = include_bytes!("../../data/font/ibm-plex-mono-regular.otf") as &[u8];
     font_ctx.collection.register_fonts(peniko::Blob::new(Arc::new(font_data)), None);
 
-    let font_data = include_bytes!("../../NotoColorEmoji.ttf") as &[u8];
+    let font_data = include_bytes!("../../data/font/NotoColorEmoji.ttf") as &[u8];
     font_ctx.collection.register_fonts(peniko::Blob::new(Arc::new(font_data)), None);
 
-    //let font_data = include_bytes!("../../darkfi-custom-emoji.ttf") as &[u8];
-    //font_ctx.collection.register_fonts(peniko::Blob::new(Arc::new(font_data)), None);
+    let font_data = include_bytes!("../../data/font/darkfi-custom-emoji.ttf") as &[u8];
+    font_ctx.collection.register_fonts(peniko::Blob::new(Arc::new(font_data)), None);
 
     font_ctx
 });
@@ -62,7 +62,7 @@ thread_local! {
 const FONT_STACK: &[parley::FontFamilyName<'_>] = &[
     parley::FontFamilyName::named("IBM Plex Mono"),
     parley::FontFamilyName::named("Noto Color Emoji"),
-    //parley::FontFamilyName::named("DarkIRC Emoji"),
+    parley::FontFamilyName::named("DarkIRC Emoji"),
 ];
 
 /// A parley layout paired with the window scale it was built with.