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.
1415use std::{ops::Deref, sync::Arc};
1617use as_variant::as_variant;
1819use super::{EventTimelineItem, VirtualTimelineItem};
2021/// 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);
2829/// 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.
34Event(EventTimelineItem),
35/// An item that doesn't correspond to an event, for example the user's
36 /// own read marker, or a date divider.
37Virtual(VirtualTimelineItem),
38}
3940/// A single entry in timeline.
41#[derive(Clone, Debug)]
42pub struct TimelineItem {
43pub(crate) kind: TimelineItemKind,
44pub(crate) internal_id: TimelineUniqueId,
45}
4647impl TimelineItem {
48/// Create a new `TimelineItem` with the given kind and internal id.
49pub(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 }
5556/// Create a clone of the current `TimelineItem` with the given kind.
57pub(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 }
6061/// Get the [`TimelineItemKind`] of this item.
62pub fn kind(&self) -> &TimelineItemKind {
63&self.kind
64 }
6566/// Get the inner `EventTimelineItem`, if this is a
67 /// [`TimelineItemKind::Event`].
68pub fn as_event(&self) -> Option<&EventTimelineItem> {
69as_variant!(&self.kind, TimelineItemKind::Event)
70 }
7172/// Get the inner `VirtualTimelineItem`, if this is a
73 /// [`TimelineItemKind::Virtual`].
74pub fn as_virtual(&self) -> Option<&VirtualTimelineItem> {
75as_variant!(&self.kind, TimelineItemKind::Virtual)
76 }
7778/// 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.
86pub fn unique_id(&self) -> &TimelineUniqueId {
87&self.internal_id
88 }
8990pub(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 }
9697pub(crate) fn is_local_echo(&self) -> bool {
98matches!(&self.kind, TimelineItemKind::Event(ev) if ev.is_local_echo())
99 }
100101pub(crate) fn is_remote_event(&self) -> bool {
102matches!(&self.kind, TimelineItemKind::Event(ev) if ev.is_remote_event())
103 }
104105pub(crate) fn is_event(&self) -> bool {
106matches!(&self.kind, TimelineItemKind::Event(_))
107 }
108109/// Check whether this item is a (virtual) date divider.
110pub fn is_date_divider(&self) -> bool {
111matches!(self.kind, TimelineItemKind::Virtual(VirtualTimelineItem::DateDivider(_)))
112 }
113114pub(crate) fn is_read_marker(&self) -> bool {
115matches!(self.kind, TimelineItemKind::Virtual(VirtualTimelineItem::ReadMarker))
116 }
117118/// Check whether this item is a (virtual) timeline start item.
119pub fn is_timeline_start(&self) -> bool {
120matches!(self.kind, TimelineItemKind::Virtual(VirtualTimelineItem::TimelineStart))
121 }
122}
123124impl Deref for TimelineItem {
125type Target = TimelineItemKind;
126127fn deref(&self) -> &Self::Target {
128&self.kind
129 }
130}
131132impl From<EventTimelineItem> for TimelineItemKind {
133fn from(item: EventTimelineItem) -> Self {
134Self::Event(item)
135 }
136}
137138impl From<VirtualTimelineItem> for TimelineItemKind {
139fn from(item: VirtualTimelineItem) -> Self {
140Self::Virtual(item)
141 }
142}