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

Open
wants to merge 19 commits into
base: main
Choose a base branch
from
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.
156 changes: 146 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_badge::*;

ICON_HOME = dep("crate://self/resources/icons/home.svg")
ICON_SETTINGS = dep("crate://self/resources/icons/settings.svg")
Expand All @@ -16,21 +22,26 @@ 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);

let c = self.rect_size * 0.5;
sdf.circle(c.x, c.x, c.x)

let r = self.rect_size * 0.38;

sdf.circle(c.x, c.x, r.x);
sdf.fill_keep(self.background_color);
return sdf.result
}
Expand All @@ -46,6 +57,20 @@ 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 = <VerificationNotice> { }
}

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

<Home> {}

<Filler> {}

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

<Profile> {}

<Filler> {}

<Home> {}

<Filler> {}

<Settings> {}

<Filler> {}
}
}
}
struct VerificationNoticeText {
yes: &'static str,
no: &'static str,
unk: &'static str,
}

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

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

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 = self.tooltip(id!(verification_notice));

if self.view(id!(verification_icon)).area().rect(cx).contains(e.abs) {
let text = match self.verification_state {
VerificationState::Unknown => self.verification_notice_text.unk,
VerificationState::Unverified => self.verification_notice_text.no,
VerificationState::Verified => self.verification_notice_text.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.show_with_options(cx, DVec2 {x: 65., y: 23.}, text);
}
else {
verification_notice.apply_over(cx, live!{
content: {

// Via setting suitable align & padding,
// we can simulate a relative position to make the tootip follow widget `Profile (U)`,
// this is not a perfect solution.
// TODO: Find a way to follow widget `Profile (U)` more precisely.
align: { x: 0.43, y: 1. }
padding: { left: 30., bottom: 31. }
}
});
verification_notice.show_with_options(cx, DVec2 {x: 0., y: 0.}, text);
}
}
//Hide it if cursor is not hovering.
else {
verification_notice.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);
aaravlu marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
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_badge;

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_badge::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
93 changes: 93 additions & 0 deletions src/shared/verification_badge.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
use makepad_widgets::*;

live_design! {
import makepad_widgets::base::*;
import makepad_widgets::theme_desktop_dark::*;
import makepad_draw::shader::std::*;

import crate::shared::styles::*;

VERIFICATION_YES = dep("crate://self/resources/icons/verification_yes.svg")
VERIFICATION_NO = dep("crate://self/resources/icons/verification_no.svg")
VERIFICATION_UNK = dep("crate://self/resources/icons/verification_unk.svg")

VerificationIcon = <Icon> {
icon_walk: { width: 23 }
}
IconYes = <View> {
visible: false
width: 31, height: 31
<VerificationIcon> {
draw_icon: {
svg_file: (VERIFICATION_YES),
fn get_color(self) -> vec4 {
return #x00BF00;
}
}
}
}
IconNo = <View> {
visible: false
width: 31, height: 31
<VerificationIcon> {
draw_icon: {
svg_file: (VERIFICATION_NO),
fn get_color(self) -> vec4 {
return #xBF0000;
}
}
}
}
IconUnk = <View> {
visible: false
width: 31, height: 31
<VerificationIcon> {
draw_icon: {
svg_file: (VERIFICATION_UNK),
fn get_color(self) -> vec4 {
return #x333333;
}
}
}
}

VerificationNotice = <TooltipBase> {
width: Fill, height: Fill,
flow: Overlay

draw_bg: {
fn pixel(self) -> vec4 {
return vec4(0., 0., 0., 0.0)
}
}

content: <View> {
flow: Overlay

//The 'Fill' allows it shows anywhere we want over the app screen,
//our goal is to set the global relative position to make it an illusion of following the cursor.
width: Fill, height: Fill

<RoundedView> {
width: Fit, height: Fit,
padding: 7,

draw_bg: {
color: (COLOR_TOOLTIP_BG),
border_width: 1.0,
border_color: #000000,
radius: 2.5
}

tooltip_label = <Label> {
width: 230
draw_text: {
text_style: <THEME_FONT_REGULAR>{font_size: SMALL_STATE_FONT_SIZE},
text_wrap: Word,
color: #000
}
}
}
}
}
}
Loading
Loading