Move timeline API into a new crate
… aimed at interactive user interfaces.
This commit is contained in:
committed by
Jonas Platte
parent
91d97cd588
commit
cfc8effa66
Generated
+28
-4
@@ -1546,6 +1546,7 @@ dependencies = [
|
||||
"clap 4.2.4",
|
||||
"futures",
|
||||
"matrix-sdk",
|
||||
"matrix-sdk-ui",
|
||||
"tokio",
|
||||
"tracing-subscriber",
|
||||
"url",
|
||||
@@ -2615,7 +2616,6 @@ dependencies = [
|
||||
"backoff",
|
||||
"bytes",
|
||||
"bytesize",
|
||||
"chrono",
|
||||
"ctor 0.2.0",
|
||||
"dashmap",
|
||||
"dirs",
|
||||
@@ -2632,7 +2632,6 @@ dependencies = [
|
||||
"hyper",
|
||||
"image 0.24.6",
|
||||
"imbl",
|
||||
"indexmap",
|
||||
"matrix-sdk-base",
|
||||
"matrix-sdk-common",
|
||||
"matrix-sdk-indexeddb",
|
||||
@@ -2640,8 +2639,6 @@ dependencies = [
|
||||
"matrix-sdk-test",
|
||||
"mime",
|
||||
"mime2ext",
|
||||
"once_cell",
|
||||
"pin-project-lite",
|
||||
"rand 0.8.5",
|
||||
"reqwest",
|
||||
"ruma",
|
||||
@@ -2861,6 +2858,7 @@ dependencies = [
|
||||
"futures-util",
|
||||
"log-panics",
|
||||
"matrix-sdk",
|
||||
"matrix-sdk-ui",
|
||||
"mime",
|
||||
"once_cell",
|
||||
"opentelemetry",
|
||||
@@ -3032,6 +3030,32 @@ dependencies = [
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "matrix-sdk-ui"
|
||||
version = "0.6.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"assert_matches",
|
||||
"async-trait",
|
||||
"chrono",
|
||||
"eyeball-im",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"imbl",
|
||||
"indexmap",
|
||||
"matrix-sdk",
|
||||
"matrix-sdk-test",
|
||||
"mime",
|
||||
"once_cell",
|
||||
"pin-project-lite",
|
||||
"ruma",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.5.0"
|
||||
|
||||
@@ -28,6 +28,7 @@ eyeball-im = { workspace = true }
|
||||
extension-trait = "1.0.1"
|
||||
futures-core = "0.3.17"
|
||||
futures-util = { version = "0.3.17", default-features = false }
|
||||
matrix-sdk-ui = { path = "../../crates/matrix-sdk-ui", features = ["experimental-sliding-sync"] }
|
||||
mime = "0.3.16"
|
||||
# FIXME: we currently can't feature flag anything in the api.udl, therefore we must enforce experimental-sliding-sync being exposed here..
|
||||
# see https://github.com/matrix-org/matrix-rust-sdk/issues/1014
|
||||
@@ -62,7 +63,6 @@ default-features = false
|
||||
features = [
|
||||
"anyhow",
|
||||
"experimental-sliding-sync",
|
||||
"experimental-timeline",
|
||||
"e2e-encryption",
|
||||
"markdown",
|
||||
"socks",
|
||||
@@ -76,7 +76,6 @@ default-features = false
|
||||
features = [
|
||||
"anyhow",
|
||||
"experimental-sliding-sync",
|
||||
"experimental-timeline",
|
||||
"e2e-encryption",
|
||||
"markdown",
|
||||
"native-tls",
|
||||
|
||||
@@ -45,10 +45,8 @@ use self::{client::Client, error::ClientError};
|
||||
pub static RUNTIME: Lazy<Runtime> =
|
||||
Lazy::new(|| Runtime::new().expect("Can't start Tokio runtime"));
|
||||
|
||||
pub use matrix_sdk::{
|
||||
room::timeline::PaginationOutcome,
|
||||
ruma::{api::client::account::register, UserId},
|
||||
};
|
||||
pub use matrix_sdk::ruma::{api::client::account::register, UserId};
|
||||
pub use matrix_sdk_ui::timeline::PaginationOutcome;
|
||||
pub use platform::*;
|
||||
|
||||
pub use self::{
|
||||
@@ -60,5 +58,5 @@ uniffi::include_scaffolding!("api");
|
||||
|
||||
#[uniffi::export]
|
||||
pub fn sdk_git_sha() -> String {
|
||||
env!("VERGEN_GIT_SHA").to_string()
|
||||
env!("VERGEN_GIT_SHA").to_owned()
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ use matrix_sdk::{
|
||||
AttachmentConfig, AttachmentInfo, BaseAudioInfo, BaseFileInfo, BaseImageInfo,
|
||||
BaseThumbnailInfo, BaseVideoInfo, Thumbnail,
|
||||
},
|
||||
room::{timeline::Timeline, Receipts, Room as SdkRoom},
|
||||
room::{Receipts, Room as SdkRoom},
|
||||
ruma::{
|
||||
api::client::{receipt::create_receipt::v3::ReceiptType, room::report_content},
|
||||
events::{
|
||||
@@ -26,6 +26,7 @@ use matrix_sdk::{
|
||||
},
|
||||
RoomMemberships,
|
||||
};
|
||||
use matrix_sdk_ui::timeline::{RoomExt, Timeline};
|
||||
use mime::Mime;
|
||||
use tracing::error;
|
||||
|
||||
@@ -806,9 +807,9 @@ pub enum PaginationOptions {
|
||||
UntilNumItems { event_limit: u16, items: u16 },
|
||||
}
|
||||
|
||||
impl From<PaginationOptions> for matrix_sdk::room::timeline::PaginationOptions<'static> {
|
||||
impl From<PaginationOptions> for matrix_sdk_ui::timeline::PaginationOptions<'static> {
|
||||
fn from(value: PaginationOptions) -> Self {
|
||||
use matrix_sdk::room::timeline::PaginationOptions as Opts;
|
||||
use matrix_sdk_ui::timeline::PaginationOptions as Opts;
|
||||
match value {
|
||||
PaginationOptions::SingleRequest { event_limit } => Opts::single_request(event_limit),
|
||||
PaginationOptions::UntilNumItems { event_limit, items } => {
|
||||
|
||||
@@ -11,10 +11,11 @@ use matrix_sdk::ruma::{
|
||||
assign, IdParseError, OwnedRoomId, RoomId,
|
||||
};
|
||||
pub use matrix_sdk::{
|
||||
room::timeline::Timeline, ruma::api::client::sync::sync_events::v4::SyncRequestListFilters,
|
||||
Client as MatrixClient, LoopCtrl, RoomListEntry as MatrixRoomEntry,
|
||||
SlidingSyncBuilder as MatrixSlidingSyncBuilder, SlidingSyncMode, SlidingSyncState,
|
||||
ruma::api::client::sync::sync_events::v4::SyncRequestListFilters, Client as MatrixClient,
|
||||
LoopCtrl, RoomListEntry as MatrixRoomEntry, SlidingSyncBuilder as MatrixSlidingSyncBuilder,
|
||||
SlidingSyncMode, SlidingSyncState,
|
||||
};
|
||||
use matrix_sdk_ui::timeline::SlidingSyncRoomExt;
|
||||
use tokio::task::JoinHandle;
|
||||
use tracing::{debug, error, warn};
|
||||
use url::Url;
|
||||
|
||||
@@ -3,11 +3,11 @@ use std::{collections::HashMap, sync::Arc, time::Duration};
|
||||
use anyhow::bail;
|
||||
use extension_trait::extension_trait;
|
||||
use eyeball_im::VectorDiff;
|
||||
pub use matrix_sdk::ruma::events::room::{message::RoomMessageEventContent, MediaSource};
|
||||
use matrix_sdk::{
|
||||
attachment::{BaseAudioInfo, BaseFileInfo, BaseImageInfo, BaseThumbnailInfo, BaseVideoInfo},
|
||||
room::timeline::{Profile, TimelineDetails},
|
||||
use matrix_sdk::attachment::{
|
||||
BaseAudioInfo, BaseFileInfo, BaseImageInfo, BaseThumbnailInfo, BaseVideoInfo,
|
||||
};
|
||||
pub use matrix_sdk::ruma::events::room::{message::RoomMessageEventContent, MediaSource};
|
||||
use matrix_sdk_ui::timeline::{Profile, TimelineDetails};
|
||||
use ruma::UInt;
|
||||
use tracing::warn;
|
||||
|
||||
@@ -45,7 +45,7 @@ pub enum TimelineDiff {
|
||||
}
|
||||
|
||||
impl TimelineDiff {
|
||||
pub(crate) fn new(inner: VectorDiff<Arc<matrix_sdk::room::timeline::TimelineItem>>) -> Self {
|
||||
pub(crate) fn new(inner: VectorDiff<Arc<matrix_sdk_ui::timeline::TimelineItem>>) -> Self {
|
||||
match inner {
|
||||
VectorDiff::Append { values } => {
|
||||
Self::Append { values: values.into_iter().map(TimelineItem::from_arc).collect() }
|
||||
@@ -184,10 +184,10 @@ pub enum TimelineChange {
|
||||
|
||||
#[repr(transparent)]
|
||||
#[derive(Clone)]
|
||||
pub struct TimelineItem(pub(crate) matrix_sdk::room::timeline::TimelineItem);
|
||||
pub struct TimelineItem(pub(crate) matrix_sdk_ui::timeline::TimelineItem);
|
||||
|
||||
impl TimelineItem {
|
||||
pub(crate) fn from_arc(arc: Arc<matrix_sdk::room::timeline::TimelineItem>) -> Arc<Self> {
|
||||
pub(crate) fn from_arc(arc: Arc<matrix_sdk_ui::timeline::TimelineItem>) -> Arc<Self> {
|
||||
// SAFETY: This is valid because Self is a repr(transparent) wrapper
|
||||
// around the other Timeline type.
|
||||
unsafe { Arc::from_raw(Arc::into_raw(arc) as _) }
|
||||
@@ -197,14 +197,14 @@ impl TimelineItem {
|
||||
#[uniffi::export]
|
||||
impl TimelineItem {
|
||||
pub fn as_event(self: Arc<Self>) -> Option<Arc<EventTimelineItem>> {
|
||||
use matrix_sdk::room::timeline::TimelineItem as Item;
|
||||
use matrix_sdk_ui::timeline::TimelineItem as Item;
|
||||
unwrap_or_clone_arc_into_variant!(self, .0, Item::Event(evt) => {
|
||||
Arc::new(EventTimelineItem(evt))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn as_virtual(self: Arc<Self>) -> Option<VirtualTimelineItem> {
|
||||
use matrix_sdk::room::timeline::{TimelineItem as Item, VirtualTimelineItem as VItem};
|
||||
use matrix_sdk_ui::timeline::{TimelineItem as Item, VirtualTimelineItem as VItem};
|
||||
match &self.0 {
|
||||
Item::Virtual(VItem::DayDivider(ts)) => {
|
||||
Some(VirtualTimelineItem::DayDivider { ts: ts.0.into() })
|
||||
@@ -233,9 +233,9 @@ pub enum EventSendState {
|
||||
Sent { event_id: String },
|
||||
}
|
||||
|
||||
impl From<&matrix_sdk::room::timeline::EventSendState> for EventSendState {
|
||||
fn from(value: &matrix_sdk::room::timeline::EventSendState) -> Self {
|
||||
use matrix_sdk::room::timeline::EventSendState::*;
|
||||
impl From<&matrix_sdk_ui::timeline::EventSendState> for EventSendState {
|
||||
fn from(value: &matrix_sdk_ui::timeline::EventSendState) -> Self {
|
||||
use matrix_sdk_ui::timeline::EventSendState::*;
|
||||
|
||||
match value {
|
||||
NotSentYet => Self::NotSentYet,
|
||||
@@ -246,7 +246,7 @@ impl From<&matrix_sdk::room::timeline::EventSendState> for EventSendState {
|
||||
}
|
||||
|
||||
#[derive(uniffi::Object)]
|
||||
pub struct EventTimelineItem(pub(crate) matrix_sdk::room::timeline::EventTimelineItem);
|
||||
pub struct EventTimelineItem(pub(crate) matrix_sdk_ui::timeline::EventTimelineItem);
|
||||
|
||||
#[uniffi::export]
|
||||
impl EventTimelineItem {
|
||||
@@ -357,12 +357,12 @@ impl From<&TimelineDetails<Profile>> for ProfileDetails {
|
||||
}
|
||||
|
||||
#[derive(Clone, uniffi::Object)]
|
||||
pub struct TimelineItemContent(matrix_sdk::room::timeline::TimelineItemContent);
|
||||
pub struct TimelineItemContent(matrix_sdk_ui::timeline::TimelineItemContent);
|
||||
|
||||
#[uniffi::export]
|
||||
impl TimelineItemContent {
|
||||
pub fn kind(&self) -> TimelineItemContentKind {
|
||||
use matrix_sdk::room::timeline::TimelineItemContent as Content;
|
||||
use matrix_sdk_ui::timeline::TimelineItemContent as Content;
|
||||
|
||||
match &self.0 {
|
||||
Content::Message(_) => TimelineItemContentKind::Message,
|
||||
@@ -424,7 +424,7 @@ impl TimelineItemContent {
|
||||
}
|
||||
|
||||
pub fn as_message(self: Arc<Self>) -> Option<Arc<Message>> {
|
||||
use matrix_sdk::room::timeline::TimelineItemContent as Content;
|
||||
use matrix_sdk_ui::timeline::TimelineItemContent as Content;
|
||||
unwrap_or_clone_arc_into_variant!(self, .0, Content::Message(msg) => Arc::new(Message(msg)))
|
||||
}
|
||||
}
|
||||
@@ -467,7 +467,7 @@ pub enum TimelineItemContentKind {
|
||||
}
|
||||
|
||||
#[derive(Clone, uniffi::Object)]
|
||||
pub struct Message(matrix_sdk::room::timeline::Message);
|
||||
pub struct Message(matrix_sdk_ui::timeline::Message);
|
||||
|
||||
#[uniffi::export]
|
||||
impl Message {
|
||||
@@ -838,8 +838,8 @@ pub struct InReplyToDetails {
|
||||
event: RepliedToEventDetails,
|
||||
}
|
||||
|
||||
impl From<&matrix_sdk::room::timeline::InReplyToDetails> for InReplyToDetails {
|
||||
fn from(inner: &matrix_sdk::room::timeline::InReplyToDetails) -> Self {
|
||||
impl From<&matrix_sdk_ui::timeline::InReplyToDetails> for InReplyToDetails {
|
||||
fn from(inner: &matrix_sdk_ui::timeline::InReplyToDetails) -> Self {
|
||||
let event_id = inner.event_id.to_string();
|
||||
let event = match &inner.event {
|
||||
TimelineDetails::Unavailable => RepliedToEventDetails::Unavailable,
|
||||
@@ -882,8 +882,8 @@ pub enum EncryptedMessage {
|
||||
}
|
||||
|
||||
impl EncryptedMessage {
|
||||
fn new(msg: &matrix_sdk::room::timeline::EncryptedMessage) -> Self {
|
||||
use matrix_sdk::room::timeline::EncryptedMessage as Message;
|
||||
fn new(msg: &matrix_sdk_ui::timeline::EncryptedMessage) -> Self {
|
||||
use matrix_sdk_ui::timeline::EncryptedMessage as Message;
|
||||
|
||||
match msg {
|
||||
Message::OlmV1Curve25519AesSha2 { sender_key } => {
|
||||
@@ -975,9 +975,9 @@ pub enum MembershipChange {
|
||||
NotImplemented,
|
||||
}
|
||||
|
||||
impl From<matrix_sdk::room::timeline::MembershipChange> for MembershipChange {
|
||||
fn from(membership_change: matrix_sdk::room::timeline::MembershipChange) -> Self {
|
||||
use matrix_sdk::room::timeline::MembershipChange as Change;
|
||||
impl From<matrix_sdk_ui::timeline::MembershipChange> for MembershipChange {
|
||||
fn from(membership_change: matrix_sdk_ui::timeline::MembershipChange) -> Self {
|
||||
use matrix_sdk_ui::timeline::MembershipChange as Change;
|
||||
match membership_change {
|
||||
Change::None => Self::None,
|
||||
Change::Error => Self::Error,
|
||||
@@ -1025,12 +1025,11 @@ pub enum OtherState {
|
||||
Custom { event_type: String },
|
||||
}
|
||||
|
||||
impl From<&matrix_sdk::room::timeline::AnyOtherFullStateEventContent> for OtherState {
|
||||
fn from(content: &matrix_sdk::room::timeline::AnyOtherFullStateEventContent) -> Self {
|
||||
use matrix_sdk::{
|
||||
room::timeline::AnyOtherFullStateEventContent as Content,
|
||||
ruma::events::FullStateEventContent as FullContent,
|
||||
};
|
||||
impl From<&matrix_sdk_ui::timeline::AnyOtherFullStateEventContent> for OtherState {
|
||||
fn from(content: &matrix_sdk_ui::timeline::AnyOtherFullStateEventContent) -> Self {
|
||||
use matrix_sdk::ruma::events::FullStateEventContent as FullContent;
|
||||
use matrix_sdk_ui::timeline::AnyOtherFullStateEventContent as Content;
|
||||
|
||||
match content {
|
||||
Content::PolicyRuleRoom(_) => Self::PolicyRuleRoom,
|
||||
Content::PolicyRuleServer(_) => Self::PolicyRuleServer,
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
[package]
|
||||
name = "matrix-sdk-ui"
|
||||
version = "0.6.0"
|
||||
edition = "2021"
|
||||
|
||||
[features]
|
||||
default = ["e2e-encryption"]
|
||||
e2e-encryption = ["matrix-sdk/e2e-encryption"]
|
||||
experimental-sliding-sync = ["matrix-sdk/experimental-sliding-sync"]
|
||||
|
||||
[dependencies]
|
||||
async-trait = { workspace = true }
|
||||
chrono = "0.4.23"
|
||||
eyeball-im = { workspace = true }
|
||||
futures-core = "0.3.21"
|
||||
futures-util = { workspace = true }
|
||||
imbl = { version = "2.0.0", features = ["serde"] }
|
||||
indexmap = "1.9.1"
|
||||
matrix-sdk = { version = "0.6.2", path = "../matrix-sdk", default-features = false }
|
||||
mime = "0.3.16"
|
||||
once_cell = { workspace = true }
|
||||
pin-project-lite = "0.2.9"
|
||||
ruma = { workspace = true, features = ["unstable-msc2677", "unstable-sanitize"] }
|
||||
serde = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
tokio = { workspace = true }
|
||||
tracing = { workspace = true, features = ["attributes"] }
|
||||
|
||||
[dev-dependencies]
|
||||
anyhow = { workspace = true }
|
||||
assert_matches = { workspace = true }
|
||||
matrix-sdk-test = { version = "0.6.0", path = "../../testing/matrix-sdk-test" }
|
||||
@@ -0,0 +1,18 @@
|
||||
// Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
mod events;
|
||||
pub mod timeline;
|
||||
|
||||
pub use self::timeline::Timeline;
|
||||
+11
-8
@@ -15,7 +15,10 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use imbl::Vector;
|
||||
use matrix_sdk_base::deserialized_responses::{EncryptionInfo, SyncTimelineEvent};
|
||||
use matrix_sdk::{
|
||||
deserialized_responses::{EncryptionInfo, SyncTimelineEvent},
|
||||
room,
|
||||
};
|
||||
use ruma::{
|
||||
events::receipt::{ReceiptThread, ReceiptType, SyncReceiptEvent},
|
||||
push::Action,
|
||||
@@ -26,7 +29,6 @@ use tracing::error;
|
||||
#[cfg(feature = "e2e-encryption")]
|
||||
use super::to_device::{handle_forwarded_room_key_event, handle_room_key_event};
|
||||
use super::{inner::TimelineInner, Timeline, TimelineEventHandlerHandles};
|
||||
use crate::room;
|
||||
|
||||
/// Builder that allows creating and configuring various parts of a
|
||||
/// [`Timeline`].
|
||||
@@ -120,6 +122,7 @@ impl TimelineBuilder {
|
||||
|
||||
let inner = Arc::new(inner);
|
||||
let room = inner.room();
|
||||
let client = room.client();
|
||||
|
||||
let timeline_event_handle = room.add_event_handler({
|
||||
let inner = inner.clone();
|
||||
@@ -133,14 +136,15 @@ impl TimelineBuilder {
|
||||
|
||||
// Not using room.add_event_handler here because RoomKey events are
|
||||
// to-device events that are not received in the context of a room.
|
||||
|
||||
#[cfg(feature = "e2e-encryption")]
|
||||
let room_key_handle = room
|
||||
.client
|
||||
let room_key_handle = client
|
||||
.add_event_handler(handle_room_key_event(inner.clone(), room.room_id().to_owned()));
|
||||
#[cfg(feature = "e2e-encryption")]
|
||||
let forwarded_room_key_handle = room.client.add_event_handler(
|
||||
handle_forwarded_room_key_event(inner.clone(), room.room_id().to_owned()),
|
||||
);
|
||||
let forwarded_room_key_handle = client.add_event_handler(handle_forwarded_room_key_event(
|
||||
inner.clone(),
|
||||
room.room_id().to_owned(),
|
||||
));
|
||||
|
||||
let mut handles = vec![
|
||||
timeline_event_handle,
|
||||
@@ -176,7 +180,6 @@ impl TimelineBuilder {
|
||||
handles.push(read_receipts_handle);
|
||||
}
|
||||
|
||||
let client = room.client.clone();
|
||||
let timeline = Timeline {
|
||||
inner,
|
||||
start_token: Mutex::new(prev_token),
|
||||
+5
-7
@@ -17,7 +17,7 @@ use std::{collections::HashMap, sync::Arc};
|
||||
use chrono::{Datelike, Local, TimeZone};
|
||||
use eyeball_im::{ObservableVector, Vector};
|
||||
use indexmap::{map::Entry, IndexMap, IndexSet};
|
||||
use matrix_sdk_base::deserialized_responses::EncryptionInfo;
|
||||
use matrix_sdk::deserialized_responses::EncryptionInfo;
|
||||
use ruma::{
|
||||
events::{
|
||||
reaction::ReactionEventContent,
|
||||
@@ -49,13 +49,11 @@ use super::{
|
||||
},
|
||||
find_read_marker,
|
||||
read_receipts::maybe_add_implicit_read_receipt,
|
||||
rfind_event_by_id, rfind_event_item, EventTimelineItem, Message, ReactionGroup,
|
||||
TimelineDetails, TimelineInnerState, TimelineItem, TimelineItemContent, VirtualTimelineItem,
|
||||
};
|
||||
use crate::{
|
||||
events::SyncTimelineEventWithoutContent,
|
||||
room::timeline::{MembershipChange, DEFAULT_SANITIZER_MODE},
|
||||
rfind_event_by_id, rfind_event_item, EventTimelineItem, MembershipChange, Message,
|
||||
ReactionGroup, TimelineDetails, TimelineInnerState, TimelineItem, TimelineItemContent,
|
||||
VirtualTimelineItem, DEFAULT_SANITIZER_MODE,
|
||||
};
|
||||
use crate::events::SyncTimelineEventWithoutContent;
|
||||
|
||||
pub(super) enum Flow {
|
||||
Local {
|
||||
+26
-32
@@ -2,7 +2,7 @@ use std::{fmt, ops::Deref, sync::Arc};
|
||||
|
||||
use imbl::{vector, Vector};
|
||||
use indexmap::IndexMap;
|
||||
use matrix_sdk_base::deserialized_responses::TimelineEvent;
|
||||
use matrix_sdk::{deserialized_responses::TimelineEvent, Result};
|
||||
use ruma::{
|
||||
events::{
|
||||
policy::rule::{
|
||||
@@ -43,11 +43,8 @@ use ruma::{
|
||||
use tracing::{debug, error};
|
||||
|
||||
use super::{EventTimelineItem, Profile, TimelineDetails};
|
||||
use crate::{
|
||||
room::timeline::{
|
||||
inner::RoomDataProvider, Error as TimelineError, TimelineItem, DEFAULT_SANITIZER_MODE,
|
||||
},
|
||||
Result,
|
||||
use crate::timeline::{
|
||||
inner::RoomDataProvider, Error as TimelineError, TimelineItem, DEFAULT_SANITIZER_MODE,
|
||||
};
|
||||
|
||||
/// The content of an [`EventTimelineItem`][super::EventTimelineItem].
|
||||
@@ -123,14 +120,14 @@ impl TimelineItemContent {
|
||||
/// An `m.room.message` event or extensible event, including edits.
|
||||
#[derive(Clone)]
|
||||
pub struct Message {
|
||||
pub(in crate::room::timeline) msgtype: MessageType,
|
||||
pub(in crate::room::timeline) in_reply_to: Option<InReplyToDetails>,
|
||||
pub(in crate::room::timeline) edited: bool,
|
||||
pub(in crate::timeline) msgtype: MessageType,
|
||||
pub(in crate::timeline) in_reply_to: Option<InReplyToDetails>,
|
||||
pub(in crate::timeline) edited: bool,
|
||||
}
|
||||
|
||||
impl Message {
|
||||
/// Construct a `Message` from a `m.room.message` event.
|
||||
pub(in crate::room::timeline) fn from_event(
|
||||
pub(in crate::timeline) fn from_event(
|
||||
c: RoomMessageEventContent,
|
||||
relations: BundledMessageLikeRelations<AnySyncMessageLikeEvent>,
|
||||
timeline_items: &Vector<Arc<TimelineItem>>,
|
||||
@@ -216,10 +213,7 @@ impl Message {
|
||||
self.edited
|
||||
}
|
||||
|
||||
pub(in crate::room::timeline) fn with_in_reply_to(
|
||||
&self,
|
||||
in_reply_to: InReplyToDetails,
|
||||
) -> Self {
|
||||
pub(in crate::timeline) fn with_in_reply_to(&self, in_reply_to: InReplyToDetails) -> Self {
|
||||
Self { in_reply_to: Some(in_reply_to), ..self.clone() }
|
||||
}
|
||||
}
|
||||
@@ -248,16 +242,16 @@ pub struct InReplyToDetails {
|
||||
/// Use [`Timeline::fetch_details_for_event`] to fetch the data if it is
|
||||
/// unavailable.
|
||||
///
|
||||
/// [`Timeline::fetch_details_for_event`]: crate::room::timeline::Timeline::fetch_details_for_event
|
||||
/// [`Timeline::fetch_details_for_event`]: crate::Timeline::fetch_details_for_event
|
||||
pub event: TimelineDetails<Box<RepliedToEvent>>,
|
||||
}
|
||||
|
||||
/// An event that is replied to.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct RepliedToEvent {
|
||||
pub(in crate::room::timeline) message: Message,
|
||||
pub(in crate::room::timeline) sender: OwnedUserId,
|
||||
pub(in crate::room::timeline) sender_profile: TimelineDetails<Profile>,
|
||||
pub(in crate::timeline) message: Message,
|
||||
pub(in crate::timeline) sender: OwnedUserId,
|
||||
pub(in crate::timeline) sender_profile: TimelineDetails<Profile>,
|
||||
}
|
||||
|
||||
impl RepliedToEvent {
|
||||
@@ -293,19 +287,19 @@ impl RepliedToEvent {
|
||||
})
|
||||
}
|
||||
|
||||
pub(in crate::room::timeline) async fn try_from_timeline_event<P: RoomDataProvider>(
|
||||
pub(in crate::timeline) async fn try_from_timeline_event<P: RoomDataProvider>(
|
||||
timeline_event: TimelineEvent,
|
||||
room_data_provider: &P,
|
||||
) -> Result<Self> {
|
||||
) -> Result<Self, TimelineError> {
|
||||
let event = match timeline_event.event.deserialize() {
|
||||
Ok(AnyTimelineEvent::MessageLike(event)) => event,
|
||||
_ => {
|
||||
return Err(TimelineError::UnsupportedEvent.into());
|
||||
return Err(TimelineError::UnsupportedEvent);
|
||||
}
|
||||
};
|
||||
|
||||
let Some(AnyMessageLikeEventContent::RoomMessage(c)) = event.original_content() else {
|
||||
return Err(TimelineError::UnsupportedEvent.into());
|
||||
return Err(TimelineError::UnsupportedEvent);
|
||||
};
|
||||
|
||||
let message = Message::from_event(c, event.relations(), &vector![]);
|
||||
@@ -376,7 +370,7 @@ type ReactionGroupInner = IndexMap<(Option<OwnedTransactionId>, Option<OwnedEven
|
||||
/// This is a map of the event ID or transaction ID of the reactions to the ID
|
||||
/// of the sender of the reaction.
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct ReactionGroup(pub(in crate::room::timeline) ReactionGroupInner);
|
||||
pub struct ReactionGroup(pub(in crate::timeline) ReactionGroupInner);
|
||||
|
||||
impl ReactionGroup {
|
||||
/// The senders of the reactions in this group.
|
||||
@@ -396,7 +390,7 @@ impl Deref for ReactionGroup {
|
||||
/// An `m.sticker` event.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Sticker {
|
||||
pub(in crate::room::timeline) content: StickerEventContent,
|
||||
pub(in crate::timeline) content: StickerEventContent,
|
||||
}
|
||||
|
||||
impl Sticker {
|
||||
@@ -409,9 +403,9 @@ impl Sticker {
|
||||
/// An event changing a room membership.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct RoomMembershipChange {
|
||||
pub(in crate::room::timeline) user_id: OwnedUserId,
|
||||
pub(in crate::room::timeline) content: FullStateEventContent<RoomMemberEventContent>,
|
||||
pub(in crate::room::timeline) change: Option<MembershipChange>,
|
||||
pub(in crate::timeline) user_id: OwnedUserId,
|
||||
pub(in crate::timeline) content: FullStateEventContent<RoomMemberEventContent>,
|
||||
pub(in crate::timeline) change: Option<MembershipChange>,
|
||||
}
|
||||
|
||||
impl RoomMembershipChange {
|
||||
@@ -498,9 +492,9 @@ pub enum MembershipChange {
|
||||
/// membership is already `join`.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct MemberProfileChange {
|
||||
pub(in crate::room::timeline) user_id: OwnedUserId,
|
||||
pub(in crate::room::timeline) displayname_change: Option<Change<Option<String>>>,
|
||||
pub(in crate::room::timeline) avatar_url_change: Option<Change<Option<OwnedMxcUri>>>,
|
||||
pub(in crate::timeline) user_id: OwnedUserId,
|
||||
pub(in crate::timeline) displayname_change: Option<Change<Option<String>>>,
|
||||
pub(in crate::timeline) avatar_url_change: Option<Change<Option<OwnedMxcUri>>>,
|
||||
}
|
||||
|
||||
impl MemberProfileChange {
|
||||
@@ -654,8 +648,8 @@ impl AnyOtherFullStateEventContent {
|
||||
/// A state event that doesn't have its own variant.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct OtherState {
|
||||
pub(in crate::room::timeline) state_key: String,
|
||||
pub(in crate::room::timeline) content: AnyOtherFullStateEventContent,
|
||||
pub(in crate::timeline) state_key: String,
|
||||
pub(in crate::timeline) content: AnyOtherFullStateEventContent,
|
||||
}
|
||||
|
||||
impl OtherState {
|
||||
+1
-1
@@ -5,7 +5,7 @@ use super::EventSendState;
|
||||
/// An item for an event that was created locally and not yet echoed back by
|
||||
/// the homeserver.
|
||||
#[derive(Debug, Clone)]
|
||||
pub(in crate::room::timeline) struct LocalEventTimelineItem {
|
||||
pub(in crate::timeline) struct LocalEventTimelineItem {
|
||||
/// The send state of this local event.
|
||||
pub send_state: EventSendState,
|
||||
/// The transaction ID.
|
||||
+1
-3
@@ -15,7 +15,7 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use indexmap::IndexMap;
|
||||
use matrix_sdk_base::deserialized_responses::EncryptionInfo;
|
||||
use matrix_sdk::{deserialized_responses::EncryptionInfo, Error};
|
||||
use once_cell::sync::Lazy;
|
||||
use ruma::{
|
||||
events::{receipt::Receipt, room::message::MessageType, AnySyncTimelineEvent},
|
||||
@@ -24,8 +24,6 @@ use ruma::{
|
||||
UserId,
|
||||
};
|
||||
|
||||
use crate::Error;
|
||||
|
||||
mod content;
|
||||
mod local;
|
||||
mod remote;
|
||||
+3
-3
@@ -1,7 +1,7 @@
|
||||
use std::fmt;
|
||||
|
||||
use indexmap::IndexMap;
|
||||
use matrix_sdk_base::deserialized_responses::EncryptionInfo;
|
||||
use matrix_sdk::deserialized_responses::EncryptionInfo;
|
||||
use ruma::{
|
||||
events::{receipt::Receipt, AnySyncTimelineEvent},
|
||||
serde::Raw,
|
||||
@@ -12,7 +12,7 @@ use super::BundledReactions;
|
||||
|
||||
/// An item for an event that was received from the homeserver.
|
||||
#[derive(Clone)]
|
||||
pub(in crate::room::timeline) struct RemoteEventTimelineItem {
|
||||
pub(in crate::timeline) struct RemoteEventTimelineItem {
|
||||
/// The event ID.
|
||||
pub event_id: OwnedEventId,
|
||||
/// All bundled reactions about the event.
|
||||
@@ -66,7 +66,7 @@ impl RemoteEventTimelineItem {
|
||||
|
||||
/// Where we got an event from.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub(in crate::room::timeline) enum RemoteEventOrigin {
|
||||
pub(in crate::timeline) enum RemoteEventOrigin {
|
||||
/// The event came from a cache.
|
||||
Cache,
|
||||
/// The event came from a sync response.
|
||||
+66
-21
@@ -20,10 +20,13 @@ use async_trait::async_trait;
|
||||
use eyeball_im::{ObservableVector, VectorSubscriber};
|
||||
use imbl::Vector;
|
||||
use indexmap::{IndexMap, IndexSet};
|
||||
#[cfg(feature = "e2e-encryption")]
|
||||
use matrix_sdk_base::crypto::OlmMachine;
|
||||
use matrix_sdk_base::deserialized_responses::{EncryptionInfo, SyncTimelineEvent, TimelineEvent};
|
||||
#[cfg(feature = "e2e-encryption")]
|
||||
#[cfg(all(test, feature = "e2e-encryption"))]
|
||||
use matrix_sdk::crypto::OlmMachine;
|
||||
use matrix_sdk::{
|
||||
deserialized_responses::{EncryptionInfo, SyncTimelineEvent, TimelineEvent},
|
||||
room, Error, Result,
|
||||
};
|
||||
#[cfg(all(test, feature = "e2e-encryption"))]
|
||||
use ruma::RoomId;
|
||||
use ruma::{
|
||||
api::client::receipt::create_receipt::v3::ReceiptType as SendReceiptType,
|
||||
@@ -57,7 +60,7 @@ use super::{
|
||||
Message, Profile, RelativePosition, RepliedToEvent, TimelineDetails, TimelineItem,
|
||||
TimelineItemContent,
|
||||
};
|
||||
use crate::{events::SyncTimelineEventWithoutContent, room, Error, Result};
|
||||
use crate::events::SyncTimelineEventWithoutContent;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(super) struct TimelineInner<P: RoomDataProvider = room::Common> {
|
||||
@@ -347,12 +350,30 @@ impl<P: RoomDataProvider> TimelineInner<P> {
|
||||
}
|
||||
|
||||
#[cfg(feature = "e2e-encryption")]
|
||||
#[instrument(skip(self, olm_machine))]
|
||||
#[instrument(skip(self, room), fields(room_id = ?room.room_id()))]
|
||||
pub(super) async fn retry_event_decryption(
|
||||
&self,
|
||||
room: &room::Common,
|
||||
session_ids: Option<BTreeSet<&str>>,
|
||||
) {
|
||||
self.retry_event_decryption_inner(room, session_ids).await
|
||||
}
|
||||
|
||||
#[cfg(all(test, feature = "e2e-encryption"))]
|
||||
pub(super) async fn retry_event_decryption_test(
|
||||
&self,
|
||||
room_id: &RoomId,
|
||||
olm_machine: &OlmMachine,
|
||||
session_ids: Option<BTreeSet<&str>>,
|
||||
) {
|
||||
self.retry_event_decryption_inner((olm_machine, room_id), session_ids).await
|
||||
}
|
||||
|
||||
#[cfg(feature = "e2e-encryption")]
|
||||
async fn retry_event_decryption_inner(
|
||||
&self,
|
||||
decryptor: impl Decryptor,
|
||||
session_ids: Option<BTreeSet<&str>>,
|
||||
) {
|
||||
use super::EncryptedMessage;
|
||||
|
||||
@@ -392,8 +413,7 @@ impl<P: RoomDataProvider> TimelineInner<P> {
|
||||
|
||||
tracing::Span::current().record("event_id", debug(&remote_event.event_id));
|
||||
|
||||
let raw = remote_event.original_json.cast_ref();
|
||||
match olm_machine.decrypt_room_event(raw, room_id).await {
|
||||
match decryptor.decrypt_event_impl(&remote_event.original_json).await {
|
||||
Ok(event) => {
|
||||
trace!("Successfully decrypted event that previously failed to decrypt");
|
||||
Some(event)
|
||||
@@ -544,7 +564,10 @@ impl TimelineInner {
|
||||
}
|
||||
|
||||
#[instrument(skip(self))]
|
||||
pub(super) async fn fetch_in_reply_to_details(&self, event_id: &EventId) -> Result<()> {
|
||||
pub(super) async fn fetch_in_reply_to_details(
|
||||
&self,
|
||||
event_id: &EventId,
|
||||
) -> Result<(), super::Error> {
|
||||
let state = self.state.lock().await;
|
||||
let (index, item) = rfind_event_by_id(&state.items, event_id)
|
||||
.ok_or(super::Error::RemoteEventNotInTimeline)?;
|
||||
@@ -572,7 +595,7 @@ impl TimelineInner {
|
||||
&in_reply_to.event_id,
|
||||
self.room(),
|
||||
)
|
||||
.await;
|
||||
.await?;
|
||||
|
||||
// We need to be sure to have the latest position of the event as it might have
|
||||
// changed while waiting for the request.
|
||||
@@ -686,7 +709,7 @@ async fn fetch_replied_to_event(
|
||||
message: &Message,
|
||||
in_reply_to: &EventId,
|
||||
room: &room::Common,
|
||||
) -> TimelineDetails<Box<RepliedToEvent>> {
|
||||
) -> Result<TimelineDetails<Box<RepliedToEvent>>, super::Error> {
|
||||
if let Some((_, item)) = rfind_event_by_id(&state.items, in_reply_to) {
|
||||
let details = match item.content() {
|
||||
TimelineItemContent::Message(message) => {
|
||||
@@ -696,11 +719,11 @@ async fn fetch_replied_to_event(
|
||||
sender_profile: item.sender_profile().clone(),
|
||||
}))
|
||||
}
|
||||
_ => TimelineDetails::Error(Arc::new(super::Error::UnsupportedEvent.into())),
|
||||
_ => return Err(super::Error::UnsupportedEvent),
|
||||
};
|
||||
|
||||
debug!("Found replied-to event locally");
|
||||
return details;
|
||||
return Ok(details);
|
||||
};
|
||||
|
||||
trace!("Setting in-reply-to details to pending");
|
||||
@@ -715,15 +738,13 @@ async fn fetch_replied_to_event(
|
||||
drop(state);
|
||||
|
||||
trace!("Fetching replied-to event");
|
||||
match room.event(in_reply_to).await {
|
||||
Ok(timeline_event) => {
|
||||
match RepliedToEvent::try_from_timeline_event(timeline_event, room).await {
|
||||
Ok(event) => TimelineDetails::Ready(Box::new(event)),
|
||||
Err(e) => TimelineDetails::Error(Arc::new(e)),
|
||||
}
|
||||
}
|
||||
let res = match room.event(in_reply_to).await {
|
||||
Ok(timeline_event) => TimelineDetails::Ready(Box::new(
|
||||
RepliedToEvent::try_from_timeline_event(timeline_event, room).await?,
|
||||
)),
|
||||
Err(e) => TimelineDetails::Error(Arc::new(e)),
|
||||
}
|
||||
};
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
@@ -850,3 +871,27 @@ async fn handle_remote_event<P: RoomDataProvider>(
|
||||
TimelineEventHandler::new(event_meta, flow, timeline_state, track_read_receipts)
|
||||
.handle_event(event_kind)
|
||||
}
|
||||
|
||||
// Internal helper to make most of retry_event_decryption independent of a room
|
||||
// object, which is annoying to create for testing and not really needed
|
||||
#[async_trait]
|
||||
trait Decryptor: Copy {
|
||||
async fn decrypt_event_impl(&self, raw: &Raw<AnySyncTimelineEvent>) -> Result<TimelineEvent>;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Decryptor for &room::Common {
|
||||
async fn decrypt_event_impl(&self, raw: &Raw<AnySyncTimelineEvent>) -> Result<TimelineEvent> {
|
||||
self.decrypt_event(raw.cast_ref()).await
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(test, feature = "e2e-encryption"))]
|
||||
#[async_trait]
|
||||
impl Decryptor for (&OlmMachine, &RoomId) {
|
||||
async fn decrypt_event_impl(&self, raw: &Raw<AnySyncTimelineEvent>) -> Result<TimelineEvent> {
|
||||
let (olm_machine, room_id) = self;
|
||||
let event = olm_machine.decrypt_room_event(raw.cast_ref(), room_id).await?;
|
||||
Ok(event)
|
||||
}
|
||||
}
|
||||
+54
-42
@@ -22,6 +22,12 @@ use eyeball_im::{VectorDiff, VectorSubscriber};
|
||||
use futures_core::Stream;
|
||||
use futures_util::TryFutureExt;
|
||||
use imbl::Vector;
|
||||
use matrix_sdk::{
|
||||
attachment::AttachmentConfig,
|
||||
event_handler::EventHandlerHandle,
|
||||
room::{self, MessagesOptions, Receipts, Room},
|
||||
Client, Result,
|
||||
};
|
||||
use mime::Mime;
|
||||
use pin_project_lite::pin_project;
|
||||
use ruma::{
|
||||
@@ -38,20 +44,15 @@ use thiserror::Error;
|
||||
use tokio::sync::Mutex;
|
||||
use tracing::{error, instrument, warn};
|
||||
|
||||
use super::{Joined, Receipts};
|
||||
use crate::{
|
||||
attachment::AttachmentConfig,
|
||||
event_handler::EventHandlerHandle,
|
||||
room::{self, MessagesOptions},
|
||||
Client, Result,
|
||||
};
|
||||
|
||||
mod builder;
|
||||
mod event_handler;
|
||||
mod event_item;
|
||||
mod inner;
|
||||
mod pagination;
|
||||
mod read_receipts;
|
||||
mod room_ext;
|
||||
#[cfg(feature = "experimental-sliding-sync")]
|
||||
mod sliding_sync_ext;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
#[cfg(feature = "e2e-encryption")]
|
||||
@@ -60,6 +61,8 @@ mod virtual_item;
|
||||
|
||||
pub(crate) use self::builder::TimelineBuilder;
|
||||
use self::inner::{TimelineInner, TimelineInnerState};
|
||||
#[cfg(feature = "experimental-sliding-sync")]
|
||||
pub use self::sliding_sync_ext::SlidingSyncRoomExt;
|
||||
pub use self::{
|
||||
event_item::{
|
||||
AnyOtherFullStateEventContent, BundledReactions, EncryptedMessage, EventSendState,
|
||||
@@ -68,6 +71,7 @@ pub use self::{
|
||||
TimelineDetails, TimelineItemContent,
|
||||
},
|
||||
pagination::{PaginationOptions, PaginationOutcome},
|
||||
room_ext::RoomExt,
|
||||
virtual_item::VirtualTimelineItem,
|
||||
};
|
||||
|
||||
@@ -180,10 +184,8 @@ impl Timeline {
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use std::{path::PathBuf, time::Duration};
|
||||
/// # use matrix_sdk::{
|
||||
/// # Client, config::SyncSettings,
|
||||
/// # room::timeline::Timeline, ruma::room_id,
|
||||
/// # };
|
||||
/// # use matrix_sdk::{Client, config::SyncSettings, ruma::room_id};
|
||||
/// # use matrix_sdk_ui::Timeline;
|
||||
/// # async {
|
||||
/// # let mut client: Client = todo!();
|
||||
/// # let room_id = ruma::room_id!("!example:example.org");
|
||||
@@ -206,8 +208,7 @@ impl Timeline {
|
||||
) {
|
||||
self.inner
|
||||
.retry_event_decryption(
|
||||
self.room().room_id(),
|
||||
self.room().client.olm_machine().expect("Olm machine wasn't started"),
|
||||
self.room(),
|
||||
Some(session_ids.into_iter().map(AsRef::as_ref).collect()),
|
||||
)
|
||||
.await;
|
||||
@@ -216,13 +217,7 @@ impl Timeline {
|
||||
#[cfg(feature = "e2e-encryption")]
|
||||
#[tracing::instrument(skip(self))]
|
||||
async fn retry_decryption_for_all_events(&self) {
|
||||
self.inner
|
||||
.retry_event_decryption(
|
||||
self.room().room_id(),
|
||||
self.room().client.olm_machine().expect("Olm machine wasn't started"),
|
||||
None,
|
||||
)
|
||||
.await;
|
||||
self.inner.retry_event_decryption(self.room(), None).await;
|
||||
}
|
||||
|
||||
/// Get the current list of timeline items. Do not use this in production!
|
||||
@@ -282,16 +277,23 @@ impl Timeline {
|
||||
let txn_id = txn_id.map_or_else(TransactionId::new, ToOwned::to_owned);
|
||||
self.inner.handle_local_event(txn_id.clone(), content.clone()).await;
|
||||
|
||||
// If this room isn't actually in joined state, we'll get a server error.
|
||||
// Not ideal, but works for now.
|
||||
let room = Joined { inner: self.room().clone() };
|
||||
let send_state = match Room::from(self.room().clone()) {
|
||||
Room::Joined(room) => {
|
||||
let response = room.send(content, Some(&txn_id)).await;
|
||||
|
||||
let response = room.send(content, Some(&txn_id)).await;
|
||||
|
||||
let send_state = match response {
|
||||
Ok(response) => EventSendState::Sent { event_id: response.event_id },
|
||||
Err(error) => EventSendState::SendingFailed { error: Arc::new(error) },
|
||||
match response {
|
||||
Ok(response) => EventSendState::Sent { event_id: response.event_id },
|
||||
Err(error) => EventSendState::SendingFailed { error: Arc::new(error) },
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
EventSendState::SendingFailed {
|
||||
// FIXME: Probably not exactly right
|
||||
error: Arc::new(matrix_sdk::Error::InconsistentState),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
self.inner.update_event_send_state(&txn_id, send_state).await;
|
||||
}
|
||||
|
||||
@@ -316,9 +318,9 @@ impl Timeline {
|
||||
mime_type: Mime,
|
||||
config: AttachmentConfig,
|
||||
) -> Result<(), Error> {
|
||||
// If this room isn't actually in joined state, we'll get a server error.
|
||||
// Not ideal, but works for now.
|
||||
let room = Joined { inner: self.room().clone() };
|
||||
let Room::Joined(room) = Room::from(self.room().clone()) else {
|
||||
return Err(Error::RoomNotJoined);
|
||||
};
|
||||
|
||||
let body =
|
||||
Path::new(&url).file_name().ok_or(Error::InvalidAttachmentFileName)?.to_str().unwrap();
|
||||
@@ -351,7 +353,7 @@ impl Timeline {
|
||||
/// echo in the timeline, or if the event is removed from the timeline
|
||||
/// before all requests are handled.
|
||||
#[instrument(skip(self), fields(room_id = ?self.room().room_id()))]
|
||||
pub async fn fetch_details_for_event(&self, event_id: &EventId) -> Result<()> {
|
||||
pub async fn fetch_details_for_event(&self, event_id: &EventId) -> Result<(), Error> {
|
||||
self.inner.fetch_in_reply_to_details(event_id).await
|
||||
}
|
||||
|
||||
@@ -377,7 +379,7 @@ impl Timeline {
|
||||
|
||||
/// Get the latest read receipt for the given user.
|
||||
///
|
||||
/// Contrary to [`Common::user_receipt()`](super::Common::user_receipt) that
|
||||
/// Contrary to [`Common::user_receipt()`](room::Common::user_receipt) that
|
||||
/// only keeps track of read receipts received from the homeserver, this
|
||||
/// keeps also track of implicit read receipts in this timeline, i.e.
|
||||
/// when a room member sends an event.
|
||||
@@ -394,6 +396,8 @@ impl Timeline {
|
||||
/// This uses [`Joined::send_single_receipt`] internally, but checks
|
||||
/// first if the receipt points to an event in this timeline that is more
|
||||
/// recent than the current ones, to avoid unnecessary requests.
|
||||
///
|
||||
/// [`Joined::send_single_receipt`]: room::Joined::send_single_receipt
|
||||
#[instrument(skip(self))]
|
||||
pub async fn send_single_receipt(
|
||||
&self,
|
||||
@@ -405,9 +409,10 @@ impl Timeline {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// If this room isn't actually in joined state, we'll get a server error.
|
||||
// Not ideal, but works for now.
|
||||
let room = Joined { inner: self.room().clone() };
|
||||
let Room::Joined(room) = Room::from(self.room().clone()) else {
|
||||
// FIXME: Probably not exactly right
|
||||
return Err(matrix_sdk::Error::InconsistentState);
|
||||
};
|
||||
|
||||
room.send_single_receipt(receipt_type, thread, event_id).await
|
||||
}
|
||||
@@ -417,6 +422,8 @@ impl Timeline {
|
||||
/// This uses [`Joined::send_multiple_receipts`] internally, but checks
|
||||
/// first if the receipts point to events in this timeline that are more
|
||||
/// recent than the current ones, to avoid unnecessary requests.
|
||||
///
|
||||
/// [`Joined::send_multiple_receipts`]: room::Joined::send_multiple_receipts
|
||||
#[instrument(skip(self))]
|
||||
pub async fn send_multiple_receipts(&self, mut receipts: Receipts) -> Result<()> {
|
||||
if let Some(fully_read) = &receipts.fully_read {
|
||||
@@ -433,13 +440,13 @@ impl Timeline {
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(read_receipt) = &receipts.read_receipt {
|
||||
if let Some(read_receipt) = &receipts.public_read_receipt {
|
||||
if !self
|
||||
.inner
|
||||
.should_send_receipt(&ReceiptType::Read, &ReceiptThread::Unthreaded, read_receipt)
|
||||
.await
|
||||
{
|
||||
receipts.read_receipt = None;
|
||||
receipts.public_read_receipt = None;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -457,9 +464,10 @@ impl Timeline {
|
||||
}
|
||||
}
|
||||
|
||||
// If this room isn't actually in joined state, we'll get a server error.
|
||||
// Not ideal, but works for now.
|
||||
let room = Joined { inner: self.room().clone() };
|
||||
let Room::Joined(room) = Room::from(self.room().clone()) else {
|
||||
// FIXME: Probably not exactly right
|
||||
return Err(matrix_sdk::Error::InconsistentState);
|
||||
};
|
||||
|
||||
room.send_multiple_receipts(receipts).await
|
||||
}
|
||||
@@ -633,6 +641,10 @@ pub enum Error {
|
||||
/// The attachment could not be sent
|
||||
#[error("Failed sending attachment")]
|
||||
FailedSendingAttachment,
|
||||
|
||||
/// The room is not in a joined state.
|
||||
#[error("Room is not joined")]
|
||||
RoomNotJoined,
|
||||
}
|
||||
|
||||
/// Result of comparing events position in the timeline.
|
||||
+1
-1
@@ -16,6 +16,7 @@ use std::{collections::HashMap, sync::Arc};
|
||||
|
||||
use eyeball_im::ObservableVector;
|
||||
use indexmap::IndexMap;
|
||||
use matrix_sdk::room;
|
||||
use ruma::{
|
||||
events::receipt::{Receipt, ReceiptEventContent, ReceiptThread, ReceiptType},
|
||||
EventId, OwnedEventId, OwnedUserId, UserId,
|
||||
@@ -28,7 +29,6 @@ use super::{
|
||||
inner::{RoomDataProvider, TimelineInnerState},
|
||||
rfind_event_by_id, EventTimelineItem, RelativePosition, TimelineItem,
|
||||
};
|
||||
use crate::room;
|
||||
|
||||
struct FullReceipt<'a> {
|
||||
event_id: &'a EventId,
|
||||
@@ -0,0 +1,35 @@
|
||||
// Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use async_trait::async_trait;
|
||||
use matrix_sdk::room;
|
||||
|
||||
use crate::timeline::Timeline;
|
||||
|
||||
#[async_trait]
|
||||
pub trait RoomExt {
|
||||
/// Get a [`Timeline`] for this room.
|
||||
///
|
||||
/// This offers a higher-level API than event handlers, in treating things
|
||||
/// like edits and reactions as updates of existing items rather than new
|
||||
/// independent events.
|
||||
async fn timeline(&self) -> Timeline;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl RoomExt for room::Common {
|
||||
async fn timeline(&self) -> Timeline {
|
||||
Timeline::builder(self).track_read_marker_and_receipts().build().await
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
// Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use async_trait::async_trait;
|
||||
use matrix_sdk::SlidingSyncRoom;
|
||||
use tracing::{error, instrument};
|
||||
|
||||
use super::{EventTimelineItem, Timeline, TimelineBuilder};
|
||||
|
||||
#[async_trait]
|
||||
pub trait SlidingSyncRoomExt {
|
||||
/// Get a `Timeline` for this room.
|
||||
async fn timeline(&self) -> Option<Timeline>;
|
||||
|
||||
/// Get the latest timeline item of this room.
|
||||
///
|
||||
/// Use `Timeline::latest_event` instead if you already have a timeline for
|
||||
/// this `SlidingSyncRoom`.
|
||||
async fn latest_event(&self) -> Option<EventTimelineItem>;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl SlidingSyncRoomExt for SlidingSyncRoom {
|
||||
async fn timeline(&self) -> Option<Timeline> {
|
||||
Some(sliding_sync_timeline_builder(self)?.track_read_marker_and_receipts().build().await)
|
||||
}
|
||||
|
||||
#[instrument(skip_all)]
|
||||
async fn latest_event(&self) -> Option<EventTimelineItem> {
|
||||
sliding_sync_timeline_builder(self)?.build().await.latest_event().await
|
||||
}
|
||||
}
|
||||
|
||||
fn sliding_sync_timeline_builder(room: &SlidingSyncRoom) -> Option<TimelineBuilder> {
|
||||
let room_id = room.room_id();
|
||||
match room.client().get_room(room_id) {
|
||||
Some(r) => Some(Timeline::builder(&r).events(room.prev_batch(), room.timeline_queue())),
|
||||
None => {
|
||||
error!(?room_id, "Room not found in client. Can't provide a timeline for it");
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -16,7 +16,7 @@ use assert_matches::assert_matches;
|
||||
use eyeball_im::VectorDiff;
|
||||
use futures_util::StreamExt;
|
||||
use imbl::vector;
|
||||
use matrix_sdk_base::deserialized_responses::SyncTimelineEvent;
|
||||
use matrix_sdk::deserialized_responses::SyncTimelineEvent;
|
||||
use matrix_sdk_test::async_test;
|
||||
use ruma::{
|
||||
assign,
|
||||
@@ -34,7 +34,7 @@ use ruma::{
|
||||
use serde_json::{json, Value as JsonValue};
|
||||
|
||||
use super::{TestTimeline, ALICE, BOB};
|
||||
use crate::room::timeline::{
|
||||
use crate::timeline::{
|
||||
event_item::AnyOtherFullStateEventContent, MembershipChange, TimelineDetails, TimelineItem,
|
||||
TimelineItemContent, VirtualTimelineItem,
|
||||
};
|
||||
+2
-1
@@ -17,6 +17,7 @@ use std::{io, sync::Arc};
|
||||
use assert_matches::assert_matches;
|
||||
use eyeball_im::VectorDiff;
|
||||
use futures_util::StreamExt;
|
||||
use matrix_sdk::Error;
|
||||
use matrix_sdk_test::async_test;
|
||||
use ruma::{
|
||||
event_id,
|
||||
@@ -25,7 +26,7 @@ use ruma::{
|
||||
use serde_json::json;
|
||||
|
||||
use super::{TestTimeline, ALICE, BOB};
|
||||
use crate::{room::timeline::event_item::EventSendState, Error};
|
||||
use crate::timeline::event_item::EventSendState;
|
||||
|
||||
#[async_test]
|
||||
async fn remote_echo_full_trip() {
|
||||
+1
-1
@@ -30,7 +30,7 @@ use ruma::{
|
||||
use serde_json::json;
|
||||
|
||||
use super::{TestTimeline, ALICE};
|
||||
use crate::room::timeline::TimelineItemContent;
|
||||
use crate::timeline::TimelineItemContent;
|
||||
|
||||
#[async_test]
|
||||
async fn live_redacted() {
|
||||
+6
-6
@@ -19,7 +19,7 @@ use std::{collections::BTreeSet, io::Cursor, iter};
|
||||
use assert_matches::assert_matches;
|
||||
use eyeball_im::VectorDiff;
|
||||
use futures_util::StreamExt;
|
||||
use matrix_sdk_base::crypto::{decrypt_room_key_export, OlmMachine};
|
||||
use matrix_sdk::crypto::{decrypt_room_key_export, OlmMachine};
|
||||
use matrix_sdk_test::async_test;
|
||||
use ruma::{
|
||||
assign,
|
||||
@@ -31,7 +31,7 @@ use ruma::{
|
||||
};
|
||||
|
||||
use super::{TestTimeline, BOB};
|
||||
use crate::room::timeline::{EncryptedMessage, TimelineItemContent};
|
||||
use crate::timeline::{EncryptedMessage, TimelineItemContent};
|
||||
|
||||
#[async_test]
|
||||
async fn retry_message_decryption() {
|
||||
@@ -100,7 +100,7 @@ async fn retry_message_decryption() {
|
||||
|
||||
timeline
|
||||
.inner
|
||||
.retry_event_decryption(
|
||||
.retry_event_decryption_test(
|
||||
room_id!("!DovneieKSTkdHKpIXy:morpheus.localhost"),
|
||||
&olm_machine,
|
||||
Some(iter::once(SESSION_ID).collect()),
|
||||
@@ -203,7 +203,7 @@ async fn retry_edit_decryption() {
|
||||
|
||||
timeline
|
||||
.inner
|
||||
.retry_event_decryption(
|
||||
.retry_event_decryption_test(
|
||||
room_id!("!bdsREiCPHyZAPkpXer:morpheus.localhost"),
|
||||
&olm_machine,
|
||||
None,
|
||||
@@ -306,7 +306,7 @@ async fn retry_edit_and_more() {
|
||||
|
||||
timeline
|
||||
.inner
|
||||
.retry_event_decryption(
|
||||
.retry_event_decryption_test(
|
||||
room_id!("!wFnAUSQbxMcfIMgvNX:flipdot.org"),
|
||||
&olm_machine,
|
||||
Some(BTreeSet::from_iter([SESSION_ID])),
|
||||
@@ -392,7 +392,7 @@ async fn retry_message_decryption_highlighted() {
|
||||
|
||||
timeline
|
||||
.inner
|
||||
.retry_event_decryption(
|
||||
.retry_event_decryption_test(
|
||||
room_id!("!rYtFvMGENJleNQVJzb:matrix.org"),
|
||||
&olm_machine,
|
||||
Some(iter::once(SESSION_ID).collect()),
|
||||
+1
-1
@@ -28,7 +28,7 @@ use ruma::{
|
||||
use serde_json::json;
|
||||
|
||||
use super::{TestTimeline, ALICE, BOB};
|
||||
use crate::room::timeline::TimelineItemContent;
|
||||
use crate::timeline::TimelineItemContent;
|
||||
|
||||
#[async_test]
|
||||
async fn invalid_edit() {
|
||||
+1
-1
@@ -26,7 +26,7 @@ use async_trait::async_trait;
|
||||
use eyeball_im::VectorDiff;
|
||||
use futures_core::Stream;
|
||||
use indexmap::IndexMap;
|
||||
use matrix_sdk_base::deserialized_responses::TimelineEvent;
|
||||
use matrix_sdk::deserialized_responses::TimelineEvent;
|
||||
use once_cell::sync::Lazy;
|
||||
use ruma::{
|
||||
events::{
|
||||
+1
-1
@@ -23,7 +23,7 @@ use ruma::{
|
||||
};
|
||||
|
||||
use super::{TestTimeline, ALICE, BOB};
|
||||
use crate::room::timeline::{TimelineItem, VirtualTimelineItem};
|
||||
use crate::timeline::{TimelineItem, VirtualTimelineItem};
|
||||
|
||||
#[async_test]
|
||||
async fn day_divider() {
|
||||
+4
-10
@@ -14,6 +14,7 @@
|
||||
|
||||
use std::{iter, sync::Arc};
|
||||
|
||||
use matrix_sdk::{event_handler::EventHandler, Client};
|
||||
use ruma::{
|
||||
events::{forwarded_room_key::ToDeviceForwardedRoomKeyEvent, room_key::ToDeviceRoomKeyEvent},
|
||||
OwnedRoomId,
|
||||
@@ -21,7 +22,6 @@ use ruma::{
|
||||
use tracing::{debug_span, error, trace, Instrument};
|
||||
|
||||
use super::inner::TimelineInner;
|
||||
use crate::{event_handler::EventHandler, Client};
|
||||
|
||||
pub(super) fn handle_room_key_event(
|
||||
inner: Arc<TimelineInner>,
|
||||
@@ -70,16 +70,10 @@ async fn retry_decryption(
|
||||
return;
|
||||
}
|
||||
|
||||
let Some(olm_machine) = client.olm_machine() else {
|
||||
error!("The olm machine isn't yet available");
|
||||
let Some(room) = client.get_room(&room_id) else {
|
||||
error!("Failed to fetch room object");
|
||||
return;
|
||||
};
|
||||
|
||||
inner
|
||||
.retry_event_decryption(
|
||||
&room_id,
|
||||
olm_machine,
|
||||
Some(iter::once(session_id.as_str()).collect()),
|
||||
)
|
||||
.await;
|
||||
inner.retry_event_decryption(&room, Some(iter::once(session_id.as_str()).collect())).await;
|
||||
}
|
||||
+2
-2
@@ -1,5 +1,3 @@
|
||||
#![cfg(feature = "experimental-timeline")]
|
||||
|
||||
use std::{sync::Arc, time::Duration};
|
||||
|
||||
use assert_matches::assert_matches;
|
||||
@@ -34,6 +32,8 @@ use wiremock::{
|
||||
};
|
||||
|
||||
mod read_receipts;
|
||||
#[cfg(feature = "experimental-sliding-sync")]
|
||||
mod sliding_sync;
|
||||
|
||||
use crate::{logged_in_client, mock_encryption_state, mock_sync};
|
||||
|
||||
+4
-1
@@ -5,10 +5,13 @@ use assert_matches::assert_matches;
|
||||
use eyeball_im::{Vector, VectorDiff};
|
||||
use futures::{pin_mut, Stream, StreamExt};
|
||||
use matrix_sdk::{
|
||||
room::timeline::{TimelineItem, VirtualTimelineItem},
|
||||
SlidingSync, SlidingSyncList, SlidingSyncListBuilder, SlidingSyncMode, UpdateSummary,
|
||||
};
|
||||
use matrix_sdk_test::async_test;
|
||||
use matrix_sdk_ui::{
|
||||
timeline::{TimelineItem, VirtualTimelineItem},
|
||||
SlidingSyncRoomExt,
|
||||
};
|
||||
use ruma::{room_id, RoomId};
|
||||
use serde_json::json;
|
||||
use wiremock::{http::Method, Match, Mock, MockServer, Request, ResponseTemplate};
|
||||
@@ -47,13 +47,7 @@ appservice = ["ruma/appservice-api-s"]
|
||||
image-proc = ["dep:image"]
|
||||
image-rayon = ["image-proc", "image?/jpeg_rayon"]
|
||||
|
||||
experimental-timeline = ["ruma/unstable-msc2677", "ruma/unstable-sanitize", "dep:chrono"]
|
||||
|
||||
experimental-sliding-sync = [
|
||||
"matrix-sdk-base/experimental-sliding-sync",
|
||||
"experimental-timeline",
|
||||
"reqwest/gzip",
|
||||
]
|
||||
experimental-sliding-sync = ["matrix-sdk-base/experimental-sliding-sync", "reqwest/gzip"]
|
||||
|
||||
docsrs = [
|
||||
"e2e-encryption",
|
||||
@@ -70,7 +64,6 @@ async-stream = { workspace = true }
|
||||
async-trait = { workspace = true }
|
||||
bytes = "1.1.0"
|
||||
bytesize = "1.1"
|
||||
chrono = { version = "0.4.23", optional = true }
|
||||
dashmap = { workspace = true }
|
||||
event-listener = "2.5.2"
|
||||
eyeball = { workspace = true }
|
||||
@@ -80,7 +73,6 @@ futures-core = "0.3.21"
|
||||
futures-util = { workspace = true }
|
||||
http = { workspace = true }
|
||||
imbl = { version = "2.0.0", features = ["serde"] }
|
||||
indexmap = "1.9.1"
|
||||
hyper = { version = "0.14.20", features = ["http1", "http2", "server"], optional = true }
|
||||
matrix-sdk-base = { version = "0.6.0", path = "../matrix-sdk-base", default_features = false }
|
||||
matrix-sdk-common = { version = "0.6.0", path = "../matrix-sdk-common" }
|
||||
@@ -88,8 +80,6 @@ matrix-sdk-indexeddb = { version = "0.2.0", path = "../matrix-sdk-indexeddb", de
|
||||
matrix-sdk-sqlite = { version = "0.1.0", path = "../matrix-sdk-sqlite", default-features = false, optional = true }
|
||||
mime = "0.3.16"
|
||||
mime2ext = "0.1.52"
|
||||
once_cell = { workspace = true }
|
||||
pin-project-lite = "0.2.9"
|
||||
rand = { version = "0.8.5", optional = true }
|
||||
reqwest = { version = "0.11.10", default_features = false }
|
||||
ruma = { workspace = true, features = ["rand", "unstable-msc2448", "unstable-msc2965"] }
|
||||
@@ -137,7 +127,6 @@ assert_matches = { workspace = true }
|
||||
dirs = "4.0.0"
|
||||
futures = { version = "0.3.21", default-features = false, features = ["executor"] }
|
||||
matrix-sdk-test = { version = "0.6.0", path = "../../testing/matrix-sdk-test" }
|
||||
once_cell = { workspace = true }
|
||||
tracing-subscriber = { version = "0.3.11", features = ["env-filter"] }
|
||||
|
||||
[target.'cfg(target_arch = "wasm32")'.dev-dependencies]
|
||||
|
||||
@@ -245,11 +245,6 @@ pub enum Error {
|
||||
#[error(transparent)]
|
||||
SlidingSync(#[from] crate::sliding_sync::Error),
|
||||
|
||||
/// An error occurred in the timeline.
|
||||
#[cfg(feature = "experimental-timeline")]
|
||||
#[error(transparent)]
|
||||
Timeline(#[from] crate::room::timeline::Error),
|
||||
|
||||
/// The client is in inconsistent state. This happens when we set a room to
|
||||
/// a specific type, but then cannot get it in this type.
|
||||
#[error("The internal client state is inconsistent.")]
|
||||
|
||||
@@ -45,8 +45,6 @@ pub mod sliding_sync;
|
||||
|
||||
#[cfg(feature = "e2e-encryption")]
|
||||
pub mod encryption;
|
||||
#[cfg(feature = "experimental-timeline")]
|
||||
mod events;
|
||||
|
||||
pub use account::Account;
|
||||
#[cfg(feature = "sso-login")]
|
||||
|
||||
@@ -49,8 +49,6 @@ use serde::de::DeserializeOwned;
|
||||
use tokio::sync::Mutex;
|
||||
use tracing::{debug, instrument};
|
||||
|
||||
#[cfg(feature = "experimental-timeline")]
|
||||
use super::timeline::Timeline;
|
||||
use super::Joined;
|
||||
use crate::{
|
||||
event_handler::{EventHandler, EventHandlerHandle, SyncEvent},
|
||||
@@ -269,16 +267,6 @@ impl Common {
|
||||
self.client.add_room_event_handler(self.room_id(), handler)
|
||||
}
|
||||
|
||||
/// Get a [`Timeline`] for this room.
|
||||
///
|
||||
/// This offers a higher-level API than event handlers, in treating things
|
||||
/// like edits and reactions as updates of existing items rather than new
|
||||
/// independent events.
|
||||
#[cfg(feature = "experimental-timeline")]
|
||||
pub async fn timeline(&self) -> Timeline {
|
||||
Timeline::builder(self).track_read_marker_and_receipts().build().await
|
||||
}
|
||||
|
||||
/// Fetch the event with the given `EventId` in this room.
|
||||
pub async fn event(&self, event_id: &EventId) -> Result<TimelineEvent> {
|
||||
let request =
|
||||
|
||||
@@ -295,10 +295,10 @@ impl Joined {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let Receipts { fully_read, read_receipt, private_read_receipt } = receipts;
|
||||
let Receipts { fully_read, public_read_receipt, private_read_receipt } = receipts;
|
||||
let request = assign!(set_read_marker::v3::Request::new(self.inner.room_id().to_owned()), {
|
||||
fully_read,
|
||||
read_receipt,
|
||||
read_receipt: public_read_receipt,
|
||||
private_read_receipt,
|
||||
});
|
||||
|
||||
@@ -1139,10 +1139,14 @@ impl Joined {
|
||||
|
||||
/// Receipts to send all at once.
|
||||
#[derive(Debug, Clone, Default)]
|
||||
#[non_exhaustive]
|
||||
pub struct Receipts {
|
||||
pub(super) fully_read: Option<OwnedEventId>,
|
||||
pub(super) read_receipt: Option<OwnedEventId>,
|
||||
pub(super) private_read_receipt: Option<OwnedEventId>,
|
||||
/// Fully-read marker (room account data).
|
||||
pub fully_read: Option<OwnedEventId>,
|
||||
/// Read receipt (public ephemeral room event).
|
||||
pub public_read_receipt: Option<OwnedEventId>,
|
||||
/// Read receipt (private ephemeral room event).
|
||||
pub private_read_receipt: Option<OwnedEventId>,
|
||||
}
|
||||
|
||||
impl Receipts {
|
||||
@@ -1170,7 +1174,7 @@ impl Receipts {
|
||||
/// This is used to reset the unread messages/notification count and
|
||||
/// advertise to other users the last event that the user has likely seen.
|
||||
pub fn public_read_receipt(mut self, event_id: impl Into<Option<OwnedEventId>>) -> Self {
|
||||
self.read_receipt = event_id.into();
|
||||
self.public_read_receipt = event_id.into();
|
||||
self
|
||||
}
|
||||
|
||||
@@ -1185,7 +1189,7 @@ impl Receipts {
|
||||
/// Whether this `Receipts` is empty.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.fully_read.is_none()
|
||||
&& self.read_receipt.is_none()
|
||||
&& self.public_read_receipt.is_none()
|
||||
&& self.private_read_receipt.is_none()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,8 +9,6 @@ mod invited;
|
||||
mod joined;
|
||||
mod left;
|
||||
mod member;
|
||||
#[cfg(feature = "experimental-timeline")]
|
||||
pub mod timeline;
|
||||
|
||||
pub use self::{
|
||||
common::{Common, Messages, MessagesOptions},
|
||||
|
||||
@@ -216,8 +216,8 @@ the [`BaseClient`][`matrix_sdk_base::BaseClient`] as in previous sync. This
|
||||
allows for transparent decryption as well trigger the `client_handlers`.
|
||||
|
||||
The current and then following live events list can be queried via the
|
||||
[`timeline` API](`SlidingSyncRoom::timeline). This is prefilled with already
|
||||
received data.
|
||||
`timeline` API from `matrix-sdk-ui`. This is prefilled with already received
|
||||
data.
|
||||
|
||||
### Timeline trickling
|
||||
|
||||
@@ -349,8 +349,8 @@ copies accordingly. Because of where the loop sits in the stack, that can
|
||||
be a bit tedious though, so lists and rooms have an additional way of
|
||||
subscribing to updates via [`eyeball`].
|
||||
|
||||
The `Timeline` one can receive per room by calling
|
||||
[`.timeline()`][`SlidingSyncRoom::timeline`] will be populated with the
|
||||
The `Timeline` one can receive per room by calling `.timeline()` (from
|
||||
`matrix_sdk_ui::timeline::SlidingSyncRoomExt`) will be populated with the
|
||||
currently cached timeline events.
|
||||
|
||||
## Caching
|
||||
|
||||
@@ -13,12 +13,8 @@ use ruma::{
|
||||
OwnedRoomId, RoomId,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tracing::{error, instrument};
|
||||
|
||||
use crate::{
|
||||
room::timeline::{EventTimelineItem, Timeline, TimelineBuilder},
|
||||
Client,
|
||||
};
|
||||
use crate::Client;
|
||||
|
||||
/// The state of a [`SlidingSyncRoom`].
|
||||
#[derive(Copy, Clone, Debug, Default, PartialEq)]
|
||||
@@ -108,23 +104,25 @@ impl SlidingSyncRoom {
|
||||
|
||||
/// Get the required state.
|
||||
pub fn required_state(&self) -> Vec<Raw<AnySyncStateEvent>> {
|
||||
let inner = self.inner.inner.read().unwrap();
|
||||
|
||||
inner.required_state.clone()
|
||||
self.inner.inner.read().unwrap().required_state.clone()
|
||||
}
|
||||
|
||||
/// `Timeline` of this room
|
||||
pub async fn timeline(&self) -> Option<Timeline> {
|
||||
Some(self.inner.timeline_builder()?.track_read_marker_and_receipts().build().await)
|
||||
/// Get the token for back-pagination.
|
||||
pub fn prev_batch(&self) -> Option<String> {
|
||||
self.inner.inner.read().unwrap().prev_batch.clone()
|
||||
}
|
||||
|
||||
/// The latest timeline item of this room.
|
||||
/// Get a copy of the cached timeline events.
|
||||
///
|
||||
/// Use `Timeline::latest_event` instead if you already have a timeline for
|
||||
/// this `SlidingSyncRoom`.
|
||||
#[instrument(skip_all)]
|
||||
pub async fn latest_event(&self) -> Option<EventTimelineItem> {
|
||||
self.inner.timeline_builder()?.build().await.latest_event().await
|
||||
/// Note: This API only exists temporarily, it *will* be removed in the
|
||||
/// future.
|
||||
pub fn timeline_queue(&self) -> Vector<SyncTimelineEvent> {
|
||||
self.inner.timeline_queue.read().unwrap().clone()
|
||||
}
|
||||
|
||||
/// Get a clone of the associated client.
|
||||
pub fn client(&self) -> Client {
|
||||
self.inner.client.clone()
|
||||
}
|
||||
|
||||
pub(super) fn update(
|
||||
@@ -233,10 +231,6 @@ impl SlidingSyncRoom {
|
||||
fn set_state(&mut self, state: SlidingSyncRoomState) {
|
||||
*self.inner.state.write().unwrap() = state;
|
||||
}
|
||||
|
||||
fn timeline_queue(&self) -> std::sync::RwLockReadGuard<'_, Vector<SyncTimelineEvent>> {
|
||||
self.inner.timeline_queue.read().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -260,33 +254,6 @@ struct SlidingSyncRoomInner {
|
||||
timeline_queue: RwLock<Vector<SyncTimelineEvent>>,
|
||||
}
|
||||
|
||||
impl SlidingSyncRoomInner {
|
||||
/// Get the previous batch.
|
||||
fn prev_batch(&self) -> Option<String> {
|
||||
let inner = self.inner.read().unwrap();
|
||||
|
||||
inner.prev_batch.clone()
|
||||
}
|
||||
|
||||
fn timeline_builder(&self) -> Option<TimelineBuilder> {
|
||||
if let Some(room) = self.client.get_room(&self.room_id) {
|
||||
Some(
|
||||
Timeline::builder(&room)
|
||||
.events(self.prev_batch(), self.timeline_queue.read().unwrap().clone()),
|
||||
)
|
||||
} else if let Some(invited_room) = self.client.get_invited_room(&self.room_id) {
|
||||
Some(Timeline::builder(&invited_room).events(None, Vector::new()))
|
||||
} else {
|
||||
error!(
|
||||
room_id = ?self.room_id,
|
||||
"Room not found in client. Can't provide a timeline for it"
|
||||
);
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A “frozen” [`SlidingSyncRoom`], i.e. that can be written into, or read from
|
||||
/// a store.
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
@@ -515,7 +482,7 @@ mod tests {
|
||||
{
|
||||
let room = new_room(room_id!("!foo:bar.org"), room_response!({})).await;
|
||||
|
||||
assert_eq!(room.inner.prev_batch(), None);
|
||||
assert_eq!(room.prev_batch(), None);
|
||||
}
|
||||
|
||||
// Some value when initializing.
|
||||
@@ -524,20 +491,20 @@ mod tests {
|
||||
new_room(room_id!("!foo:bar.org"), room_response!({"prev_batch": "t111_222_333"}))
|
||||
.await;
|
||||
|
||||
assert_eq!(room.inner.prev_batch(), Some("t111_222_333".to_owned()));
|
||||
assert_eq!(room.prev_batch(), Some("t111_222_333".to_owned()));
|
||||
}
|
||||
|
||||
// Some value when updating.
|
||||
{
|
||||
let mut room = new_room(room_id!("!foo:bar.org"), room_response!({})).await;
|
||||
|
||||
assert_eq!(room.inner.prev_batch(), None);
|
||||
assert_eq!(room.prev_batch(), None);
|
||||
|
||||
room.update(room_response!({"prev_batch": "t111_222_333"}), vec![]);
|
||||
assert_eq!(room.inner.prev_batch(), Some("t111_222_333".to_owned()));
|
||||
assert_eq!(room.prev_batch(), Some("t111_222_333".to_owned()));
|
||||
|
||||
room.update(room_response!({}), vec![]);
|
||||
assert_eq!(room.inner.prev_batch(), Some("t111_222_333".to_owned()));
|
||||
assert_eq!(room.prev_batch(), Some("t111_222_333".to_owned()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,8 +16,6 @@ use wiremock::{
|
||||
mod client;
|
||||
mod refresh_token;
|
||||
mod room;
|
||||
#[cfg(feature = "experimental-sliding-sync")]
|
||||
mod sliding_sync;
|
||||
|
||||
#[cfg(all(test, not(target_arch = "wasm32")))]
|
||||
#[ctor::ctor]
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
mod common;
|
||||
mod joined;
|
||||
mod left;
|
||||
mod timeline;
|
||||
|
||||
@@ -18,5 +18,8 @@ url = "2.2.2"
|
||||
|
||||
[dependencies.matrix-sdk]
|
||||
path = "../../crates/matrix-sdk"
|
||||
features = ["experimental-timeline"]
|
||||
version = "0.6.0"
|
||||
|
||||
[dependencies.matrix-sdk-ui]
|
||||
path = "../../crates/matrix-sdk-ui"
|
||||
version = "0.6.0"
|
||||
|
||||
@@ -2,6 +2,7 @@ use anyhow::Result;
|
||||
use clap::Parser;
|
||||
use futures::StreamExt;
|
||||
use matrix_sdk::{self, config::SyncSettings, ruma::OwnedRoomId, Client};
|
||||
use matrix_sdk_ui::timeline::RoomExt;
|
||||
use url::Url;
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
|
||||
+1
-1
@@ -174,7 +174,7 @@ fn check_clippy() -> Result<()> {
|
||||
"rustup run {NIGHTLY} cargo clippy --workspace --all-targets
|
||||
--exclude matrix-sdk-crypto --exclude xtask
|
||||
--no-default-features
|
||||
--features native-tls,experimental-sliding-sync,sso-login,experimental-timeline
|
||||
--features native-tls,experimental-sliding-sync,sso-login
|
||||
-- -D warnings"
|
||||
)
|
||||
.run()?;
|
||||
|
||||
Reference in New Issue
Block a user