13
13
// limitations under the License.
14
14
15
15
use eyeball:: { AsyncLock , SharedObservable , Subscriber } ;
16
- use ruma:: { EventId , OwnedEventId , OwnedRoomId , RoomId } ;
16
+ use matrix_sdk_base:: event_cache:: Event ;
17
+ use ruma:: {
18
+ events:: {
19
+ call:: { invite:: SyncCallInviteEvent , notify:: SyncCallNotifyEvent } ,
20
+ poll:: unstable_start:: SyncUnstablePollStartEvent ,
21
+ relation:: RelationType ,
22
+ room:: {
23
+ member:: { MembershipState , SyncRoomMemberEvent } ,
24
+ message:: { MessageType , SyncRoomMessageEvent } ,
25
+ power_levels:: RoomPowerLevels ,
26
+ } ,
27
+ sticker:: SyncStickerEvent ,
28
+ AnySyncMessageLikeEvent , AnySyncStateEvent , AnySyncTimelineEvent ,
29
+ } ,
30
+ EventId , OwnedEventId , OwnedRoomId , RoomId , UserId ,
31
+ } ;
32
+ use tracing:: warn;
17
33
18
- use crate :: event_cache:: RoomEventCache ;
34
+ use crate :: { event_cache:: RoomEventCache , room :: WeakRoom } ;
19
35
20
36
/// The latest event of a room or a thread.
21
37
///
@@ -35,12 +51,13 @@ impl LatestEvent {
35
51
room_id : & RoomId ,
36
52
thread_id : Option < & EventId > ,
37
53
room_event_cache : & RoomEventCache ,
54
+ weak_room : & WeakRoom ,
38
55
) -> Self {
39
56
Self {
40
57
room_id : room_id. to_owned ( ) ,
41
58
thread_id : thread_id. map ( ToOwned :: to_owned) ,
42
59
value : SharedObservable :: new_async (
43
- LatestEventValue :: new ( room_id, thread_id, room_event_cache) . await ,
60
+ LatestEventValue :: new ( room_id, thread_id, room_event_cache, weak_room ) . await ,
44
61
) ,
45
62
}
46
63
}
@@ -51,15 +68,24 @@ impl LatestEvent {
51
68
}
52
69
53
70
/// Update the inner latest event value.
54
- pub async fn update ( & mut self , room_event_cache : & RoomEventCache ) {
55
- let new_value =
56
- LatestEventValue :: new ( & self . room_id , self . thread_id . as_deref ( ) , room_event_cache) . await ;
57
-
58
- match new_value {
59
- LatestEventValue :: None => {
60
- // The new value is `None`. It means no new value has been
61
- // computed. Let's keep the old value.
62
- }
71
+ pub async fn update (
72
+ & mut self ,
73
+ room_event_cache : & RoomEventCache ,
74
+ power_levels : & Option < ( & UserId , RoomPowerLevels ) > ,
75
+ ) {
76
+ let new_value = LatestEventValue :: new_with_power_levels (
77
+ & self . room_id ,
78
+ self . thread_id . as_deref ( ) ,
79
+ room_event_cache,
80
+ power_levels,
81
+ )
82
+ . await ;
83
+
84
+ if let LatestEventValue :: None = new_value {
85
+ // The new value is `None`. It means no new value has been
86
+ // computed. Let's keep the old value.
87
+ } else {
88
+ self . value . set ( new_value) . await ;
63
89
}
64
90
}
65
91
}
@@ -69,14 +95,146 @@ impl LatestEvent {
69
95
pub enum LatestEventValue {
70
96
/// No value has been computed yet, or no candidate value was found.
71
97
None ,
98
+
99
+ /// A `m.room.message` event.
100
+ RoomMessage ( SyncRoomMessageEvent ) ,
101
+
102
+ /// A `m.sticker` event.
103
+ Sticker ( SyncStickerEvent ) ,
104
+
105
+ /// An `org.matrix.msc3381.poll.start` event.
106
+ Poll ( SyncUnstablePollStartEvent ) ,
107
+
108
+ /// A `m.call.invite` event.
109
+ CallInvite ( SyncCallInviteEvent ) ,
110
+
111
+ /// A `m.call.notify` event.
112
+ CallNotify ( SyncCallNotifyEvent ) ,
113
+
114
+ /// A `m.room.member` event, more precisely a knock membership change that
115
+ /// can be handled by the current user.
116
+ KnockedStateEvent ( SyncRoomMemberEvent ) ,
72
117
}
73
118
74
119
impl LatestEventValue {
75
120
async fn new (
121
+ room_id : & RoomId ,
122
+ thread_id : Option < & EventId > ,
123
+ room_event_cache : & RoomEventCache ,
124
+ weak_room : & WeakRoom ,
125
+ ) -> Self {
126
+ // Get the power levels of the user for the current room if the `WeakRoom` is
127
+ // still valid.
128
+ let room = weak_room. get ( ) ;
129
+ let power_levels = match & room {
130
+ Some ( room) => {
131
+ let power_levels = room. power_levels ( ) . await . ok ( ) ;
132
+
133
+ Some ( room. own_user_id ( ) ) . zip ( power_levels)
134
+ }
135
+
136
+ None => None ,
137
+ } ;
138
+
139
+ Self :: new_with_power_levels ( room_id, thread_id, room_event_cache, & power_levels) . await
140
+ }
141
+
142
+ async fn new_with_power_levels (
76
143
_room_id : & RoomId ,
77
144
_thread_id : Option < & EventId > ,
78
- _room_event_cache : & RoomEventCache ,
145
+ room_event_cache : & RoomEventCache ,
146
+ power_levels : & Option < ( & UserId , RoomPowerLevels ) > ,
79
147
) -> Self {
80
- LatestEventValue :: None
148
+ room_event_cache
149
+ . rfind_map_event_in_memory_by ( |event| find_and_map ( event, power_levels) )
150
+ . await
151
+ . unwrap_or ( LatestEventValue :: None )
152
+ }
153
+ }
154
+
155
+ pub fn find_and_map (
156
+ event : & Event ,
157
+ power_levels : & Option < ( & UserId , RoomPowerLevels ) > ,
158
+ ) -> Option < LatestEventValue > {
159
+ // Cast the event into an `AnySyncTimelineEvent`. If deserializing fails, we
160
+ // ignore the event.
161
+ let Some ( event) = event. raw ( ) . deserialize ( ) . ok ( ) else {
162
+ warn ! ( ?event, "Failed to deserialize the event when looking for a suitable latest event" ) ;
163
+
164
+ return None ;
165
+ } ;
166
+
167
+ match event {
168
+ AnySyncTimelineEvent :: MessageLike ( message_like_event) => match message_like_event {
169
+ AnySyncMessageLikeEvent :: RoomMessage ( message) => {
170
+ if let Some ( original_message) = message. as_original ( ) {
171
+ // Don't show incoming verification requests.
172
+ if let MessageType :: VerificationRequest ( _) = original_message. content . msgtype {
173
+ return None ;
174
+ }
175
+
176
+ // Check if this is a replacement for another message. If it is, ignore it.
177
+ let is_replacement =
178
+ original_message. content . relates_to . as_ref ( ) . is_some_and ( |relates_to| {
179
+ if let Some ( relation_type) = relates_to. rel_type ( ) {
180
+ relation_type == RelationType :: Replacement
181
+ } else {
182
+ false
183
+ }
184
+ } ) ;
185
+
186
+ if is_replacement {
187
+ None
188
+ } else {
189
+ Some ( LatestEventValue :: RoomMessage ( message) )
190
+ }
191
+ } else {
192
+ Some ( LatestEventValue :: RoomMessage ( message) )
193
+ }
194
+ }
195
+
196
+ AnySyncMessageLikeEvent :: UnstablePollStart ( poll) => Some ( LatestEventValue :: Poll ( poll) ) ,
197
+
198
+ AnySyncMessageLikeEvent :: CallInvite ( invite) => {
199
+ Some ( LatestEventValue :: CallInvite ( invite) )
200
+ }
201
+
202
+ AnySyncMessageLikeEvent :: CallNotify ( notify) => {
203
+ Some ( LatestEventValue :: CallNotify ( notify) )
204
+ }
205
+
206
+ AnySyncMessageLikeEvent :: Sticker ( sticker) => Some ( LatestEventValue :: Sticker ( sticker) ) ,
207
+
208
+ // Encrypted events are not suitable.
209
+ AnySyncMessageLikeEvent :: RoomEncrypted ( _) => None ,
210
+
211
+ // Everything else is considered not suitable.
212
+ _ => None ,
213
+ } ,
214
+
215
+ // We don't currently support most state events
216
+ AnySyncTimelineEvent :: State ( state) => {
217
+ // But we make an exception for knocked state events *if* the current user
218
+ // can either accept or decline them.
219
+ if let AnySyncStateEvent :: RoomMember ( member) = state {
220
+ if matches ! ( member. membership( ) , MembershipState :: Knock ) {
221
+ let can_accept_or_decline_knocks = match power_levels {
222
+ Some ( ( own_user_id, room_power_levels) ) => {
223
+ room_power_levels. user_can_invite ( own_user_id)
224
+ || room_power_levels. user_can_kick ( own_user_id)
225
+ }
226
+ _ => false ,
227
+ } ;
228
+
229
+ // The current user can act on the knock changes, so they should be
230
+ // displayed
231
+ if can_accept_or_decline_knocks {
232
+ return Some ( LatestEventValue :: KnockedStateEvent ( member) ) ;
233
+ }
234
+ }
235
+ }
236
+
237
+ None
238
+ }
81
239
}
82
240
}
0 commit comments