mirror of
https://github.com/matrix-org/matrix-rust-sdk.git
synced 2026-05-16 03:55:42 -04:00
ui: Move timeline item creation helpers into TimelineInnerState
This commit is contained in:
committed by
Jonas Platte
parent
e5f021f5cf
commit
7dfe8cedbb
@@ -14,7 +14,6 @@
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use chrono::{Datelike, Local, TimeZone};
|
||||
use eyeball_im::{ObservableVector, ObservableVectorEntry};
|
||||
use indexmap::{map::Entry, IndexMap};
|
||||
use matrix_sdk::deserialized_responses::EncryptionInfo;
|
||||
@@ -50,13 +49,16 @@ use super::{
|
||||
RemoteEventTimelineItem,
|
||||
},
|
||||
find_read_marker,
|
||||
item::{new_timeline_item, timeline_item},
|
||||
item::timeline_item,
|
||||
read_receipts::maybe_add_implicit_read_receipt,
|
||||
rfind_event_by_id, rfind_event_item, EventTimelineItem, Message, OtherState, ReactionGroup,
|
||||
ReactionSenderData, Sticker, TimelineDetails, TimelineInnerState, TimelineItem,
|
||||
TimelineItemContent, VirtualTimelineItem, DEFAULT_SANITIZER_MODE,
|
||||
};
|
||||
use crate::{events::SyncTimelineEventWithoutContent, timeline::InReplyToDetails};
|
||||
use crate::{
|
||||
events::SyncTimelineEventWithoutContent,
|
||||
timeline::{timestamp_to_date, InReplyToDetails},
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(super) enum Flow {
|
||||
@@ -713,26 +715,22 @@ impl<'a> TimelineEventHandler<'a> {
|
||||
{
|
||||
let old_ts = latest_event.timestamp();
|
||||
|
||||
if let Some(day_divider_item) = maybe_create_day_divider_from_timestamps(
|
||||
old_ts,
|
||||
timestamp,
|
||||
&mut self.state.next_internal_id,
|
||||
) {
|
||||
if let Some(day_divider_item) =
|
||||
self.state.maybe_create_day_divider_from_timestamps(old_ts, timestamp)
|
||||
{
|
||||
trace!("Adding day divider (local)");
|
||||
self.state.items.push_back(day_divider_item);
|
||||
}
|
||||
} else {
|
||||
// If there is no event item, there is no day divider yet.
|
||||
trace!("Adding first day divider (local)");
|
||||
self.state.items.push_back(new_timeline_item(
|
||||
VirtualTimelineItem::DayDivider(timestamp),
|
||||
&mut self.state.next_internal_id,
|
||||
));
|
||||
let day_divider =
|
||||
self.state.new_timeline_item(VirtualTimelineItem::DayDivider(timestamp));
|
||||
self.state.items.push_back(day_divider);
|
||||
}
|
||||
|
||||
self.state
|
||||
.items
|
||||
.push_back(new_timeline_item(item, &mut self.state.next_internal_id));
|
||||
let item = self.state.new_timeline_item(item);
|
||||
self.state.items.push_back(item);
|
||||
}
|
||||
|
||||
Flow::Remote { position: TimelineItemPosition::Start, event_id, .. } => {
|
||||
@@ -753,19 +751,16 @@ impl<'a> TimelineEventHandler<'a> {
|
||||
if let Some(VirtualTimelineItem::DayDivider(divider_ts)) =
|
||||
self.state.items.front().and_then(|item| item.as_virtual())
|
||||
{
|
||||
if let Some(day_divider_item) = maybe_create_day_divider_from_timestamps(
|
||||
*divider_ts,
|
||||
timestamp,
|
||||
&mut self.state.next_internal_id,
|
||||
) {
|
||||
if let Some(day_divider_item) =
|
||||
self.state.maybe_create_day_divider_from_timestamps(*divider_ts, timestamp)
|
||||
{
|
||||
self.state.items.push_front(day_divider_item);
|
||||
}
|
||||
} else {
|
||||
// The list must always start with a day divider.
|
||||
self.state.items.push_front(new_timeline_item(
|
||||
VirtualTimelineItem::DayDivider(timestamp),
|
||||
&mut self.state.next_internal_id,
|
||||
));
|
||||
let day_divider =
|
||||
self.state.new_timeline_item(VirtualTimelineItem::DayDivider(timestamp));
|
||||
self.state.items.push_front(day_divider);
|
||||
}
|
||||
|
||||
if self.track_read_receipts {
|
||||
@@ -778,9 +773,8 @@ impl<'a> TimelineEventHandler<'a> {
|
||||
);
|
||||
}
|
||||
|
||||
self.state
|
||||
.items
|
||||
.insert(1, new_timeline_item(item, &mut self.state.next_internal_id));
|
||||
let item = self.state.new_timeline_item(item);
|
||||
self.state.items.insert(1, item);
|
||||
}
|
||||
|
||||
Flow::Remote {
|
||||
@@ -921,11 +915,7 @@ impl<'a> TimelineEventHandler<'a> {
|
||||
// If a day divider was removed for an item about to be moved and we
|
||||
// now need to add a new one, reuse the previous one's ID.
|
||||
Some(day_divider_id) => day_divider_id,
|
||||
None => {
|
||||
let internal_id = self.state.next_internal_id;
|
||||
self.state.next_internal_id += 1;
|
||||
internal_id
|
||||
}
|
||||
None => self.state.next_internal_id(),
|
||||
};
|
||||
|
||||
let day_divider_item =
|
||||
@@ -941,10 +931,8 @@ impl<'a> TimelineEventHandler<'a> {
|
||||
} else {
|
||||
// If there is no event item, there is no day divider yet.
|
||||
trace!("Adding first day divider (remote)");
|
||||
let new_day_divider = new_timeline_item(
|
||||
VirtualTimelineItem::DayDivider(timestamp),
|
||||
&mut self.state.next_internal_id,
|
||||
);
|
||||
let new_day_divider =
|
||||
self.state.new_timeline_item(VirtualTimelineItem::DayDivider(timestamp));
|
||||
if should_push {
|
||||
self.state.items.push_back(new_day_divider);
|
||||
} else {
|
||||
@@ -968,11 +956,7 @@ impl<'a> TimelineEventHandler<'a> {
|
||||
// echo) was removed and we now need to add it again, reuse
|
||||
// the previous item's ID.
|
||||
Some(id) => id,
|
||||
None => {
|
||||
let internal_id = self.state.next_internal_id;
|
||||
self.state.next_internal_id += 1;
|
||||
internal_id
|
||||
}
|
||||
None => self.state.next_internal_id(),
|
||||
};
|
||||
|
||||
trace!("Adding new remote timeline item after all non-pending events");
|
||||
@@ -987,9 +971,8 @@ impl<'a> TimelineEventHandler<'a> {
|
||||
#[cfg(feature = "e2e-encryption")]
|
||||
Flow::Remote { position: TimelineItemPosition::Update(idx), .. } => {
|
||||
trace!("Updating timeline item at position {idx}");
|
||||
self.state
|
||||
.items
|
||||
.set(*idx, new_timeline_item(item, &mut self.state.next_internal_id));
|
||||
let item = self.state.new_timeline_item(item);
|
||||
self.state.items.set(*idx, item);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1101,34 +1084,3 @@ fn _update_timeline_item(
|
||||
debug!("Timeline item not found, discarding {action}");
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq)]
|
||||
struct Date {
|
||||
year: i32,
|
||||
month: u32,
|
||||
day: u32,
|
||||
}
|
||||
|
||||
/// Converts a timestamp since Unix Epoch to a year, month and day.
|
||||
fn timestamp_to_date(ts: MilliSecondsSinceUnixEpoch) -> Date {
|
||||
let datetime = Local
|
||||
.timestamp_millis_opt(ts.0.into())
|
||||
// Only returns `None` if date is after Dec 31, 262143 BCE.
|
||||
.single()
|
||||
// Fallback to the current date to avoid issues with malicious
|
||||
// homeservers.
|
||||
.unwrap_or_else(Local::now);
|
||||
|
||||
Date { year: datetime.year(), month: datetime.month(), day: datetime.day() }
|
||||
}
|
||||
|
||||
/// Returns a new day divider item for the new timestamp if it is on a different
|
||||
/// day than the old timestamp
|
||||
fn maybe_create_day_divider_from_timestamps(
|
||||
old_ts: MilliSecondsSinceUnixEpoch,
|
||||
new_ts: MilliSecondsSinceUnixEpoch,
|
||||
next_internal_id: &mut u64,
|
||||
) -> Option<Arc<TimelineItem>> {
|
||||
(timestamp_to_date(old_ts) != timestamp_to_date(new_ts))
|
||||
.then(|| new_timeline_item(VirtualTimelineItem::DayDivider(new_ts), next_internal_id))
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ use super::{
|
||||
compare_events_positions,
|
||||
event_handler::{HandleEventResult, TimelineItemPosition},
|
||||
event_item::EventItemIdentifier,
|
||||
item::{new_timeline_item, timeline_item},
|
||||
item::timeline_item,
|
||||
reactions::ReactionToggleResult,
|
||||
rfind_event_by_id, rfind_event_item,
|
||||
traits::RoomDataProvider,
|
||||
@@ -1029,8 +1029,8 @@ async fn fetch_replied_to_event(
|
||||
});
|
||||
let event_item = item.with_content(TimelineItemContent::Message(reply), None);
|
||||
|
||||
let state_ref = &mut *state;
|
||||
state_ref.items.set(index, new_timeline_item(event_item, &mut state_ref.next_internal_id));
|
||||
let new_timeline_item = state.new_timeline_item(event_item);
|
||||
state.items.set(index, new_timeline_item);
|
||||
|
||||
// Don't hold the state lock while the network request is made
|
||||
drop(state);
|
||||
|
||||
@@ -41,16 +41,17 @@ use crate::{
|
||||
event_item::EventItemIdentifier,
|
||||
item::timeline_item,
|
||||
reactions::{ReactionToggleResult, Reactions},
|
||||
rfind_event_item,
|
||||
rfind_event_item, timestamp_to_date,
|
||||
traits::RoomDataProvider,
|
||||
AnnotationKey, Error as TimelineError, Profile, ReactionSenderData, TimelineItem,
|
||||
TimelineItemKind, VirtualTimelineItem,
|
||||
},
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(in crate::timeline) struct TimelineInnerState {
|
||||
pub items: ObservableVector<Arc<TimelineItem>>,
|
||||
pub next_internal_id: u64,
|
||||
next_internal_id: u64,
|
||||
pub reactions: Reactions,
|
||||
pub fully_read_event: Option<OwnedEventId>,
|
||||
/// Whether the fully-read marker item should try to be updated when an
|
||||
@@ -87,6 +88,27 @@ impl TimelineInnerState {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn next_internal_id(&mut self) -> u64 {
|
||||
let val = self.next_internal_id;
|
||||
self.next_internal_id += 1;
|
||||
val
|
||||
}
|
||||
|
||||
pub fn new_timeline_item(&mut self, kind: impl Into<TimelineItemKind>) -> Arc<TimelineItem> {
|
||||
timeline_item(kind, self.next_internal_id())
|
||||
}
|
||||
|
||||
/// Returns a new day divider item for the new timestamp if it is on a
|
||||
/// different day than the old timestamp
|
||||
pub fn maybe_create_day_divider_from_timestamps(
|
||||
&mut self,
|
||||
old_ts: MilliSecondsSinceUnixEpoch,
|
||||
new_ts: MilliSecondsSinceUnixEpoch,
|
||||
) -> Option<Arc<TimelineItem>> {
|
||||
(timestamp_to_date(old_ts) != timestamp_to_date(new_ts))
|
||||
.then(|| self.new_timeline_item(VirtualTimelineItem::DayDivider(new_ts)))
|
||||
}
|
||||
|
||||
pub async fn handle_sync_timeline<P: RoomDataProvider>(
|
||||
&mut self,
|
||||
timeline: Timeline,
|
||||
|
||||
@@ -119,12 +119,3 @@ pub(crate) fn timeline_item(
|
||||
) -> Arc<TimelineItem> {
|
||||
Arc::new(TimelineItem { kind: kind.into(), internal_id })
|
||||
}
|
||||
|
||||
pub(crate) fn new_timeline_item(
|
||||
kind: impl Into<TimelineItemKind>,
|
||||
next_internal_id: &mut u64,
|
||||
) -> Arc<TimelineItem> {
|
||||
let internal_id = *next_internal_id;
|
||||
*next_internal_id += 1;
|
||||
timeline_item(kind, internal_id)
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
use std::{ops::Deref, pin::Pin, sync::Arc, task::Poll, time::Duration};
|
||||
|
||||
use async_std::sync::{Condvar, Mutex};
|
||||
use chrono::{Datelike, Local, TimeZone};
|
||||
use eyeball::{SharedObservable, Subscriber};
|
||||
use eyeball_im::VectorDiff;
|
||||
use futures_core::Stream;
|
||||
@@ -43,7 +44,7 @@ use ruma::{
|
||||
room::{message::sanitize::HtmlSanitizerMode, redaction::RoomRedactionEventContent},
|
||||
AnyMessageLikeEventContent,
|
||||
},
|
||||
EventId, OwnedEventId, OwnedTransactionId, TransactionId, UserId,
|
||||
EventId, MilliSecondsSinceUnixEpoch, OwnedEventId, OwnedTransactionId, TransactionId, UserId,
|
||||
};
|
||||
use thiserror::Error;
|
||||
use tokio::sync::mpsc::Sender;
|
||||
@@ -819,3 +820,23 @@ fn compare_events_positions(
|
||||
Some(RelativePosition::After)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq)]
|
||||
struct Date {
|
||||
year: i32,
|
||||
month: u32,
|
||||
day: u32,
|
||||
}
|
||||
|
||||
/// Converts a timestamp since Unix Epoch to a year, month and day.
|
||||
fn timestamp_to_date(ts: MilliSecondsSinceUnixEpoch) -> Date {
|
||||
let datetime = Local
|
||||
.timestamp_millis_opt(ts.0.into())
|
||||
// Only returns `None` if date is after Dec 31, 262143 BCE.
|
||||
.single()
|
||||
// Fallback to the current date to avoid issues with malicious
|
||||
// homeservers.
|
||||
.unwrap_or_else(Local::now);
|
||||
|
||||
Date { year: datetime.year(), month: datetime.month(), day: datetime.day() }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user