fractal/session/model/room/timeline/
timeline_item.rs

1use gtk::{glib, prelude::*, subclass::prelude::*};
2use matrix_sdk_ui::timeline::{TimelineItem as SdkTimelineItem, TimelineItemKind};
3use tracing::error;
4
5use super::{Event, Timeline, VirtualItem};
6use crate::session::model::Room;
7
8mod imp {
9    use std::cell::{OnceCell, RefCell};
10
11    use super::*;
12
13    #[repr(C)]
14    pub struct TimelineItemClass {
15        parent_class: glib::object::ObjectClass,
16    }
17
18    unsafe impl ClassStruct for TimelineItemClass {
19        type Type = TimelineItem;
20    }
21
22    #[derive(Debug, Default, glib::Properties)]
23    #[properties(wrapper_type = super::TimelineItem)]
24    pub struct TimelineItem {
25        /// The timeline containing this `TimelineItem`.
26        #[property(get, construct_only)]
27        timeline: OnceCell<Timeline>,
28        /// A unique ID for this `TimelineItem` in the local timeline.
29        #[property(get, construct_only)]
30        timeline_id: RefCell<String>,
31    }
32
33    #[glib::object_subclass]
34    impl ObjectSubclass for TimelineItem {
35        const NAME: &'static str = "TimelineItem";
36        const ABSTRACT: bool = true;
37        type Type = super::TimelineItem;
38        type Class = TimelineItemClass;
39    }
40
41    #[glib::derived_properties]
42    impl ObjectImpl for TimelineItem {}
43}
44
45glib::wrapper! {
46    /// Interface implemented by items inside the `Timeline`.
47    pub struct TimelineItem(ObjectSubclass<imp::TimelineItem>);
48}
49
50impl TimelineItem {
51    /// Create a new `TimelineItem` with the given SDK timeline item.
52    ///
53    /// Constructs the proper child type.
54    pub fn new(item: &SdkTimelineItem, timeline: &Timeline) -> Self {
55        let timeline_id = &item.unique_id().0;
56
57        match item.kind() {
58            TimelineItemKind::Event(event_item) => {
59                Event::new(timeline, event_item.clone(), timeline_id).upcast()
60            }
61            TimelineItemKind::Virtual(virtual_item) => {
62                VirtualItem::with_item(timeline, virtual_item, timeline_id).upcast()
63            }
64        }
65    }
66
67    /// Update this `TimelineItem` with the given SDK timeline item.
68    ///
69    /// A `TimelineItem` should not be updated with a SDK item that has a
70    /// different timeline ID.
71    pub(crate) fn update_with(&self, item: &SdkTimelineItem) {
72        if self.timeline_id() != item.unique_id().0 {
73            error!("Should not update an item with a different timeline ID");
74        }
75
76        match item.kind() {
77            TimelineItemKind::Event(new_event) => {
78                if let Some(event) = self.downcast_ref::<Event>() {
79                    event.update_with(new_event.clone());
80                } else {
81                    error!("Could not update a TimelineItem that is not an Event with an event SDK item");
82                }
83            }
84            TimelineItemKind::Virtual(new_item) => {
85                if let Some(virtual_item) = self.downcast_ref::<VirtualItem>() {
86                    virtual_item.update_with_item(new_item);
87                } else {
88                    error!("Could not update a TimelineItem that is not a VirtualItem with a virtual SDK item");
89                }
90            }
91        }
92    }
93}
94
95/// Public trait containing implemented methods for everything that derives from
96/// `TimelineItem`.
97///
98/// To override the behavior of these methods, override the corresponding method
99/// of `TimelineItemImpl`.
100#[allow(dead_code)]
101pub(crate) trait TimelineItemExt: 'static {
102    /// The timeline containing this `TimelineItem`.
103    fn timeline(&self) -> Timeline;
104
105    /// The room containing this `TimelineItem`.
106    fn room(&self) -> Room {
107        self.timeline().room()
108    }
109
110    /// A unique ID for this `TimelineItem` in the local timeline.
111    fn timeline_id(&self) -> String;
112}
113
114impl<O: IsA<TimelineItem>> TimelineItemExt for O {
115    fn timeline(&self) -> Timeline {
116        self.upcast_ref().timeline()
117    }
118
119    fn timeline_id(&self) -> String {
120        self.upcast_ref().timeline_id()
121    }
122}
123
124/// Public trait that must be implemented for everything that derives from
125/// `TimelineItem`.
126///
127/// Overriding a method from this Trait overrides also its behavior in
128/// `TimelineItemExt`.
129pub(crate) trait TimelineItemImpl: ObjectImpl {}
130
131// Make `TimelineItem` subclassable.
132unsafe impl<T> IsSubclassable<T> for TimelineItem
133where
134    T: TimelineItemImpl,
135    T::Type: IsA<TimelineItem>,
136{
137    fn class_init(class: &mut glib::Class<Self>) {
138        Self::parent_class_init::<T>(class.upcast_ref_mut());
139    }
140}