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

Use interactive buttons to display and send (toggle) reactions #168

Merged
merged 79 commits into from
Jan 20, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
79 commits
Select commit Hold shift + click to select a range
1453a13
emoji
alanpoon Sep 27, 2024
5d13dfc
emo
alanpoon Sep 29, 2024
d77281a
reaction_list
alanpoon Oct 1, 2024
20b4f0d
added emoji toggle
alanpoon Oct 1, 2024
ad3b14d
minor cleanup
alanpoon Oct 1, 2024
632eaa7
Merge branch 'main' into interactive_icon_button_#115
alanpoon Oct 2, 2024
0d0dc06
removed message annotation template
alanpoon Oct 2, 2024
c26719f
Merge branch 'main' into interactive_icon_button_#115
alanpoon Oct 3, 2024
f9bbb9f
resolve conflict
alanpoon Oct 3, 2024
39c7c58
commiting cargo.lock
alanpoon Oct 3, 2024
4f818a3
revert use main's cargo.lock
alanpoon Oct 3, 2024
3f5500c
did some formating
alanpoon Oct 4, 2024
d3a0b29
Merge branch 'main' into interactive_icon_button_#115
alanpoon Oct 11, 2024
0db2482
resolve minor issue
alanpoon Oct 11, 2024
926871d
Merge branch 'main' into interactive_icon_button_#115
alanpoon Oct 12, 2024
e585205
resolve conflict
alanpoon Oct 12, 2024
0279b2e
merge main n resolve conflict
alanpoon Oct 12, 2024
b02c4ce
Merge branch 'main' into interactive_icon_button_#115
alanpoon Oct 30, 2024
bda02cf
Merge branch 'main' of https://github.com/project-robius/robrix into …
alanpoon Nov 27, 2024
867a455
ReactionButton hide before width calculated
alanpoon Nov 28, 2024
d4efb31
Merge branch 'main' of https://github.com/project-robius/robrix into …
alanpoon Nov 28, 2024
48f22b5
resolve conflict
alanpoon Nov 28, 2024
ac13516
removed unneccessary changes
alanpoon Nov 28, 2024
1b31846
remove redraw after set_list
alanpoon Nov 28, 2024
5616b4a
Merge branch 'main' of https://github.com/project-robius/robrix into …
alanpoon Dec 3, 2024
23c74dd
fixed spelling mistake
alanpoon Dec 3, 2024
3e4ae95
fix clippy
alanpoon Dec 3, 2024
af313fb
increase padding to 5
alanpoon Dec 4, 2024
52c2b01
Added tooltip for interactive button
alanpoon Dec 6, 2024
34f1543
fix clippy
alanpoon Dec 6, 2024
d6c2bfd
Added display_name from user profile cache in tooltip
alanpoon Dec 9, 2024
dc1c4ad
changed to map
alanpoon Dec 9, 2024
33a2437
Removed tuple and slight improve in tooltip cutoff
alanpoon Dec 10, 2024
bc19f96
consistent roomscreentooltipActions
alanpoon Dec 10, 2024
70ed74c
minor change
alanpoon Dec 10, 2024
49425bb
Update src/sliding_sync.rs
alanpoon Dec 18, 2024
2be0b94
changing wording for reaction_key
alanpoon Dec 18, 2024
f3bd019
removed clientuser_id in timelinestate
alanpoon Dec 23, 2024
82e5efc
Merge branch 'main' into interactive_icon_button_#115
alanpoon Dec 23, 2024
bf1e2cf
fix tooltip
alanpoon Dec 23, 2024
b369397
Added callout tooltip widget
alanpoon Dec 23, 2024
a8ad340
remove temporary hack for tooltip
alanpoon Dec 23, 2024
21a5313
Added displayname for reaction tooltip
alanpoon Dec 25, 2024
742f1f4
Merge branch 'main' of https://github.com/project-robius/robrix into …
alanpoon Dec 31, 2024
ac2ffcb
fix tooltip overlap
alanpoon Dec 31, 2024
4693920
tooltip cleanup
alanpoon Jan 1, 2025
16f204c
remove Option for callout_y_offset
alanpoon Jan 1, 2025
8238397
Fix Doc
alanpoon Jan 2, 2025
ce504c9
Merge branch 'main' of https://github.com/project-robius/robrix into …
alanpoon Jan 2, 2025
b261b5d
use rightwrap
alanpoon Jan 3, 2025
84d5e51
remove hide for first draw in event_reaction_list
alanpoon Jan 3, 2025
6826070
Merge branch 'main' of https://github.com/project-robius/robrix into …
alanpoon Jan 3, 2025
e195ec5
remove width_calculated
alanpoon Jan 3, 2025
e8efce0
Flow:RightWrap
alanpoon Jan 4, 2025
4ca7219
margin remove parenthese
alanpoon Jan 4, 2025
211b375
format margin
alanpoon Jan 4, 2025
be4d20b
format bg_color
alanpoon Jan 4, 2025
f9709eb
early return
alanpoon Jan 4, 2025
5dcebf3
format long function line
alanpoon Jan 4, 2025
5f85166
simplifed reaction list
alanpoon Jan 7, 2025
7090dc9
restore MatrixRequest::CheckCanUserSendMessage
alanpoon Jan 7, 2025
69a16c4
doc formating
alanpoon Jan 13, 2025
c7a5045
Populate tooltip text after mouseover in reaction buttons
alanpoon Jan 13, 2025
6b54d76
merge main
alanpoon Jan 13, 2025
8bc1470
remove debugging
alanpoon Jan 13, 2025
0997f40
revert changes
alanpoon Jan 13, 2025
f29683c
Merge remote-tracking branch 'origin' into interactive_icon_button_#115
alanpoon Jan 13, 2025
368deff
Change to mouse position detection for message body
alanpoon Jan 13, 2025
c0fddf5
revert handle_event for Message
alanpoon Jan 14, 2025
fbc3ab1
Merge branch 'main' into interactive_icon_button_#115
alanpoon Jan 16, 2025
d99ac95
fix doc comment
alanpoon Jan 16, 2025
0a3118b
Fix half screening reaction button styling
alanpoon Jan 17, 2025
b5f2b93
improving tooltip clearing
alanpoon Jan 17, 2025
b39631f
Merge branch 'main' into interactive_icon_button_#115
alanpoon Jan 17, 2025
05e7d0e
fix edge toggle reaction
alanpoon Jan 17, 2025
f89ef19
Display tooltip at the bottom if the reaction button is too close to …
alanpoon Jan 19, 2025
0229149
align callout to the first line
alanpoon Jan 20, 2025
0fd23c3
Enlarge reaction font size. Left-align reaction buttons.
kevinaboos Jan 20, 2025
6189c44
Merge branch 'main' into interactive_icon_button_#115
kevinaboos Jan 20, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ url = "2.5.0"
emojis = "0.6.1"
bytesize = "1.3.0"
bitflags = "2.6.0"
indexmap = "2.6.0"

