1+ //! A room image message detail widget that displays a user's avatar, username, and message date.
2+
3+ use makepad_widgets:: * ;
4+ use matrix_sdk:: ruma:: { MilliSecondsSinceUnixEpoch , OwnedRoomId , OwnedUserId } ;
5+ use matrix_sdk_ui:: timeline:: { Profile , TimelineDetails } ;
6+
7+ use crate :: {
8+ shared:: {
9+ avatar:: AvatarWidgetExt ,
10+ timestamp:: TimestampWidgetExt ,
11+ } ,
12+ utils:: unix_time_millis_to_datetime,
13+ } ;
14+ use matrix_sdk:: ruma:: OwnedEventId ;
15+
16+ live_design ! {
17+ use link:: theme:: * ;
18+ use link:: shaders:: * ;
19+ use link:: widgets:: * ;
20+
21+ use crate :: shared:: styles:: * ;
22+ use crate :: shared:: avatar:: Avatar ;
23+ use crate :: shared:: timestamp:: Timestamp ;
24+
25+ pub RoomImageMessageDetail = { { RoomImageMessageDetail } } {
26+ width: Fill , height: Fill
27+ flow: Right
28+
29+ top_left_container = <View > {
30+ width: 150 , height: Fit ,
31+ flow: Right ,
32+ spacing: 10 ,
33+ margin: { left: 20 , top: 20 }
34+ align: { y: 0.5 }
35+
36+ avatar = <Avatar > {
37+ width: 40 ,
38+ height: 40 ,
39+ }
40+
41+ content = <View > {
42+ width: Fill , height: Fit ,
43+ flow: Down ,
44+ spacing: 4 ,
45+ align: { x: 0.0 }
46+
47+ username = <Label > {
48+ width: Fill , height: Fit ,
49+ draw_text: {
50+ text_style: <REGULAR_TEXT >{ font_size: 14 } ,
51+ color: ( COLOR_TEXT )
52+ }
53+ text: ""
54+ }
55+ timestamp_view = <View > {
56+ width: Fill , height: Fit
57+ timestamp = <Timestamp > {
58+ width: Fill , height: Fit ,
59+ margin: { left: 5 }
60+ }
61+ }
62+
63+ }
64+ }
65+ image_name_and_size = <Label > {
66+ width: Fill , height: Fit ,
67+ margin: { top: 40 }
68+ align: { x: 0.5 , }
69+ draw_text: {
70+ text_style: <REGULAR_TEXT >{ font_size: 14 } ,
71+ color: ( COLOR_TEXT ) ,
72+ wrap: Word
73+ }
74+ }
75+ empty_right_container = <View > {
76+ // equal width as the top-left container to keep the image name centered.
77+ width: 150 , height: Fit ,
78+ }
79+ }
80+ }
81+
82+ #[ derive( Live , LiveHook , Widget ) ]
83+ pub struct RoomImageMessageDetail {
84+ #[ deref] view : View ,
85+ #[ rust] sender : Option < OwnedUserId > ,
86+ #[ rust] sender_profile : Option < TimelineDetails < Profile > > ,
87+ #[ rust] room_id : Option < OwnedRoomId > ,
88+ #[ rust] event_id : Option < OwnedEventId > ,
89+ #[ rust] avatar_drawn : bool ,
90+ }
91+
92+ /// Convert bytes to human-readable file size format
93+ fn format_file_size ( bytes : i32 ) -> String {
94+ if bytes < 0 {
95+ return "Unknown size" . to_string ( ) ;
96+ }
97+
98+ let bytes = bytes as u64 ;
99+ const UNITS : & [ & str ] = & [ "B" , "KB" , "MB" , "GB" , "TB" ] ;
100+
101+ if bytes == 0 {
102+ return "0 B" . to_string ( ) ;
103+ }
104+
105+ let mut size = bytes as f64 ;
106+ let mut unit_index = 0 ;
107+
108+ while size >= 1024.0 && unit_index < UNITS . len ( ) - 1 {
109+ size /= 1024.0 ;
110+ unit_index += 1 ;
111+ }
112+
113+ if unit_index == 0 {
114+ format ! ( "{} {}" , bytes, UNITS [ unit_index] )
115+ } else {
116+ format ! ( "{:.1} {}" , size, UNITS [ unit_index] )
117+ }
118+ }
119+
120+ impl Widget for RoomImageMessageDetail {
121+ fn handle_event ( & mut self , cx : & mut Cx , event : & Event , scope : & mut Scope ) {
122+ self . view . handle_event ( cx, event, scope) ;
123+ self . match_event ( cx, event) ;
124+ }
125+
126+ fn draw_walk ( & mut self , cx : & mut Cx2d , scope : & mut Scope , walk : Walk ) -> DrawStep {
127+ if !self . avatar_drawn {
128+ let avatar_ref = self . avatar ( id ! ( top_left_container. avatar) ) ;
129+ let Some ( room_id) = & self . room_id else { return DrawStep :: done ( ) } ;
130+ let Some ( sender) = & self . sender else { return DrawStep :: done ( ) } ;
131+ let ( username, avatar_drawn) = avatar_ref. set_avatar_and_get_username ( cx, room_id, sender, self . sender_profile . as_ref ( ) , self . event_id . as_deref ( ) ) ;
132+ self . label ( id ! ( top_left_container. username) ) . set_text ( cx, & username) ;
133+ self . avatar_drawn = avatar_drawn;
134+ }
135+ self . view . draw_walk ( cx, scope, walk)
136+ }
137+ }
138+
139+ impl MatchEvent for RoomImageMessageDetail {
140+ fn handle_action ( & mut self , cx : & mut Cx , action : & Action ) {
141+ match action. as_widget_action ( ) . cast ( ) {
142+ RoomImageMessageDetailAction :: SetImageDetail {
143+ room_id,
144+ sender,
145+ sender_profile,
146+ event_id,
147+ timestamp_millis,
148+ image_name,
149+ image_size
150+ } => {
151+ self . room_id = room_id. clone ( ) ;
152+ self . sender = sender. clone ( ) ;
153+ self . sender_profile = sender_profile. clone ( ) ;
154+ self . event_id = event_id. clone ( ) ;
155+ self . avatar_drawn = false ;
156+ // Format and display image name and size
157+ let human_readable_size = format_file_size ( image_size) ;
158+ let display_text = format ! ( "{} ({})" , image_name, human_readable_size) ;
159+ self . label ( id ! ( image_name_and_size) ) . set_text ( cx, & display_text) ;
160+ if let Some ( dt) = unix_time_millis_to_datetime ( timestamp_millis) {
161+ self . view ( id ! ( timestamp_view) ) . set_visible ( cx, true ) ;
162+ self . timestamp ( id ! ( timestamp) ) . set_date_time ( cx, dt) ;
163+ }
164+ }
165+ _ => { }
166+ }
167+ }
168+ }
169+
170+ impl RoomImageMessageDetail {
171+ /// Reset the widget state to its default values
172+ pub fn reset_state ( & mut self , cx : & mut Cx ) {
173+ self . sender = None ;
174+ self . sender_profile = None ;
175+ self . room_id = None ;
176+ self . event_id = None ;
177+ self . avatar_drawn = false ;
178+
179+ // Clear the UI elements
180+ self . label ( id ! ( top_left_container. username) ) . set_text ( cx, "" ) ;
181+ self . label ( id ! ( image_name_and_size) ) . set_text ( cx, "" ) ;
182+ self . view ( id ! ( timestamp_view) ) . set_visible ( cx, false ) ;
183+ }
184+ }
185+
186+ impl RoomImageMessageDetailRef {
187+ /// See [`RoomImageMessageDetail::reset_state()`]
188+ pub fn reset_state ( & self , cx : & mut Cx ) {
189+ if let Some ( mut inner) = self . borrow_mut ( ) {
190+ inner. reset_state ( cx) ;
191+ }
192+ }
193+ }
194+
195+ /// Actions handled by the `RoomImageMessageDetail`
196+ #[ derive( Debug , Clone , DefaultNone ) ]
197+ pub enum RoomImageMessageDetailAction {
198+ /// Set the image detail onto image viewer modal.
199+ SetImageDetail {
200+ /// Room ID
201+ room_id : Option < OwnedRoomId > ,
202+ /// User ID for the sender of the image
203+ sender : Option < OwnedUserId > ,
204+ /// Profile details for the sender
205+ sender_profile : Option < TimelineDetails < Profile > > ,
206+ /// Event ID
207+ event_id : Option < OwnedEventId > ,
208+ /// Timestamp of the message
209+ timestamp_millis : MilliSecondsSinceUnixEpoch ,
210+ /// Image name
211+ image_name : String ,
212+ /// Image size in bytes.
213+ image_size : i32
214+ } ,
215+ None ,
216+ }
0 commit comments