Skip to main content

matrix_sdk_ui/timeline/event_item/content/
msg_like.rs

1// Copyright 2024 The Matrix.org Foundation C.I.C.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use as_variant::as_variant;
16use ruma::OwnedEventId;
17
18use super::{
19    EmbeddedEvent, EncryptedMessage, InReplyToDetails, LiveLocationState, Message, PollState,
20    Sticker,
21};
22use crate::timeline::{
23    ReactionsByKeyBySender, TimelineDetails, event_item::content::other::OtherMessageLike,
24};
25
26#[derive(Clone, Debug)]
27pub enum MsgLikeKind {
28    /// An `m.room.message` event or extensible event, including edits.
29    Message(Message),
30
31    /// An `m.sticker` event.
32    Sticker(Sticker),
33
34    /// An `m.poll.start` event.
35    Poll(PollState),
36
37    /// A redacted message.
38    Redacted,
39
40    /// An `m.room.encrypted` event that could not be decrypted.
41    UnableToDecrypt(EncryptedMessage),
42
43    /// A custom message like event.
44    Other(OtherMessageLike),
45
46    /// A live location sharing session (MSC3489).
47    LiveLocation(LiveLocationState),
48}
49
50#[derive(Clone, Debug)]
51pub struct ThreadSummary {
52    pub latest_event: TimelineDetails<Box<EmbeddedEvent>>,
53
54    /// The number of events in the thread, except for the thread root.
55    ///
56    /// This can be zero if all the events in the thread have been redacted.
57    ///
58    /// Note: this doesn't interact with the timeline filter; so opening a
59    /// thread-focused timeline with the same timeline filter may result in
60    /// *fewer* events than this number.
61    pub num_replies: u32,
62
63    /// The user's own public read receipt event id, for this particular thread.
64    pub public_read_receipt_event_id: Option<OwnedEventId>,
65
66    /// The user's own private read receipt event id, for this particular
67    /// thread.
68    pub private_read_receipt_event_id: Option<OwnedEventId>,
69}
70
71/// A special kind of [`super::TimelineItemContent`] that groups together
72/// different room message types with their respective reactions and thread
73/// information.
74#[derive(Clone, Debug)]
75pub struct MsgLikeContent {
76    pub kind: MsgLikeKind,
77    pub reactions: ReactionsByKeyBySender,
78    /// The event this message is replying to, if any.
79    pub in_reply_to: Option<InReplyToDetails>,
80    /// Event ID of the thread root, if this is a message in a thread.
81    pub thread_root: Option<OwnedEventId>,
82    /// Information about the thread this message is the root of, if any.
83    pub thread_summary: Option<ThreadSummary>,
84}
85
86impl MsgLikeContent {
87    #[cfg(not(tarpaulin_include))] // debug-logging functionality
88    pub(crate) fn debug_string(&self) -> &'static str {
89        match self.kind {
90            MsgLikeKind::Message(_) => "a message",
91            MsgLikeKind::Sticker(_) => "a sticker",
92            MsgLikeKind::Poll(_) => "a poll",
93            MsgLikeKind::Redacted => "a redacted message",
94            MsgLikeKind::UnableToDecrypt(_) => "an encrypted message we couldn't decrypt",
95            MsgLikeKind::Other(_) => "a custom message-like event",
96            MsgLikeKind::LiveLocation(_) => "a live location share",
97        }
98    }
99
100    pub fn redacted() -> Self {
101        Self {
102            kind: MsgLikeKind::Redacted,
103            reactions: Default::default(),
104            thread_root: None,
105            in_reply_to: None,
106            thread_summary: None,
107        }
108    }
109
110    pub fn unable_to_decrypt(encrypted_message: EncryptedMessage) -> Self {
111        Self {
112            kind: MsgLikeKind::UnableToDecrypt(encrypted_message),
113            reactions: Default::default(),
114            thread_root: None,
115            in_reply_to: None,
116            thread_summary: None,
117        }
118    }
119
120    /// Whether this item is part of a thread.
121    pub fn is_threaded(&self) -> bool {
122        self.thread_root.is_some()
123    }
124
125    pub fn with_in_reply_to(&self, in_reply_to: InReplyToDetails) -> Self {
126        Self { in_reply_to: Some(in_reply_to), ..self.clone() }
127    }
128
129    pub fn with_kind(&self, kind: MsgLikeKind) -> Self {
130        Self { kind, ..self.clone() }
131    }
132
133    /// If `kind` is of the [`MsgLikeKind`][MsgLikeKind::Message] variant,
134    /// return the inner [`Message`].
135    pub fn as_message(&self) -> Option<Message> {
136        as_variant!(&self.kind, MsgLikeKind::Message(message) => message.clone())
137    }
138}