###################################################################################################
#### Note: we now enable the usage of `rustls-tls` and the `bundled-sqlite` features in the 2 ####
Expand Down
5 changes: 5 additions & 0 deletions src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,9 @@ impl MatchEvent for App {

impl AppMain for App {
fn handle_event(&mut self, cx: &mut Cx, event: &Event) {
if let Event::WindowGeomChange(window_geom_change_event) = event {
self.app_state.window_geom = Some(window_geom_change_event.new_geom.clone());
}
// Forward events to the MatchEvent trait impl, and then to the App's UI element.
self.match_event(cx, event);
let scope = &mut Scope::with_data(&mut self.app_state);
Expand All @@ -321,6 +324,8 @@ impl App {
pub struct AppState {
pub rooms_panel: RoomsPanelState,
pub logged_in: bool,
/// The current window geometry.
kevinaboos marked this conversation as resolved.
Show resolved Hide resolved
pub window_geom: Option<event::WindowGeom>,
}

#[derive(Default, Debug)]
Expand Down
307 changes: 307 additions & 0 deletions src/home/event_reaction_list.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,307 @@
use crate::sliding_sync::{current_user_id, submit_async_request, MatrixRequest};
use makepad_widgets::*;
use matrix_sdk::ruma::{OwnedRoomId, OwnedUserId};
use matrix_sdk_ui::timeline::{ReactionInfo, ReactionsByKeyBySender, TimelineEventItemId};
use crate::profile::user_profile_cache::get_user_profile_and_room_member;
use crate::home::room_screen::RoomScreenTooltipActions;
use indexmap::IndexMap;

const TOOLTIP_WIDTH: f64 = 200.0;
const EMOJI_BORDER_COLOR_INCLUDE_SELF: Vec4 = Vec4 { x: 0.0, y: 0.6, z: 0.47, w: 1.0 }; // DarkGreen
const EMOJI_BORDER_COLOR_NOT_INCLUDE_SELF: Vec4 = Vec4 { x: 0.714, y: 0.73, z: 0.75, w: 1.0 }; // Grey

const EMOJI_BG_COLOR_INCLUDE_SELF: Vec4 = Vec4 { x: 0.89, y: 0.967, z: 0.929, w: 1.0 }; // LightGreen
const EMOJI_BG_COLOR_NOT_INCLUDE_SELF: Vec4 = Vec4 { x: 0.968, y: 0.976, z: 0.98, w: 1.0 }; // LightGrey

live_design! {
use link::theme::*;
use link::shaders::*;
use link::widgets::*;

use crate::shared::styles::*;
COLOR_BUTTON_GREY = #B6BABF
REACTION_LIST_PADDING_RIGHT = 30.0;
pub ReactionList = {{ReactionList}} {
width: Fill,
height: Fit,
flow: RightWrap,
margin: {top: 5.0}
padding:{
right: (REACTION_LIST_PADDING_RIGHT)
}
item: <Button> {
width: Fit,
height: Fit,
padding: 6,
// Use a zero margin on the left because we want the first reaction
// to be flush with the left edge of the message text.
margin: { top: 3, bottom: 3, left: 0, right: 6 },
draw_bg: {
instance color: (COLOR_BUTTON_GREY)
instance color_hover: #fef65b
instance border_width: 1.5
instance border_color: #001A11
instance radius: 3.0
instance hover: 0.0
fn get_color(self) -> vec4 {
return mix(self.color, mix(self.color, self.color_hover, 0.2), self.hover)
}

fn pixel(self) -> vec4 {
let sdf = Sdf2d::viewport(self.pos * self.rect_size)
sdf.box(
self.border_width,
self.border_width,
self.rect_size.x - self.border_width * 2.0,
self.rect_size.y - self.border_width * 2.0,
max(1.0, self.radius)
)
sdf.fill_keep(self.get_color())
if self.border_width > 0.0 {
//let stroke_color = mix(self.get_color(), self.border_color, 0.2);
sdf.stroke(self.border_color, self.border_width)
}
return sdf.result;
}
}
draw_text: {
text_style: <REGULAR_TEXT>{font_size: 9},
color: #000
fn get_color(self) -> vec4 {
return self.color;
}
}
}
}

}
#[derive(Clone, Debug)]
pub struct ReactionData {
/// Refers to an emoji "shortcode" string, which is a temporary hack
/// because Makepad does not yet support drawing actual emoji.
pub emoji_shortcode: String,
/// Original reaction string from the backend before emoji shortcode conversion.
pub reaction_raw: String,
/// Boolean indicating if the current user is also a sender of this reaction.
pub includes_user: bool,
/// List of all users who have reacted to the emoji.
pub reaction_senders: IndexMap<OwnedUserId, ReactionInfo>,
/// The ID of the room that the reaction is for
pub room_id: OwnedRoomId
}

#[derive(Live, LiveHook, Widget)]
pub struct ReactionList {
#[redraw]
#[rust]
area: Area,
#[live]
item: Option<LivePtr>,
#[rust]
children: Vec<(ButtonRef, ReactionData)>,
#[layout]
layout: Layout,
#[walk]
walk: Walk,
#[rust]
room_id: Option<OwnedRoomId>,
#[rust]
timeline_event_id: Option<TimelineEventItemId>,
}
impl Widget for ReactionList {
fn draw_walk(&mut self, cx: &mut Cx2d, scope: &mut Scope, walk: Walk) -> DrawStep {
cx.begin_turtle(walk, self.layout);
self.children.iter_mut().for_each(|(target, _)| {
let _ = target.draw(cx, scope);
});
cx.end_turtle();
DrawStep::done()
}
fn handle_event(&mut self, cx: &mut Cx, event: &Event, scope: &mut Scope) {
let uid: WidgetUid = self.widget_uid();
let app_state = scope.data.get::<crate::app::AppState>().unwrap();

for (widget_ref, reaction_data) in self.children.iter() {
match event.hits(cx, widget_ref.area()) {
Hit::FingerHoverIn(_) => {
let widget_rect = widget_ref.area().rect(cx);
let mut too_close_to_right = false;
if let Some(window_geom) = &app_state.window_geom {
if (widget_rect.pos.x + widget_rect.size.x) + TOOLTIP_WIDTH > window_geom.inner_size.x {
too_close_to_right = true;
}
}
let tooltip_pos = if too_close_to_right {
DVec2 {
x: widget_rect.pos.x + (widget_rect.size.x - TOOLTIP_WIDTH),
y: widget_rect.pos.y + widget_rect.size.y
}
} else {
DVec2 {
x: widget_rect.pos.x + widget_rect.size.x,
y: widget_rect.pos.y - 5.0
}
};
let callout_offset = if too_close_to_right {
TOOLTIP_WIDTH - (widget_rect.size.x - 5.0) / 2.0
} else {
10.0
};
cx.widget_action(uid, &scope.path, RoomScreenTooltipActions::HoverInReactionButton {
tooltip_pos,
tooltip_width: TOOLTIP_WIDTH,
callout_offset,
reaction_data: reaction_data.clone(),
pointing_up: too_close_to_right,
});
cx.set_cursor(MouseCursor::Hand);
widget_ref.apply_over(cx, live!(draw_bg: {hover: 1.0}));
break;
}
Hit::FingerHoverOut(_) => {
cx.widget_action(uid, &scope.path, RoomScreenTooltipActions::HoverOut);
widget_ref.apply_over(cx, live!(draw_bg: {hover: 0.0}));
cx.set_cursor(MouseCursor::Arrow);
break;
}
Hit::FingerDown(_) => {
let Some(room_id) = &self.room_id else { return };
let Some(timeline_event_id) = &self.timeline_event_id else {
return;
};
submit_async_request(MatrixRequest::ToggleReaction {
room_id: room_id.clone(),
timeline_event_id: timeline_event_id.clone(),
reaction: reaction_data.reaction_raw.clone(),
});
// update the reaction button before the timeline is updated
let (bg_color, border_color) = if !reaction_data.includes_user {
(EMOJI_BG_COLOR_INCLUDE_SELF, EMOJI_BORDER_COLOR_INCLUDE_SELF)
} else {
(EMOJI_BG_COLOR_NOT_INCLUDE_SELF, EMOJI_BORDER_COLOR_NOT_INCLUDE_SELF)
};
widget_ref.apply_over(cx, live! {
draw_bg: { color: (bg_color) , border_color: (border_color) }
});
cx.widget_action(uid, &scope.path, RoomScreenTooltipActions::HoverOut);
cx.set_cursor(MouseCursor::Hand);
break;
},
Hit::FingerUp(_) => {
cx.widget_action(uid, &scope.path, RoomScreenTooltipActions::HoverOut);
cx.set_cursor(MouseCursor::Hand);
break;
}
_ => { }
}
}
if let Event::Scroll(_) = event {
cx.widget_action(uid, &scope.path, RoomScreenTooltipActions::HoverOut);
cx.set_cursor(MouseCursor::Hand);
}
}
}

impl ReactionListRef {
/// Set the list of reactions and their counts to display in the ReactionList widget,
/// along with the room ID and event ID that these reactions are for.
///
/// This will clear any existing list of reactions and replace it with the given one.
///
/// The given `event_tl_item_reactions` is a map from each reaction's raw string (including any variant selectors)
/// to the list of users who have reacted with that reaction.
///
/// The given `room_id` is the ID of the room that these reactions are for.
///
/// The given `timeline_event_item_id` is the ID of the event that these reactions are for.
/// Required by Matrix API
pub fn set_list(
&mut self,
cx: &mut Cx,
event_tl_item_reactions: &ReactionsByKeyBySender,
room_id: OwnedRoomId,
timeline_event_item_id: TimelineEventItemId,
id: usize,
) {
const DRAW_ITEM_ID_REACTION: bool = false;

let Some(client_user_id) = current_user_id() else { return };
let Some(mut inner) = self.borrow_mut() else { return };
if event_tl_item_reactions.is_empty() && !DRAW_ITEM_ID_REACTION {
inner.children.clear();
return;
}
inner.children.clear(); //Inefficient but we don't want to compare the event_tl_item_reactions
for (reaction_raw, reaction_senders) in event_tl_item_reactions.iter() {
// Just take the first char of the emoji, which ignores any variant selectors.
let reaction_first_char = reaction_raw.chars().next().map(|c| c.to_string());
let reaction_str = reaction_first_char.as_deref().unwrap_or(reaction_raw);
let mut includes_user: bool = false;
let emoji_text = emojis::get(reaction_str)
.and_then(|e| e.shortcode())
.unwrap_or(reaction_raw);
for (sender, _) in reaction_senders.iter() {
if sender == &client_user_id {
includes_user = true;
}
// Cache the reaction sender's user profile so that tooltip will show displayable name
let _ = get_user_profile_and_room_member(cx, sender.clone(), &room_id, true);
}
let mut emoji_text = emoji_text.to_string();

// Debugging: draw the item ID as a reaction
if DRAW_ITEM_ID_REACTION {
emoji_text = format!("{emoji_text}\n ID: {}", id);
}
let reaction_data = ReactionData {
reaction_raw: reaction_raw.to_string(),
emoji_shortcode: emoji_text.to_string(),
includes_user,
reaction_senders: reaction_senders.clone(),
room_id: room_id.clone(),
};
let button = WidgetRef::new_from_ptr(cx, inner.item).as_button();
button.set_text(&format!("{} {}", reaction_data.emoji_shortcode, reaction_senders.len()));
let (bg_color, border_color) = if reaction_data.includes_user {
(EMOJI_BG_COLOR_INCLUDE_SELF, EMOJI_BORDER_COLOR_INCLUDE_SELF)
} else {
(EMOJI_BG_COLOR_NOT_INCLUDE_SELF, EMOJI_BORDER_COLOR_NOT_INCLUDE_SELF)
};
button.apply_over(cx, live! {
draw_bg: { color: (bg_color) , border_color: (border_color) }
});
inner.children.push((button, reaction_data));
}
inner.room_id = Some(room_id);
inner.timeline_event_id = Some(timeline_event_item_id);
}


/// Handles hover in action and returns the appropriate `RoomScreenTooltipActions`.
///
/// This function checks if there is a widget action associated with the current
/// widget's unique identifier in the provided `actions`. If an action exists,
/// it is cast to `RoomScreenTooltipActions` and returned. Otherwise, it returns
/// `RoomScreenTooltipActions::None`.
///
/// # Arguments
///
/// * `actions` - A reference to the `Actions` that may contain widget actions
/// relevant to this widget.
pub fn hover_in(&self, actions: &Actions) -> RoomScreenTooltipActions {
if let Some(item) = actions.find_widget_action(self.widget_uid()) {
item.cast()
} else {
RoomScreenTooltipActions::None
}
}
/// Handles widget actions and returns `true` if the hover out action was found in the provided `actions`.
/// Otherwise, returns `false`.
pub fn hover_out(&self, actions: &Actions) -> bool {
if let Some(item) = actions.find_widget_action(self.widget_uid()) {
matches!(item.cast(), RoomScreenTooltipActions::HoverOut)
} else {
false
}
}
}
2 changes: 2 additions & 0 deletions src/home/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ pub mod rooms_list;
pub mod rooms_sidebar;
pub mod spaces_dock;
pub mod welcome_screen;
pub mod event_reaction_list;
pub mod message_context_menu;

pub fn live_design(cx: &mut Cx) {
Expand All @@ -25,5 +26,6 @@ pub fn live_design(cx: &mut Cx) {
spaces_dock::live_design(cx);
welcome_screen::live_design(cx);
light_themed_dock::live_design(cx);
event_reaction_list::live_design(cx);
message_context_menu::live_design(cx);
}
Loading
Loading