-
Notifications
You must be signed in to change notification settings - Fork 222
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(ui): RoomList
, step 1: the Foundation
#1953
Conversation
70688d3
to
b669ca5
Compare
pub fn state(&self) -> State { | ||
self.state.get() | ||
} | ||
|
||
pub fn state_stream(&self) -> impl Stream<Item = State> { | ||
Observable::subscribe(&self.state) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you just have one function that returns a Subscriber<State>
, that can be used to obtain both the current value and wait for updates.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah but I don't want to create a new stream every time one wants to get a single value.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"Create a new stream" is a trivial operation: https://docs.rs/eyeball/latest/src/eyeball/subscriber.rs.html#26-28
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Addressed in #2035.
9f56b20
to
f2286da
Compare
This patch does several things. First, `SlidingSync::on_list` is now async, and accept async closures. Second, `SlidingSync::lists` and `::rooms` are behind an `AsyncRwLock` instead of a `StdRwLock`. The rest of the patch updates the consequence of this.
This patch adds a new method on `SlidingSyncList` named `room_list_filtered_stream`, which uses the new `eyeball-im-util` crate with its new `FilteredSubscriber` type.
This patch implements the `RoomList::entries` method, which allows to get a stream of `VectorDiff` over the room list, and that can be filtered.
This patch lands the first design of the `Input` API. An `Input` is something external that can be understood by the `RoomList` state machine. The first inpuput is `Viewport` to change the “viewport” of the `RoomList`, which translates to the change of the ranges of one specific Sliding Sync list.
This patch implements `RoomList::room`, which returns a `Result<Room>`: the room ay or may not exist. A new `Room` type is created. It wraps an `Arc<RoomInner>` type, so that it's cheap to clone it. The `RoomInner` type owns a `SlidingSyncRoom` and `matrix_sdk::room::Room` type. If some API or data are missing on the former, the latter acts as a backup. This is the case for the `Room::name` method which makes it best to return a name to the caller.
This patch changes `RoomInner` to have an `room: Option<matrix_sdk::room::Room>` field to `room: matrix_sdk::room::Room`. `room` is not longer an `Option`. Then, it's easier to have a new `timeline: Timeline` field, along with a `sneaky_timeline: Timeline` field. Both are used by the new `Room::timeline()` and `Room::latest_event()` method.
First off, `SlidingSync::sync_once` no longer returns `Option<UpdateSummary>` but `UpdateSummary`. It simplifies the rest of the patch. Next, `SlidingSync::sync` returns either `Ok(…)` and continues to sync, or it returns `Err(…)` and it stops the sync stream immediately. No more error could be returned with the stream to continue syncing. Finally, the `UnknownPos` error no longer emits an err, but silently skip over the sync-loop until the threshold is reached; which in this case will generate a proper error.
This patch handles errors from the Sliding Sync loop properly. When an error is received, the `RoomList` state machine enters the `Terminated` state. Immediately after that, the sync stream is stopped. When the sync stream is restarted, the next state will be calculated. When the current state is `Terminated`, the next state is the state that led to the `Terminated` state. To avoid having a “first” huge sync (imagine a room list of 1000 rooms, you don't want to “resume” from an error with a first sync for 1000 rooms), lists are partially “reset” depending of the state where the machine is in. An important test has been added to test all possibles cases with errors and list' states.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome stuff, and all the testing gives lot of confidence that it behaves as intended, thanks and great work!
I'm thinking about #2004 in this PR. On the one hand this PR is already gigantic, on the other hand it's really trivial to fix as a one-liner; feel free to embed that fix here too, if you'd like to!
My only concerns below is about add_cached_list
that I think we should temporarily revert to a add_list
, to not trigger our ultra slow cache restoration edge case. Other than that, this is good to go 👍
/// An operation has been requested on an unknown list. | ||
#[error("Unknown list `{0}`")] | ||
UnknownList(String), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a bit strange to expose this error, as it's very internal: it's used only for the ALL_ROOMS_LIST, which is always added at start. I think it would be confused for the user; can we unwrap()
(scary) or at least soft fail with a warning, when that list isn't found anymore?
At the limit. InputHasNotBeenApplied
really means "I couldn't find the list VISIBLE_ROOMS", so if you wanted to keep UnknownList
, we could reuse it here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree. Let's address that with another PR maybe.
/// An input was asked to be applied but it wasn't possible to apply it. | ||
#[error("The input has been not applied")] | ||
InputHasNotBeenApplied(Input), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could encode a type state machine, so that the apply_input
method is only accessible if you're after the AllRooms
state 🙈 (and get rid of this error)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Eh, yeah, maybe!
let sliding_sync = client | ||
.sliding_sync() | ||
.storage_key(Some("matrix-sdk-ui-roomlist".to_string())) | ||
.add_cached_list( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ALL_ROOMS
list is likely to contain many lists and rooms, and reloading from cache + building a timeline will attempt to decrypt all the previous messages present in those rooms.
Perhaps disable with a big TODO
that it should be added back in the future, when the timeline is properly cached?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's address in another PR. The goal of this PR is not to fix this problem.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just to make sure we're on the same page: I am fine with that, but that means we can't use the new Room API until we have 1. either made restoring much faster, 2. or replaced this line with add_list
until we do (1). I'm suggesting we could do (2) right now, so as to be able to use the Room API as soon as possible in the apps.
Another question raised by @jplatte: should we cap the number of events in a timeline owned in a room, to avoid trashing the user's memory with too many events? If I understand correctly, elements are duplicated in the |
That's indeed a problem, but the goal is not to solve it here. Let's fix that with another PR. |
Codecov ReportPatch coverage:
Additional details and impacted files@@ Coverage Diff @@
## main #1953 +/- ##
==========================================
+ Coverage 74.56% 75.24% +0.68%
==========================================
Files 138 147 +9
Lines 15016 16052 +1036
==========================================
+ Hits 11196 12079 +883
- Misses 3820 3973 +153
☔ View full report in Codecov by Sentry. |
Address #1911.
This PR is quite large, but it's quite simple to review. It introduces the new
matrix_sdk_ui::RoomList
type, which acts as a state machine. The code contains the necessary documentation. If something isn't clear, please tell me.