Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Display verification status as a badge atop the user profile icon #244

Merged
merged 19 commits into from
Dec 18, 2024
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -215,4 +215,4 @@ collapsible_else_if = "allow"
too_many_arguments = "allow"
blocks_in_conditions = "allow"
used_underscore_binding = "allow"
module_name_repetitions = "allow"
module_name_repetitions = "allow"
4 changes: 4 additions & 0 deletions resources/icons/verification_no.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 18 additions & 0 deletions resources/icons/verification_unk.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions resources/icons/verification_yes.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
153 changes: 143 additions & 10 deletions src/home/spaces_dock.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
use makepad_widgets::*;
use matrix_sdk::encryption::VerificationState;

use crate::shared::adaptive_view::DisplayContext;
use crate::sliding_sync::get_client;
use crate::verification::VerificationStateAction;

live_design! {
import makepad_widgets::base::*;
Expand All @@ -8,6 +13,7 @@ live_design! {
import crate::shared::styles::*;
import crate::shared::helpers::*;
import crate::shared::adaptive_view::AdaptiveView;
import crate::shared::verification::*;

ICON_HOME = dep("crate://self/resources/icons/home.svg")
ICON_SETTINGS = dep("crate://self/resources/icons/settings.svg")
Expand All @@ -16,21 +22,30 @@ live_design! {
height: Fill, width: Fill
}

Profile = <View> {
Profile = {{Profile}} {
flow: Overlay
width: Fit, height: Fit
align: { x: 0.5, y: 0.5 }

text_view = <View> {
width: 45., height: 45.,
flow: Overlay
width: 60, height: 60,
align: { x: 0.5, y: 0.5 }
show_bg: true,

draw_bg: {
instance background_color: (COLOR_AVATAR_BG_IDLE),
fn pixel(self) -> vec4 {
let sdf = Sdf2d::viewport(self.pos * self.rect_size);

//The center of the circle we are going to draw.
let c = self.rect_size * 0.5;
sdf.circle(c.x, c.x, c.x)

//The radius of the circle
let r = self.rect_size * 0.38;

//We just keeping the center position of sdf fixed, which is equal to center of the cisrcle,
//while reducing the circle's radius.
sdf.circle(c.x, c.x, r.x);
sdf.fill_keep(self.background_color);
return sdf.result
}
Expand All @@ -46,6 +61,21 @@ live_design! {
text: "U"
}
}
<View> {
align: { x: 1.0, y: 0.0 }

verification_icon = <View> {
flow: Overlay
align:{ x: 0.5, y: 0.5 }
width: 31, height: 31

icon_yes = <IconYes> {}
icon_no = <IconNo> {}
icon_unk = <IconUnk> {}
}
}
verification_notice_desktop = <VerificationNoticeDesktop> { }
verification_notice_mobile = <VerificationNoticeMobile> { }
}

Separator = <LineH> {
Expand Down Expand Up @@ -118,7 +148,7 @@ live_design! {
<Separator> {}

<Home> {}

<Filler> {}

<Settings> {}
Expand All @@ -137,16 +167,119 @@ live_design! {
<Filler> {}

<Profile> {}

<Filler> {}

<Home> {}

<Filler> {}

<Settings> {}

<Filler> {}
}
}
}
struct VerificationNotice {
yes: String,
no: String,
unk: String,
}

impl Default for VerificationNotice{
fn default() -> Self {
Self {
yes: String::from("This device is fully verified."),
no: String::from("This device is unverified. To view your encrypted message history, please verify it from another client."),
unk: String::from("Verification state is unknown."),
}
}
}

#[derive(Live, Widget)]
pub struct Profile {
#[deref]
view: View,
#[rust(VerificationState::Unknown)]
verification_state: VerificationState,
#[rust]
verification_notice: VerificationNotice,
}

impl Profile {
fn set_verification_icon_visibility(&self) {
let (yes_visible, no_visible, unk_visible) = match self.verification_state {
VerificationState::Unknown => (false, false, true),
VerificationState::Unverified => (false, true, false),
VerificationState::Verified => (true, false, false),
};

self.view(id!(icon_yes)).set_visible(yes_visible);
self.view(id!(icon_no)).set_visible(no_visible);
self.view(id!(icon_unk)).set_visible(unk_visible);
}
}

impl Widget for Profile {
fn handle_event(&mut self, cx: &mut Cx, event: &Event, scope: &mut Scope) {
if let Event::MouseMove(e) = event {
let mut verification_notice_desktop = self.tooltip(id!(verification_notice_desktop));
let mut verification_notice_mobile = self.tooltip(id!(verification_notice_mobile));

if self.view(id!(verification_icon)).area().rect(cx).contains(e.abs) {
let text = match self.verification_state {
VerificationState::Unknown => &self.verification_notice.unk,
VerificationState::Unverified => &self.verification_notice.no,
VerificationState::Verified => &self.verification_notice.yes
};

//Determine if it's a desktop or mobile layout,
//then we set the relative position so that the tooltip looks like following the cursor.
if cx.get_global::<DisplayContext>().is_desktop() {
verification_notice_desktop.show_with_options(cx, DVec2 {x: 65., y: 23.}, text);
}
else {
verification_notice_mobile.set_text(text);
verification_notice_mobile.show(cx);
}
}
//Hide it if cursor is not hovering.
else {
verification_notice_desktop.hide(cx);
verification_notice_mobile.hide(cx)
}
}

self.match_event(cx, event);
self.view.handle_event(cx, event, scope)
}

fn draw_walk(&mut self, cx: &mut Cx2d, scope: &mut Scope, walk: Walk) -> DrawStep {
self.view.draw_walk(cx, scope, walk)
}
}

impl MatchEvent for Profile {
fn handle_action(&mut self, cx: &mut Cx, action:&Action) {
if let Some(VerificationStateAction::Update(state)) = action.downcast_ref() {
if self.verification_state != *state {
self.verification_state = *state;

self.set_verification_icon_visibility();
self.redraw(cx);
}
}
}
}

impl LiveHook for Profile {
fn after_new_from_doc(&mut self, cx:&mut Cx) {
if let Some(client) = get_client() {
let current_verification_state = client.encryption().verification_state().get();
self.verification_state = current_verification_state;

self.set_verification_icon_visibility();
self.redraw(cx);
}
}
}
2 changes: 2 additions & 0 deletions src/shared/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ pub mod search_bar;
pub mod styles;
pub mod text_or_image;
pub mod typing_animation;
pub mod verification;

pub fn live_design(cx: &mut Cx) {
// Order matters here, as some widget definitions depend on others.
Expand All @@ -25,4 +26,5 @@ pub fn live_design(cx: &mut Cx) {
adaptive_view::live_design(cx);
typing_animation::live_design(cx);
jump_to_bottom_button::live_design(cx);
verification::live_design(cx);
}
2 changes: 2 additions & 0 deletions src/shared/styles.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ live_design! {
COLOR_AVATAR_BG = #52b2ac
COLOR_AVATAR_BG_IDLE = #d8d8d8

COLOR_TOOLTIP_BG = (COLOR_SECONDARY)

COLOR_UNREAD_MESSAGE_BADGE = (COLOR_AVATAR_BG)

COLOR_TEXT_IDLE = #d8d8d8
Expand Down
Loading
Loading