Skip to main content

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 own
36    /// 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 to an
81    /// [`EventTimelineItem`] will not change the ID of the enclosing
82    /// `TimelineItem`. For some virtual items like date dividers, identity
83    /// isn't easy to define though and you might see a new ID getting generated
84    /// for a date divider that you perceive to be "the same" as a previous one.
85    pub fn unique_id(&self) -> &TimelineUniqueId {
86        &self.internal_id
87    }
88
89    pub(crate) fn read_marker() -> Arc<TimelineItem> {
90        Arc::new(Self {
91            kind: TimelineItemKind::Virtual(VirtualTimelineItem::ReadMarker),
92            internal_id: TimelineUniqueId("__read_marker".to_owned()),
93        })
94    }
95
96    pub(crate) fn is_local_echo(&self) -> bool {
97        matches!(&self.kind, TimelineItemKind::Event(ev) if ev.is_local_echo())
98    }
99
100    pub(crate) fn is_remote_event(&self) -> bool {
101        matches!(&self.kind, TimelineItemKind::Event(ev) if ev.is_remote_event())
102    }
103
104    pub(crate) fn is_event(&self) -> bool {
105        matches!(&self.kind, TimelineItemKind::Event(_))
106    }
107
108    /// Check whether this item is a (virtual) date divider.
109    pub fn is_date_divider(&self) -> bool {
110        matches!(self.kind, TimelineItemKind::Virtual(VirtualTimelineItem::DateDivider(_)))
111    }
112
113    pub(crate) fn is_read_marker(&self) -> bool {
114        matches!(self.kind, TimelineItemKind::Virtual(VirtualTimelineItem::ReadMarker))
115    }
116
117    /// Check whether this item is a (virtual) timeline start item.
118    pub fn is_timeline_start(&self) -> bool {
119        matches!(self.kind, TimelineItemKind::Virtual(VirtualTimelineItem::TimelineStart))
120    }
121}
122
123impl Deref for TimelineItem {
124    type Target = TimelineItemKind;
125
126    fn deref(&self) -> &Self::Target {
127        &self.kind
128    }
129}
130
131impl From<EventTimelineItem> for TimelineItemKind {
132    fn from(item: EventTimelineItem) -> Self {
133        Self::Event(item)
134    }
135}
136
137impl From<VirtualTimelineItem> for TimelineItemKind {
138    fn from(item: VirtualTimelineItem) -> Self {
139        Self::Virtual(item)
140    }
141}