diff --git a/bindings/matrix-sdk-ffi/src/timeline/mod.rs b/bindings/matrix-sdk-ffi/src/timeline/mod.rs index 88afe7ff21..ab49adfdd3 100644 --- a/bindings/matrix-sdk-ffi/src/timeline/mod.rs +++ b/bindings/matrix-sdk-ffi/src/timeline/mod.rs @@ -1323,7 +1323,7 @@ impl LazyTimelineItemProvider { self.0.local_echo_send_handle().map(|handle| Arc::new(SendHandle::new(handle))) } - fn should_boost(&self) -> bool { - self.0.should_boost() + fn contains_only_emojis(&self) -> bool { + self.0.contains_only_emojis() } } diff --git a/crates/matrix-sdk-ui/src/timeline/event_item/mod.rs b/crates/matrix-sdk-ui/src/timeline/event_item/mod.rs index f1901c260a..374d06d76a 100644 --- a/crates/matrix-sdk-ui/src/timeline/event_item/mod.rs +++ b/crates/matrix-sdk-ui/src/timeline/event_item/mod.rs @@ -443,19 +443,6 @@ impl EventTimelineItem { } } - pub fn should_boost(&self) -> bool { - match self.content() { - TimelineItemContent::Message(msg) => match msg.msgtype() { - MessageType::Text(text) => { - let graphmes = text.body.graphemes(true).collect::>(); - graphmes.iter().all(|g| emojis::get(g).is_some()) - } - _ => false, - }, - _ => false, - } - } - /// Check whether this item can be replied to. pub fn can_be_replied_to(&self) -> bool { // This must be in sync with the early returns of `Timeline::send_reply` @@ -618,6 +605,60 @@ impl EventTimelineItem { pub fn local_echo_send_handle(&self) -> Option { as_variant!(self.handle(), TimelineItemHandle::Local(handle) => handle.clone()) } + + /// Some clients may want to know if a particular text message or media + /// caption contains only emojis so that they can render them bigger for + /// added effect. + /// + /// This function provides that feature with the following + /// behavior/limitations: + /// - ignores leading and trailing white spaces + /// - fails texts bigger than 5 graphemes for performance reasons + /// - checks the body only for [`MessageType::Text`] + /// - only checks the caption for [`MessageType::Audio`], + /// [`MessageType::File`], [`MessageType::Image`], and + /// [`MessageType::Video`] if present + /// - all other message types will not match + /// + /// See the [`tests::test_emoji_detection`] tests for examples. + pub fn contains_only_emojis(&self) -> bool { + let body = match self.content() { + TimelineItemContent::Message(msg) => match msg.msgtype() { + MessageType::Text(text) => Some(text.body.as_str()), + MessageType::Audio(audio) => audio.caption(), + MessageType::File(file) => file.caption(), + MessageType::Image(image) => image.caption(), + MessageType::Video(video) => video.caption(), + _ => None, + }, + TimelineItemContent::RedactedMessage + | TimelineItemContent::Sticker(_) + | TimelineItemContent::UnableToDecrypt(_) + | TimelineItemContent::MembershipChange(_) + | TimelineItemContent::ProfileChange(_) + | TimelineItemContent::OtherState(_) + | TimelineItemContent::FailedToParseMessageLike { .. } + | TimelineItemContent::FailedToParseState { .. } + | TimelineItemContent::Poll(_) + | TimelineItemContent::CallInvite + | TimelineItemContent::CallNotify => None, + }; + + if let Some(body) = body { + // Collect the graphemes after trimming white spaces. + let graphemes = body.trim().graphemes(true).collect::>(); + + // Limit the check to 5 graphemes for performance and security + // reasons + if graphemes.iter().count() > 5 { + return false; + } + + graphemes.iter().all(|g| emojis::get(g).is_some()) + } else { + return false; + } + } } impl From for EventTimelineItemKind { @@ -1086,31 +1127,34 @@ mod tests { .await .unwrap(); - assert!(!timeline_item.should_boost()); + assert!(!timeline_item.contains_only_emojis()); - event = message_event(room_id, user_id, "πŸ‘¨β€πŸ‘©β€πŸ‘¦1οΈβƒ£πŸš€πŸ‘³πŸΎβ€β™‚οΈπŸͺ©πŸ‘πŸ‘πŸ»πŸ«±πŸΌβ€πŸ«²πŸΎπŸ™‚πŸ‘‹", "", 0); + // Ignores leading and trailing white spaces + event = message_event(room_id, user_id, " πŸš€ ", "", 0); timeline_item = EventTimelineItem::from_latest_event(client.clone(), room_id, LatestEvent::new(event)) .await .unwrap(); - assert!(timeline_item.should_boost()); + assert!(timeline_item.contains_only_emojis()); - event = message_event(room_id, user_id, "0", "", 0); + // Too many + event = message_event(room_id, user_id, "πŸ‘¨β€πŸ‘©β€πŸ‘¦1οΈβƒ£πŸš€πŸ‘³πŸΎβ€β™‚οΈπŸͺ©πŸ‘πŸ‘πŸ»πŸ«±πŸΌβ€πŸ«²πŸΎπŸ™‚πŸ‘‹", "", 0); timeline_item = EventTimelineItem::from_latest_event(client.clone(), room_id, LatestEvent::new(event)) .await .unwrap(); - assert!(!timeline_item.should_boost()); + assert!(!timeline_item.contains_only_emojis()); - event = message_event(room_id, user_id, "0*", "", 0); + // Works with combined emojis + event = message_event(room_id, user_id, "πŸ‘¨β€πŸ‘©β€πŸ‘¦1οΈβƒ£πŸ‘³πŸΎβ€β™‚οΈπŸ‘πŸ»πŸ«±πŸΌβ€πŸ«²πŸΎ", "", 0); timeline_item = - EventTimelineItem::from_latest_event(client, room_id, LatestEvent::new(event)) + EventTimelineItem::from_latest_event(client.clone(), room_id, LatestEvent::new(event)) .await .unwrap(); - assert!(!timeline_item.should_boost()); + assert!(!timeline_item.contains_only_emojis()); } fn member_event(