matrix_sdk_ui/timeline/
item.rs

1// Copyright 2023 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 std::{ops::Deref, sync::Arc};
16
17use as_variant::as_variant;
18
19use super::{EventTimelineItem, VirtualTimelineItem};
20
21/// Opaque unique identifier for a timeline item.
22///
23/// It is transferred whenever a timeline item is updated. This can be used as a
24/// stable identifier for UI purposes, as well as operations on the event
25/// represented by the item.
26#[derive(Clone, Debug, PartialEq, Eq, Hash)]
27pub struct TimelineUniqueId(pub String);
28
29/// The type of timeline item.
30#[derive(Clone, Debug)]
31#[allow(clippy::large_enum_variant)]
32pub enum TimelineItemKind {
33    /// An event or aggregation of multiple events.
34    Event(EventTimelineItem),
35    /// An item that doesn't correspond to an event, for example the user's
36    /// own read marker, or a date divider.
37    Virtual(VirtualTimelineItem),
38}
39
40/// A single entry in timeline.
41#[derive(Clone, Debug)]
42pub struct TimelineItem {
43    pub(crate) kind: TimelineItemKind,
44    pub(crate) internal_id: TimelineUniqueId,
45}
46
47impl TimelineItem {
48    /// Create a new `TimelineItem` with the given kind and internal id.
49    pub(crate) fn new(
50        kind: impl Into<TimelineItemKind>,
51        internal_id: TimelineUniqueId,
52    ) -> Arc<Self> {
53        Arc::new(TimelineItem { kind: kind.into(), internal_id })
54    }
55
56    /// Create a clone of the current `TimelineItem` with the given kind.
57    pub(crate) fn with_kind(&self, kind: impl Into<TimelineItemKind>) -> Arc<Self> {
58        Arc::new(Self { kind: kind.into(), internal_id: self.internal_id.clone() })
59    }
60
61    /// Get the [`TimelineItemKind`] of this item.
62    pub fn kind(&self) -> &TimelineItemKind {
63        &self.kind
64    }
65
66    /// Get the inner `EventTimelineItem`, if this is a
67    /// [`TimelineItemKind::Event`].
68    pub fn as_event(&self) -> Option<&EventTimelineItem> {
69        as_variant!(&self.kind, TimelineItemKind::Event)
70    }
71
72    /// Get the inner `VirtualTimelineItem`, if this is a
73    /// [`TimelineItemKind::Virtual`].
74    pub fn as_virtual(&self) -> Option<&VirtualTimelineItem> {
75        as_variant!(&self.kind, TimelineItemKind::Virtual)
76    }
77
78    /// Get a unique ID for this timeline item.
79    ///
80    /// It identifies the item on a best-effort basis. For instance, edits
81    /// to an [`EventTimelineItem`] will not change the ID of the
82    /// enclosing `TimelineItem`. For some virtual items like date
83    /// dividers, identity isn't easy to define though and you might
84    /// see a new ID getting generated for a date divider that you
85    /// perceive to be "the same" as a previous one.
86    pub fn unique_id(&self) -> &TimelineUniqueId {
87        &self.internal_id
88    }
89
90    pub(crate) fn read_marker() -> Arc<TimelineItem> {
91        Arc::new(Self {
92            kind: TimelineItemKind::Virtual(VirtualTimelineItem::ReadMarker),
93            internal_id: TimelineUniqueId("__read_marker".to_owned()),
94        })
95    }
96
97    pub(crate) fn is_local_echo(&self) -> bool {
98        matches!(&self.kind, TimelineItemKind::Event(ev) if ev.is_local_echo())
99    }
100
101    pub(crate) fn is_remote_event(&self) -> bool {
102        matches!(&self.kind, TimelineItemKind::Event(ev) if ev.is_remote_event())
103    }
104
105    pub(crate) fn is_event(&self) -> bool {
106        matches!(&self.kind, TimelineItemKind::Event(_))
107    }
108
109    /// Check whether this item is a date divider.
110    #[must_use]
111    pub fn is_date_divider(&self) -> bool {
112        matches!(self.kind, TimelineItemKind::Virtual(VirtualTimelineItem::DateDivider(_)))
113    }
114
115    pub(crate) fn is_read_marker(&self) -> bool {
116        matches!(self.kind, TimelineItemKind::Virtual(VirtualTimelineItem::ReadMarker))
117    }
118}
119
120impl Deref for TimelineItem {
121    type Target = TimelineItemKind;
122
123    fn deref(&self) -> &Self::Target {
124        &self.kind
125    }
126}
127
128impl From<EventTimelineItem> for TimelineItemKind {
129    fn from(item: EventTimelineItem) -> Self {
130        Self::Event(item)
131    }
132}
133
134impl From<VirtualTimelineItem> for TimelineItemKind {
135    fn from(item: VirtualTimelineItem) -> Self {
136        Self::Virtual(item)
137    }
138}