From a829b1c2fe27ad120d9c26565ab14cef18eaf6ed Mon Sep 17 00:00:00 2001 From: jenniferhmartinezmejia-netizen Date: Sat, 27 Jun 2026 15:15:45 -0500 Subject: [PATCH 1/4] improved layout of verification modal --- src/verification_modal.rs | 135 +++++++++++++++++++++++++++++++------- 1 file changed, 111 insertions(+), 24 deletions(-) diff --git a/src/verification_modal.rs b/src/verification_modal.rs index 70f010d91..6e48354cb 100644 --- a/src/verification_modal.rs +++ b/src/verification_modal.rs @@ -10,6 +10,32 @@ script_mod! { use mod.widgets.* + mod.widgets.VerificationEmojiCell = View { + width: Fit, height: Fit + flow: Down + align: Align{x: 0.5} + padding: Inset{top: 6, right: 6, bottom: 6, left: 6} + spacing: 4 + + symbol := Label { + width: Fit, height: Fit + align: Align{x: 0.5} + draw_text +: { + text_style: REGULAR_TEXT {font_size: 30}, + color: #000 + } + } + description := Label { + width: Fit{max: FitBound.Abs(72.0)}, height: Fit + flow: Flow.Right{wrap: true} + align: Align{x: 0.5} + draw_text +: { + text_style: REGULAR_TEXT {font_size: 9}, + color: #000 + } + } + } + mod.widgets.VerificationModal = set_type_default() do #(VerificationModal::register_widget(vm)) { ..mod.widgets.SmallModal @@ -19,23 +45,46 @@ script_mod! { body := ModalBody {} + // SAS V1 always produces exactly 7 emojis, so 7 cells are declared up front. + emojis_view := View { + width: Fill, height: Fit + flow: Flow.Right{wrap: true} + align: Align{x: 0.5} + spacing: 10 + margin: Inset{top: 15, bottom: 5} + visible: false + + emoji0 := mod.widgets.VerificationEmojiCell {} + emoji1 := mod.widgets.VerificationEmojiCell {} + emoji2 := mod.widgets.VerificationEmojiCell {} + emoji3 := mod.widgets.VerificationEmojiCell {} + emoji4 := mod.widgets.VerificationEmojiCell {} + emoji5 := mod.widgets.VerificationEmojiCell {} + emoji6 := mod.widgets.VerificationEmojiCell {} + } + + question := ModalBody { + margin: Inset{top: 10} + visible: false + } + buttons_view := ModalButtonsRow { margin: Inset{top: 30} - cancel_button := RobrixNegativeIconButton { + accept_button := RobrixPositiveIconButton { align: Align{x: 0.5, y: 0.5} padding: 15, - draw_icon.svg: (ICON_FORBIDDEN) + draw_icon.svg: (ICON_CHECKMARK) icon_walk: Walk{width: 16, height: 16, margin: Inset{left: -2, right: -1} } - text: "Cancel" + text: "Yes" } - accept_button := RobrixPositiveIconButton { + cancel_button := RobrixNegativeIconButton { align: Align{x: 0.5, y: 0.5} padding: 15, - draw_icon.svg: (ICON_CHECKMARK) + draw_icon.svg: (ICON_FORBIDDEN) icon_walk: Walk{width: 16, height: 16, margin: Inset{left: -2, right: -1} } - text: "Yes" + text: "Cancel" } } } @@ -111,6 +160,11 @@ impl WidgetMatchEvent for VerificationModal { // Outgoing verification requests start with the accept button hidden // since we're still in the waiting state then, so show it now accept_button.set_visible(cx, true); + // The emoji grid and its question prompt are only relevant during + // the `KeysExchanged` emoji step; hide them by default and let that + // branch re-show them, so they never leak into other states. + self.view.view(cx, ids!(emojis_view)).set_visible(cx, false); + self.label(cx, ids!(question)).set_visible(cx, false); match verification_action { VerificationAction::RequestCancelled(cancel_info) => { self.label(cx, ids!(body)).set_text( @@ -193,29 +247,33 @@ impl WidgetMatchEvent for VerificationModal { } VerificationAction::KeysExchanged { emojis, decimals } => { - let text = if let Some(emoji_list) = emojis { - format!( - "Keys have been exchanged. Please verify the following emoji:\ - \n {}\n\n\ - Do these emoji keys match?", - emoji_list.emojis - .iter() - .map(|em| format!("{} ({})", em.symbol, em.description)) - .collect::>() - .join("\n ") - ) + if let Some(emoji_list) = emojis { + self.label(cx, ids!(body)).set_text( + cx, + "Keys have been exchanged. Please verify the following emoji:", + ); + // SAS V1 always yields 7 emojis. + for index in 0 .. 7 { + let content = emoji_list.emojis + .get(index) + .map(|em| (em.symbol, em.description)); + self.populate_emoji_cell(cx, index, content); + } + self.view.view(cx, ids!(emojis_view)).set_visible(cx, true); + self.label(cx, ids!(question)).set_text(cx, "Do these emoji keys match?"); + self.label(cx, ids!(question)).set_visible(cx, true); } else { - format!( + let text = format!( "Keys have been exchanged. Please verify the following numbers:\n\ \n {}\n {}\n {}\n\n\ Do these number keys match?", decimals.0, decimals.1, decimals.2, - ) - }; - self.label(cx, ids!(body)).set_text(cx, &text); + ); + self.label(cx, ids!(body)).set_text(cx, &text); + } accept_button.set_enabled(cx, true); - accept_button.set_text(cx, "Yes"); - cancel_button.set_text(cx, "No"); + accept_button.set_text(cx, "They match"); + cancel_button.set_text(cx, "They don't match"); cancel_button.set_enabled(cx, true); cancel_button.set_visible(cx, true); } @@ -245,7 +303,12 @@ impl WidgetMatchEvent for VerificationModal { } VerificationAction::RequestCompleted => { - self.label(cx, ids!(body)).set_text(cx, "Verification completed successfully!"); + self.label(cx, ids!(body)).set_text( + cx, + "Verification completed successfully! \ + Now you can read or send messages securely, and anyone you chat \ + with can also trust this device.", + ); accept_button.set_text(cx, "Ok"); accept_button.set_enabled(cx, true); cancel_button.set_visible(cx, false); @@ -270,6 +333,26 @@ impl VerificationModal { self.is_final = false; } + fn populate_emoji_cell(&mut self, cx: &mut Cx, index: usize, content: Option<(&str, &str)>) { + let (symbol_text, description_text, visible) = match content { + Some((symbol, description)) => (symbol, description, true), + None => ("", "", false), + }; + let cell = match index { + 0 => self.view.view(cx, ids!(emoji0)), + 1 => self.view.view(cx, ids!(emoji1)), + 2 => self.view.view(cx, ids!(emoji2)), + 3 => self.view.view(cx, ids!(emoji3)), + 4 => self.view.view(cx, ids!(emoji4)), + 5 => self.view.view(cx, ids!(emoji5)), + 6 => self.view.view(cx, ids!(emoji6)), + _ => return, + }; + cell.label(cx, ids!(symbol)).set_text(cx, symbol_text); + cell.label(cx, ids!(description)).set_text(cx, description_text); + cell.set_visible(cx, visible); + } + fn initialize_with_data( &mut self, cx: &mut Cx, @@ -296,6 +379,10 @@ impl VerificationModal { ).into() }; self.label(cx, ids!(body)).set_text(cx, &prompt_text); + // Ensure the emoji grid from any prior verification is not shown + // on the initial prompt screen. + self.view.view(cx, ids!(emojis_view)).set_visible(cx, false); + self.label(cx, ids!(question)).set_visible(cx, false); let accept_button = self.button(cx, ids!(accept_button)); let cancel_button = self.button(cx, ids!(cancel_button)); From e59bbbd2f760a5ae018fb3745144b230b38b686c Mon Sep 17 00:00:00 2001 From: jenniferhmartinezmejia-netizen Date: Sun, 5 Jul 2026 12:25:26 -0500 Subject: [PATCH 2/4] keep only the emoji layout change --- src/verification_modal.rs | 59 ++++++++++++++------------------------- 1 file changed, 21 insertions(+), 38 deletions(-) diff --git a/src/verification_modal.rs b/src/verification_modal.rs index 6e48354cb..ac1fe7fe7 100644 --- a/src/verification_modal.rs +++ b/src/verification_modal.rs @@ -63,28 +63,23 @@ script_mod! { emoji6 := mod.widgets.VerificationEmojiCell {} } - question := ModalBody { - margin: Inset{top: 10} - visible: false - } - buttons_view := ModalButtonsRow { margin: Inset{top: 30} - accept_button := RobrixPositiveIconButton { + cancel_button := RobrixNegativeIconButton { align: Align{x: 0.5, y: 0.5} padding: 15, - draw_icon.svg: (ICON_CHECKMARK) + draw_icon.svg: (ICON_FORBIDDEN) icon_walk: Walk{width: 16, height: 16, margin: Inset{left: -2, right: -1} } - text: "Yes" + text: "Cancel" } - cancel_button := RobrixNegativeIconButton { + accept_button := RobrixPositiveIconButton { align: Align{x: 0.5, y: 0.5} padding: 15, - draw_icon.svg: (ICON_FORBIDDEN) + draw_icon.svg: (ICON_CHECKMARK) icon_walk: Walk{width: 16, height: 16, margin: Inset{left: -2, right: -1} } - text: "Cancel" + text: "Yes" } } } @@ -160,11 +155,10 @@ impl WidgetMatchEvent for VerificationModal { // Outgoing verification requests start with the accept button hidden // since we're still in the waiting state then, so show it now accept_button.set_visible(cx, true); - // The emoji grid and its question prompt are only relevant during - // the `KeysExchanged` emoji step; hide them by default and let that - // branch re-show them, so they never leak into other states. + // The emoji grid is only relevant during the `KeysExchanged` emoji + // step; hide it by default and let that branch re-show it, so it + // never leaks into other states. self.view.view(cx, ids!(emojis_view)).set_visible(cx, false); - self.label(cx, ids!(question)).set_visible(cx, false); match verification_action { VerificationAction::RequestCancelled(cancel_info) => { self.label(cx, ids!(body)).set_text( @@ -250,18 +244,13 @@ impl WidgetMatchEvent for VerificationModal { if let Some(emoji_list) = emojis { self.label(cx, ids!(body)).set_text( cx, - "Keys have been exchanged. Please verify the following emoji:", + "Keys have been exchanged. Please verify the following emoji:\n\n\ + Do these emoji keys match?", ); - // SAS V1 always yields 7 emojis. - for index in 0 .. 7 { - let content = emoji_list.emojis - .get(index) - .map(|em| (em.symbol, em.description)); - self.populate_emoji_cell(cx, index, content); + for (index, emoji) in emoji_list.emojis.iter().enumerate() { + self.populate_emoji_cell(cx, index, emoji.symbol, emoji.description); } self.view.view(cx, ids!(emojis_view)).set_visible(cx, true); - self.label(cx, ids!(question)).set_text(cx, "Do these emoji keys match?"); - self.label(cx, ids!(question)).set_visible(cx, true); } else { let text = format!( "Keys have been exchanged. Please verify the following numbers:\n\ @@ -272,8 +261,8 @@ impl WidgetMatchEvent for VerificationModal { self.label(cx, ids!(body)).set_text(cx, &text); } accept_button.set_enabled(cx, true); - accept_button.set_text(cx, "They match"); - cancel_button.set_text(cx, "They don't match"); + accept_button.set_text(cx, "Yes"); + cancel_button.set_text(cx, "No"); cancel_button.set_enabled(cx, true); cancel_button.set_visible(cx, true); } @@ -305,9 +294,9 @@ impl WidgetMatchEvent for VerificationModal { VerificationAction::RequestCompleted => { self.label(cx, ids!(body)).set_text( cx, - "Verification completed successfully! \ - Now you can read or send messages securely, and anyone you chat \ - with can also trust this device.", + "Verification completed successfully!\n\ + Now you can send and receive encrypted messages, \ + and everyone you chat with can trust this device is yours.", ); accept_button.set_text(cx, "Ok"); accept_button.set_enabled(cx, true); @@ -333,11 +322,7 @@ impl VerificationModal { self.is_final = false; } - fn populate_emoji_cell(&mut self, cx: &mut Cx, index: usize, content: Option<(&str, &str)>) { - let (symbol_text, description_text, visible) = match content { - Some((symbol, description)) => (symbol, description, true), - None => ("", "", false), - }; + fn populate_emoji_cell(&mut self, cx: &mut Cx, index: usize, symbol: &str, description: &str) { let cell = match index { 0 => self.view.view(cx, ids!(emoji0)), 1 => self.view.view(cx, ids!(emoji1)), @@ -348,9 +333,8 @@ impl VerificationModal { 6 => self.view.view(cx, ids!(emoji6)), _ => return, }; - cell.label(cx, ids!(symbol)).set_text(cx, symbol_text); - cell.label(cx, ids!(description)).set_text(cx, description_text); - cell.set_visible(cx, visible); + cell.label(cx, ids!(symbol)).set_text(cx, symbol); + cell.label(cx, ids!(description)).set_text(cx, description); } fn initialize_with_data( @@ -382,7 +366,6 @@ impl VerificationModal { // Ensure the emoji grid from any prior verification is not shown // on the initial prompt screen. self.view.view(cx, ids!(emojis_view)).set_visible(cx, false); - self.label(cx, ids!(question)).set_visible(cx, false); let accept_button = self.button(cx, ids!(accept_button)); let cancel_button = self.button(cx, ids!(cancel_button)); From 2e317ffb9ae12e96699c05dcade990069192deac Mon Sep 17 00:00:00 2001 From: Kevin Boos Date: Thu, 6 Aug 2026 15:52:51 -0700 Subject: [PATCH 3/4] address my review comments, clean up a bit --- src/verification_modal.rs | 45 +++++++++++++++++---------------------- 1 file changed, 19 insertions(+), 26 deletions(-) diff --git a/src/verification_modal.rs b/src/verification_modal.rs index ac1fe7fe7..879fe6f5b 100644 --- a/src/verification_modal.rs +++ b/src/verification_modal.rs @@ -18,7 +18,7 @@ script_mod! { spacing: 4 symbol := Label { - width: Fit, height: Fit + width: 72, height: Fit align: Align{x: 0.5} draw_text +: { text_style: REGULAR_TEXT {font_size: 30}, @@ -26,7 +26,7 @@ script_mod! { } } description := Label { - width: Fit{max: FitBound.Abs(72.0)}, height: Fit + width: 72, height: Fit flow: Flow.Right{wrap: true} align: Align{x: 0.5} draw_text +: { @@ -45,7 +45,7 @@ script_mod! { body := ModalBody {} - // SAS V1 always produces exactly 7 emojis, so 7 cells are declared up front. + // SAS V1 always produces exactly 7 emojis. emojis_view := View { width: Fill, height: Fit flow: Flow.Right{wrap: true} @@ -155,9 +155,7 @@ impl WidgetMatchEvent for VerificationModal { // Outgoing verification requests start with the accept button hidden // since we're still in the waiting state then, so show it now accept_button.set_visible(cx, true); - // The emoji grid is only relevant during the `KeysExchanged` emoji - // step; hide it by default and let that branch re-show it, so it - // never leaks into other states. + // The emoji grid is hidden by default and only shown during emoji verification. self.view.view(cx, ids!(emojis_view)).set_visible(cx, false); match verification_action { VerificationAction::RequestCancelled(cancel_info) => { @@ -245,10 +243,22 @@ impl WidgetMatchEvent for VerificationModal { self.label(cx, ids!(body)).set_text( cx, "Keys have been exchanged. Please verify the following emoji:\n\n\ - Do these emoji keys match?", + Do the emoji names match?", ); - for (index, emoji) in emoji_list.emojis.iter().enumerate() { - self.populate_emoji_cell(cx, index, emoji.symbol, emoji.description); + let emoji_cell_paths: &[&[LiveId]] = ids_array!( + emoji0, + emoji1, + emoji2, + emoji3, + emoji4, + emoji5, + emoji6, + ); + for (emoji, cell) in emoji_list.emojis.iter().zip( + self.view_set(cx, emoji_cell_paths).iter() + ) { + cell.label(cx, ids!(symbol)).set_text(cx, emoji.symbol); + cell.label(cx, ids!(description)).set_text(cx, emoji.description); } self.view.view(cx, ids!(emojis_view)).set_visible(cx, true); } else { @@ -322,21 +332,6 @@ impl VerificationModal { self.is_final = false; } - fn populate_emoji_cell(&mut self, cx: &mut Cx, index: usize, symbol: &str, description: &str) { - let cell = match index { - 0 => self.view.view(cx, ids!(emoji0)), - 1 => self.view.view(cx, ids!(emoji1)), - 2 => self.view.view(cx, ids!(emoji2)), - 3 => self.view.view(cx, ids!(emoji3)), - 4 => self.view.view(cx, ids!(emoji4)), - 5 => self.view.view(cx, ids!(emoji5)), - 6 => self.view.view(cx, ids!(emoji6)), - _ => return, - }; - cell.label(cx, ids!(symbol)).set_text(cx, symbol); - cell.label(cx, ids!(description)).set_text(cx, description); - } - fn initialize_with_data( &mut self, cx: &mut Cx, @@ -363,8 +358,6 @@ impl VerificationModal { ).into() }; self.label(cx, ids!(body)).set_text(cx, &prompt_text); - // Ensure the emoji grid from any prior verification is not shown - // on the initial prompt screen. self.view.view(cx, ids!(emojis_view)).set_visible(cx, false); let accept_button = self.button(cx, ids!(accept_button)); From 4d0f3d54e16988968c94f90536fa1bc1bd9b47da Mon Sep 17 00:00:00 2001 From: Kevin Boos <1139460+kevinaboos@users.noreply.github.com> Date: Thu, 6 Aug 2026 16:03:00 -0700 Subject: [PATCH 4/4] fix spacing / new lines in verification modal message --- src/verification_modal.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/verification_modal.rs b/src/verification_modal.rs index 879fe6f5b..65771fabf 100644 --- a/src/verification_modal.rs +++ b/src/verification_modal.rs @@ -304,8 +304,8 @@ impl WidgetMatchEvent for VerificationModal { VerificationAction::RequestCompleted => { self.label(cx, ids!(body)).set_text( cx, - "Verification completed successfully!\n\ - Now you can send and receive encrypted messages, \ + "Verification completed successfully!\n\n\ + Now you can send and receive encrypted messages,\ and everyone you chat with can trust this device is yours.", ); accept_button.set_text(cx, "Ok");