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 read receipts next to each message as a row of Avatars for users who have seen that message #162

Open
wants to merge 25 commits into
base: main
Choose a base branch
from

Conversation

alanpoon
Copy link
Contributor

@alanpoon alanpoon commented Sep 26, 2024

Fixes #153

  1. With tooltip indicating number of seen - mouse over
read_r_tooltip
  1. mouse out
read_receipt
  • Currently only supports Avatar with username. I need some advice how to put in Avatar with image.
  • Implemented max display of 4 avatars
  • Currently tooltip seems to be not able customize width and position. @jmbejar

@alanpoon alanpoon marked this pull request as ready for review September 27, 2024 04:20
@alanpoon alanpoon marked this pull request as draft September 28, 2024 12:31
@alanpoon
Copy link
Contributor Author

[WIP] Will try to change sequencer to follow GenUI's way of creating a list of widgets

@alanpoon
Copy link
Contributor Author

avatar read_receipt
  1. Modified set_avatar_and_get_username function to use &mut Avatar instead of AvatarRef
  2. Modified set_avatar_and_get_username function to take in an option of Avatar User profile. Read_receipts do not provide user profile. So the User Profile is gotten from the Sender cache.
  3. Added Caching for Avatar that is loaded with Image.

@alanpoon alanpoon marked this pull request as ready for review October 10, 2024 06:52
Copy link
Member

@kevinaboos kevinaboos left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A great start, thanks!

I suggested some minor API changes that will make things clearer and less error-prone. After those changes are made, it should be ready to merge.

src/shared/avatar.rs Outdated Show resolved Hide resolved
src/home/room_read_receipt.rs Outdated Show resolved Hide resolved
src/home/room_screen.rs Outdated Show resolved Hide resolved
src/home/room_screen.rs Outdated Show resolved Hide resolved
src/profile/user_profile.rs Outdated Show resolved Hide resolved
src/profile/user_profile_cache.rs Outdated Show resolved Hide resolved
src/home/room_screen.rs Outdated Show resolved Hide resolved
src/home/room_screen.rs Outdated Show resolved Hide resolved
src/home/room_screen.rs Outdated Show resolved Hide resolved
@kevinaboos
Copy link
Member

  • Currently only supports Avatar with username. I need some advice how to put in Avatar with image.

Using the existing Avatar widget should work just fine. The reason you never get any proper Avatar profile info is because you always pass None into the set_avatar_and_get_username() function. If you're able to obtain the Avatar image therein (by grabbing it from the cache), then that function should automatically pick it up.

Alternatively, we can create a separate function (or further modify set_avatar_and_get_username()) to utilize the user profile and avatar caches if the passed-in avatar info is None or not-yet available.

alanpoon and others added 7 commits November 4, 2024 23:17
Co-authored-by: Kevin Boos <1139460+kevinaboos@users.noreply.github.com>
Co-authored-by: Kevin Boos <1139460+kevinaboos@users.noreply.github.com>
Co-authored-by: Kevin Boos <1139460+kevinaboos@users.noreply.github.com>
@kevinaboos kevinaboos added the waiting-on-author This issue is waiting on the original author for a response label Nov 7, 2024
@kevinaboos kevinaboos added waiting-on-review This issue is waiting to be reviewed and removed waiting-on-author This issue is waiting on the original author for a response labels Nov 7, 2024
@alanpoon alanpoon self-assigned this Nov 7, 2024
Copy link
Member

@kevinaboos kevinaboos left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for refactoring the code; it does look nicer now.

I'm not sure how thoroughly you tested this, but I experience quite a few bugs or odd behaviors on my machine when testing it.

First, the on-hover tooltip is frequently cut off, so let's reposition that differently.
image

Second, the tooltip also doesn't disappear properly when you hover out of the avatar row:
image

Third, the log is absolutely filled with these messages:

src/sliding_sync.rs:433:21 - Sending get user profile request: ...

which indicates that you're not correctly using the user profile cache, as I see tons of duplicate requests. If you use the cache correctly (and do not re-draw the AvatarRow until a cache update is received), then you should see only one request max per unknown user ID.

Finally, and most importantly, the displayed read receipt info is actually incorrect and/or incomplete. If you scroll through any larger public room (e.g., Element Web/Desktop, Matrix HQ, etc) you'll see a total mismatch between the read receipts displayed on Robrix and the read receipts displayed by Element in the same room. Not sure what you're doing to cause this, but please look into the correctness/accuracy of the read receipts. Perhaps they're not getting updated?

Read receipts also seem to be completely missing for quite a few messages: I see them in Element but not in Robrix. Please investigate; this is probably related to the above issue too.

Finally, address all the compiler warnings that your PR has added (the main branch currently has 5 warnings, which you can ignore).

