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 (virtual) date divider.
110    pub fn is_date_divider(&self) -> bool {
111        matches!(self.kind, TimelineItemKind::Virtual(VirtualTimelineItem::DateDivider(_)))
112    }
113
114    pub(crate) fn is_read_marker(&self) -> bool {
115        matches!(self.kind, TimelineItemKind::Virtual(VirtualTimelineItem::ReadMarker))
116    }
117
118    /// Check whether this item is a (virtual) timeline start item.
119    pub fn is_timeline_start(&self) -> bool {
120        matches!(self.kind, TimelineItemKind::Virtual(VirtualTimelineItem::TimelineStart))
121    }
122}
123
124impl Deref for TimelineItem {
125    type Target = TimelineItemKind;
126
127    fn deref(&self) -> &Self::Target {
128        &self.kind
129    }
130}
131
132impl From<EventTimelineItem> for TimelineItemKind {
133    fn from(item: EventTimelineItem) -> Self {
134        Self::Event(item)
135    }
136}
137
138impl From<VirtualTimelineItem> for TimelineItemKind {
139    fn from(item: VirtualTimelineItem) -> Self {
140        Self::Virtual(item)
141    }
142}