Comment on lines +300 to +302
pub fn set_avatar_and_get_username(
cx: &mut Cx,
avatar: AvatarRef,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think you missed my point that this should be a method implemented on the Avatar type, with a typical WidgetRef-style wrapper that is implemented for AvatarRef and just calls this inner function. That is the canonical Makepad way of doing things.

Comment on lines +212 to +213
/// Returns Hit event when mouse in
fn hit(&mut self, cx: &mut Cx, event: &Event, sweep_area: Area) -> Hit {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the docs and/or function name here need to be improved, because I can't understand what this function is doing or how I should be using it just by reading the docs alone (which is the goal).

Comment on lines +256 to +257
/// Returns an option of Hit event when mouse in
pub fn hit(&mut self, cx: &mut Cx, event: &Event, sweep_area: Area) -> Option<Hit> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same comment here:
the docs and/or function name here need to be improved, because I can't understand what this function is doing or how I should be using it just by reading the docs alone (which is the goal).

@@ -2577,10 +2593,10 @@ fn draw_replied_to_message(
let (in_reply_to_username, is_avatar_fully_drawn) = set_avatar_and_get_username(
cx,
replied_to_message_view
.avatar(id!(replied_to_message_content.reply_preview_avatar)),
.avatar(id!(replied_to_message_content.reply_preview_avatar)),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the indentation was previously correct, please undo.

fn data_to_widget(&mut self, cx: &mut Cx, nodes: &[LiveNode], path: &[LiveId]) {}
}
impl AvatarRow {
fn set_range(&mut self, cx: &mut Cx, total_num_seen: usize) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still don't understand why we need this function ... see my other review comment within fn set_avatar_row().

Having this as a separate function invites someone to accidentally invoke it without understanding what other steps need to be simultaneously undertaken, i.e., it's easy to mis-use this.

All of this is just a fancy way of saying "use defensive programming techniques", in which you employ a design/implementation strategy that prevents a developer from accidentally making mistakes (yourself included). That is one of Rust's design goals as well.

Comment on lines +161 to +162
pub fn set_avatar_row<'a, T>(&mut self, cx: &mut Cx, room_id: &RoomId, event_id: Option<&EventId>, receipts_len: usize, receipts_iter: T)
where T:Iterator<Item = (&'a OwnedUserId, &'a Receipt)> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this function can be simplified in two ways.

  1. There's no need for generics here. Just directly accept the IndexMap of read receipts, which will also allow you do to number 2 below.
  2. Remove the receipts_len parameter, since that is completely unnecessary and error-prone, as it's separate from (and redundant with) the IndexMap of read receipts.

impl Widget for AvatarRow {
fn handle_event(&mut self, cx: &mut Cx, event: &Event, scope: &mut Scope) {
let uid = self.widget_uid();
for button in self.buttons.iter_mut() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isn't this a very expensive way to check for a hit? Surely we should just be checking the full area of the AvatarRow as a single "whole" entity, instead of checking each button.

Unless, that is, you're going to allow each mini avatar in the AvatarRow to be individually-clickable ... which is fine, but I don't think you're doing that.

for v in self.buttons.iter_mut() {
let _ = v.draw(cx, scope);
}
if self.total_num_seen > 5 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

extract this 5 into a const and place that const at the top of this file. You can name it MAX_VISIBLE_AVATARS_IN_READ_RECEIPT_ROW or something similar.

See the programming rule of no magic numbers.

pub fn set_avatar_row<'a, T>(&mut self, cx: &mut Cx, room_id: &RoomId, event_id: Option<&EventId>, receipts_len: usize, receipts_iter: T)
where T:Iterator<Item = (&'a OwnedUserId, &'a Receipt)> {
if let Some(ref mut inner) = self.borrow_mut() {
inner.set_range(cx, receipts_len);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

set_range should not be a separate function, as that overcomplicates the API. You should directly set the total_num_seen within this set_avatar_row() fn based on the len() of the read receipts IndexMap, as that guarantees that the value will always be correct/accurate.

@@ -419,7 +420,7 @@ live_design! {

message_annotations = <MessageAnnotations> {}
}

avatar_row = <AvatarRow> {width: 40, height: 30, margin: { top: (12.0) }, hover_actions_enabled:true }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

formatting fixes

Suggested change
avatar_row = <AvatarRow> {width: 40, height: 30, margin: { top: (12.0) }, hover_actions_enabled:true }
avatar_row = <AvatarRow> {
width: 40,
height: 30,
margin: { top: (12.0) },
hover_actions_enabled: true,
}

@kevinaboos kevinaboos added waiting-on-author This issue is waiting on the original author for a response and removed waiting-on-review This issue is waiting to be reviewed labels Nov 7, 2024
@kevinaboos kevinaboos changed the title Add read receipt display Display read receipts next to each message as a row of Avatars for users who have seen that message Nov 7, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
waiting-on-author This issue is waiting on the original author for a response
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Display read receipts for other users on the bottom right of a message/event
2 participants