mirror of
https://github.com/youwen5/iamb.git
synced 2025-06-20 05:39:52 -07:00
parent
3ed87aae05
commit
a2a708f1ae
7 changed files with 271 additions and 64 deletions
55
src/base.rs
55
src/base.rs
|
@ -53,6 +53,7 @@ use matrix_sdk::{
|
|||
OwnedRoomId,
|
||||
OwnedUserId,
|
||||
RoomId,
|
||||
UserId,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -209,11 +210,26 @@ bitflags::bitflags! {
|
|||
/// Fields that rooms and spaces can be sorted by.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub enum SortFieldRoom {
|
||||
/// Sort rooms by whether they have the Favorite tag.
|
||||
Favorite,
|
||||
|
||||
/// Sort rooms by whether they have the Low Priority tag.
|
||||
LowPriority,
|
||||
|
||||
/// Sort rooms by their room name.
|
||||
Name,
|
||||
|
||||
/// Sort rooms by their canonical room alias.
|
||||
Alias,
|
||||
|
||||
/// Sort rooms by their Matrix room identifier.
|
||||
RoomId,
|
||||
|
||||
/// Sort rooms by whether they have unread messages.
|
||||
Unread,
|
||||
|
||||
/// Sort rooms by the timestamps of their most recent messages.
|
||||
Recent,
|
||||
}
|
||||
|
||||
/// Fields that users can be sorted by.
|
||||
|
@ -273,6 +289,8 @@ impl<'de> Visitor<'de> for SortRoomVisitor {
|
|||
let field = match value {
|
||||
"favorite" => SortFieldRoom::Favorite,
|
||||
"lowpriority" => SortFieldRoom::LowPriority,
|
||||
"recent" => SortFieldRoom::Recent,
|
||||
"unread" => SortFieldRoom::Unread,
|
||||
"name" => SortFieldRoom::Name,
|
||||
"alias" => SortFieldRoom::Alias,
|
||||
"id" => SortFieldRoom::RoomId,
|
||||
|
@ -672,6 +690,22 @@ impl EventLocation {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq)]
|
||||
pub struct UnreadInfo {
|
||||
pub(crate) unread: bool,
|
||||
pub(crate) latest: Option<MessageTimeStamp>,
|
||||
}
|
||||
|
||||
impl UnreadInfo {
|
||||
pub fn is_unread(&self) -> bool {
|
||||
self.unread
|
||||
}
|
||||
|
||||
pub fn latest(&self) -> Option<&MessageTimeStamp> {
|
||||
self.latest.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
/// Information about room's the user's joined.
|
||||
#[derive(Default)]
|
||||
pub struct RoomInfo {
|
||||
|
@ -697,9 +731,6 @@ pub struct RoomInfo {
|
|||
/// older than the oldest loaded event, that user will not be included.
|
||||
pub user_receipts: HashMap<OwnedUserId, OwnedEventId>,
|
||||
|
||||
/// An event ID for where we should indicate we've read up to.
|
||||
pub read_till: Option<OwnedEventId>,
|
||||
|
||||
/// A map of message identifiers to a map of reaction events.
|
||||
pub reactions: HashMap<OwnedEventId, MessageReactions>,
|
||||
|
||||
|
@ -810,6 +841,20 @@ impl RoomInfo {
|
|||
msg.html = msg.event.html();
|
||||
}
|
||||
|
||||
/// Indicates whether this room has unread messages.
|
||||
pub fn unreads(&self, settings: &ApplicationSettings) -> UnreadInfo {
|
||||
let last_message = self.messages.last_key_value();
|
||||
let last_receipt = self.get_receipt(&settings.profile.user_id);
|
||||
|
||||
match (last_message, last_receipt) {
|
||||
(Some(((ts, recent), _)), Some(last_read)) => {
|
||||
UnreadInfo { unread: last_read != recent, latest: Some(*ts) }
|
||||
},
|
||||
(Some(((ts, _), _)), None) => UnreadInfo { unread: false, latest: Some(*ts) },
|
||||
(None, _) => UnreadInfo::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Inserts events that couldn't be decrypted into the scrollback.
|
||||
pub fn insert_encrypted(&mut self, msg: RoomEncryptedEvent) {
|
||||
let event_id = msg.event_id().to_owned();
|
||||
|
@ -901,6 +946,10 @@ impl RoomInfo {
|
|||
self.user_receipts.insert(user_id, event_id);
|
||||
}
|
||||
|
||||
pub fn get_receipt(&self, user_id: &UserId) -> Option<&OwnedEventId> {
|
||||
self.user_receipts.get(user_id)
|
||||
}
|
||||
|
||||
fn get_typers(&self) -> &[OwnedUserId] {
|
||||
if let Some((t, users)) = &self.users_typing {
|
||||
if t.elapsed() < Duration::from_secs(4) {
|
||||
|
|
|
@ -32,9 +32,10 @@ const DEFAULT_MEMBERS_SORT: [SortColumn<SortFieldUser>; 2] = [
|
|||
SortColumn(SortFieldUser::UserId, SortOrder::Ascending),
|
||||
];
|
||||
|
||||
const DEFAULT_ROOM_SORT: [SortColumn<SortFieldRoom>; 3] = [
|
||||
const DEFAULT_ROOM_SORT: [SortColumn<SortFieldRoom>; 4] = [
|
||||
SortColumn(SortFieldRoom::Favorite, SortOrder::Ascending),
|
||||
SortColumn(SortFieldRoom::LowPriority, SortOrder::Ascending),
|
||||
SortColumn(SortFieldRoom::Unread, SortOrder::Ascending),
|
||||
SortColumn(SortFieldRoom::Name, SortOrder::Ascending),
|
||||
];
|
||||
|
||||
|
|
25
src/main.rs
25
src/main.rs
|
@ -27,17 +27,10 @@ use std::sync::Arc;
|
|||
use std::time::Duration;
|
||||
|
||||
use clap::Parser;
|
||||
use matrix_sdk::ruma::OwnedUserId;
|
||||
use tokio::sync::Mutex as AsyncMutex;
|
||||
use tracing_subscriber::FmtSubscriber;
|
||||
|
||||
use matrix_sdk::{
|
||||
config::SyncSettings,
|
||||
ruma::{
|
||||
api::client::filter::{FilterDefinition, LazyLoadOptions, RoomEventFilter, RoomFilter},
|
||||
OwnedUserId,
|
||||
},
|
||||
};
|
||||
|
||||
use modalkit::crossterm::{
|
||||
self,
|
||||
cursor::Show as CursorShow,
|
||||
|
@ -719,20 +712,6 @@ async fn login(worker: Requester, settings: &ApplicationSettings) -> IambResult<
|
|||
}
|
||||
}
|
||||
|
||||
// Perform an initial, lazily-loaded sync.
|
||||
let mut room = RoomEventFilter::default();
|
||||
room.lazy_load_options = LazyLoadOptions::Enabled { include_redundant_members: false };
|
||||
|
||||
let mut room_ev = RoomFilter::default();
|
||||
room_ev.state = room;
|
||||
|
||||
let mut filter = FilterDefinition::default();
|
||||
filter.room = room_ev;
|
||||
|
||||
let settings = SyncSettings::new().filter(filter.into());
|
||||
|
||||
worker.client.sync_once(settings).await.map_err(IambError::from)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -744,12 +723,14 @@ fn print_exit<T: Display, N>(v: T) -> N {
|
|||
async fn run(settings: ApplicationSettings) -> IambResult<()> {
|
||||
// Set up the async worker thread and global store.
|
||||
let worker = ClientWorker::spawn(settings.clone()).await;
|
||||
let client = worker.client.clone();
|
||||
let store = ChatStore::new(worker.clone(), settings.clone());
|
||||
let store = Store::new(store);
|
||||
let store = Arc::new(AsyncMutex::new(store));
|
||||
worker.init(store.clone());
|
||||
|
||||
login(worker, &settings).await.unwrap_or_else(print_exit);
|
||||
worker::do_first_sync(client, &store).await;
|
||||
|
||||
fn restore_tty() {
|
||||
let _ = crossterm::terminal::disable_raw_mode();
|
||||
|
|
|
@ -156,7 +156,6 @@ pub fn mock_room() -> RoomInfo {
|
|||
|
||||
event_receipts: HashMap::new(),
|
||||
user_receipts: HashMap::new(),
|
||||
read_till: None,
|
||||
reactions: HashMap::new(),
|
||||
|
||||
fetching: false,
|
||||
|
|
|
@ -79,9 +79,11 @@ use crate::base::{
|
|||
SortFieldRoom,
|
||||
SortFieldUser,
|
||||
SortOrder,
|
||||
UnreadInfo,
|
||||
};
|
||||
|
||||
use self::{room::RoomState, welcome::WelcomeState};
|
||||
use crate::message::MessageTimeStamp;
|
||||
|
||||
pub mod room;
|
||||
pub mod welcome;
|
||||
|
@ -124,11 +126,31 @@ fn selected_text(s: &str, selected: bool) -> Text {
|
|||
Text::from(selected_span(s, selected))
|
||||
}
|
||||
|
||||
fn name_and_labels(name: &str, unread: bool, style: Style) -> (Span<'_>, Vec<Vec<Span<'_>>>) {
|
||||
let name_style = if unread {
|
||||
style.add_modifier(StyleModifier::BOLD)
|
||||
} else {
|
||||
style
|
||||
};
|
||||
|
||||
let name = Span::styled(name, name_style);
|
||||
let labels = if unread {
|
||||
vec![vec![Span::styled("Unread", style)]]
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
|
||||
(name, labels)
|
||||
}
|
||||
|
||||
/// Sort `Some` to be less than `None` so that list items with values come before those without.
|
||||
#[inline]
|
||||
fn some_cmp<T: Ord>(a: Option<T>, b: Option<T>) -> Ordering {
|
||||
fn some_cmp<T, F>(a: Option<T>, b: Option<T>, f: F) -> Ordering
|
||||
where
|
||||
F: Fn(&T, &T) -> Ordering,
|
||||
{
|
||||
match (a, b) {
|
||||
(Some(a), Some(b)) => a.cmp(&b),
|
||||
(Some(a), Some(b)) => f(&a, &b),
|
||||
(None, None) => Ordering::Equal,
|
||||
(None, Some(_)) => Ordering::Greater,
|
||||
(Some(_), None) => Ordering::Less,
|
||||
|
@ -167,8 +189,16 @@ fn room_cmp<T: RoomLikeItem>(a: &T, b: &T, field: &SortFieldRoom) -> Ordering {
|
|||
lowa.cmp(&lowb)
|
||||
},
|
||||
SortFieldRoom::Name => a.name().cmp(b.name()),
|
||||
SortFieldRoom::Alias => some_cmp(a.alias(), b.alias()),
|
||||
SortFieldRoom::Alias => some_cmp(a.alias(), b.alias(), Ord::cmp),
|
||||
SortFieldRoom::RoomId => a.room_id().cmp(b.room_id()),
|
||||
SortFieldRoom::Unread => {
|
||||
// Sort true (unread) before false (read)
|
||||
b.is_unread().cmp(&a.is_unread())
|
||||
},
|
||||
SortFieldRoom::Recent => {
|
||||
// sort larger timestamps towards the top.
|
||||
some_cmp(a.recent_ts(), b.recent_ts(), |a, b| b.cmp(a))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -243,6 +273,8 @@ fn append_tags<'a>(tags: Vec<Vec<Span<'a>>>, spans: &mut Vec<Span<'a>>, style: S
|
|||
trait RoomLikeItem {
|
||||
fn room_id(&self) -> &RoomId;
|
||||
fn has_tag(&self, tag: TagName) -> bool;
|
||||
fn is_unread(&self) -> bool;
|
||||
fn recent_ts(&self) -> Option<&MessageTimeStamp>;
|
||||
fn alias(&self) -> Option<&RoomAliasId>;
|
||||
fn name(&self) -> &str;
|
||||
}
|
||||
|
@ -780,6 +812,7 @@ pub struct GenericChatItem {
|
|||
room_info: MatrixRoomInfo,
|
||||
name: String,
|
||||
alias: Option<OwnedRoomAliasId>,
|
||||
unread: UnreadInfo,
|
||||
is_dm: bool,
|
||||
}
|
||||
|
||||
|
@ -788,17 +821,17 @@ impl GenericChatItem {
|
|||
let room = &room_info.deref().0;
|
||||
let room_id = room.room_id();
|
||||
|
||||
let info = store.application.get_room_info(room_id.to_owned());
|
||||
|
||||
let info = store.application.rooms.get_or_default(room_id.to_owned());
|
||||
let name = info.name.clone().unwrap_or_default();
|
||||
let alias = room.canonical_alias();
|
||||
let unread = info.unreads(&store.application.settings);
|
||||
info.tags = room_info.deref().1.clone();
|
||||
|
||||
if let Some(alias) = &alias {
|
||||
store.application.names.insert(alias.to_string(), room_id.to_owned());
|
||||
}
|
||||
|
||||
GenericChatItem { room_info, name, alias, is_dm }
|
||||
GenericChatItem { room_info, name, alias, is_dm, unread }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -832,6 +865,14 @@ impl RoomLikeItem for GenericChatItem {
|
|||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn recent_ts(&self) -> Option<&MessageTimeStamp> {
|
||||
self.unread.latest()
|
||||
}
|
||||
|
||||
fn is_unread(&self) -> bool {
|
||||
self.unread.is_unread()
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for GenericChatItem {
|
||||
|
@ -842,13 +883,16 @@ impl ToString for GenericChatItem {
|
|||
|
||||
impl ListItem<IambInfo> for GenericChatItem {
|
||||
fn show(&self, selected: bool, _: &ViewportContext<ListCursor>, _: &mut ProgramStore) -> Text {
|
||||
let unread = self.unread.is_unread();
|
||||
let style = selected_style(selected);
|
||||
let mut spans = vec![Span::styled(self.name.as_str(), style)];
|
||||
let mut labels = if self.is_dm {
|
||||
vec![vec![Span::styled("DM", style)]]
|
||||
let (name, mut labels) = name_and_labels(&self.name, unread, style);
|
||||
let mut spans = vec![name];
|
||||
|
||||
labels.push(if self.is_dm {
|
||||
vec![Span::styled("DM", style)]
|
||||
} else {
|
||||
vec![vec![Span::styled("Room", style)]]
|
||||
};
|
||||
vec![Span::styled("Room", style)]
|
||||
});
|
||||
|
||||
if let Some(tags) = &self.tags() {
|
||||
labels.extend(tags.keys().map(|t| tag_to_span(t, style)));
|
||||
|
@ -879,6 +923,7 @@ pub struct RoomItem {
|
|||
room_info: MatrixRoomInfo,
|
||||
name: String,
|
||||
alias: Option<OwnedRoomAliasId>,
|
||||
unread: UnreadInfo,
|
||||
}
|
||||
|
||||
impl RoomItem {
|
||||
|
@ -886,16 +931,17 @@ impl RoomItem {
|
|||
let room = &room_info.deref().0;
|
||||
let room_id = room.room_id();
|
||||
|
||||
let info = store.application.get_room_info(room_id.to_owned());
|
||||
let info = store.application.rooms.get_or_default(room_id.to_owned());
|
||||
let name = info.name.clone().unwrap_or_default();
|
||||
let alias = room.canonical_alias();
|
||||
let unread = info.unreads(&store.application.settings);
|
||||
info.tags = room_info.deref().1.clone();
|
||||
|
||||
if let Some(alias) = &alias {
|
||||
store.application.names.insert(alias.to_string(), room_id.to_owned());
|
||||
}
|
||||
|
||||
RoomItem { room_info, name, alias }
|
||||
RoomItem { room_info, name, alias, unread }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -929,6 +975,14 @@ impl RoomLikeItem for RoomItem {
|
|||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn recent_ts(&self) -> Option<&MessageTimeStamp> {
|
||||
self.unread.latest()
|
||||
}
|
||||
|
||||
fn is_unread(&self) -> bool {
|
||||
self.unread.is_unread()
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for RoomItem {
|
||||
|
@ -939,17 +993,18 @@ impl ToString for RoomItem {
|
|||
|
||||
impl ListItem<IambInfo> for RoomItem {
|
||||
fn show(&self, selected: bool, _: &ViewportContext<ListCursor>, _: &mut ProgramStore) -> Text {
|
||||
if let Some(tags) = &self.tags() {
|
||||
let unread = self.unread.is_unread();
|
||||
let style = selected_style(selected);
|
||||
let mut spans = vec![Span::styled(self.name.as_str(), style)];
|
||||
let tags = tags.keys().map(|t| tag_to_span(t, style)).collect();
|
||||
let (name, mut labels) = name_and_labels(&self.name, unread, style);
|
||||
let mut spans = vec![name];
|
||||
|
||||
append_tags(tags, &mut spans, style);
|
||||
if let Some(tags) = &self.tags() {
|
||||
labels.extend(tags.keys().map(|t| tag_to_span(t, style)));
|
||||
}
|
||||
|
||||
append_tags(labels, &mut spans, style);
|
||||
|
||||
Text::from(Line::from(spans))
|
||||
} else {
|
||||
selected_text(self.name.as_str(), selected)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_word(&self) -> Option<String> {
|
||||
|
@ -973,15 +1028,20 @@ pub struct DirectItem {
|
|||
room_info: MatrixRoomInfo,
|
||||
name: String,
|
||||
alias: Option<OwnedRoomAliasId>,
|
||||
unread: UnreadInfo,
|
||||
}
|
||||
|
||||
impl DirectItem {
|
||||
fn new(room_info: MatrixRoomInfo, store: &mut ProgramStore) -> Self {
|
||||
let room_id = room_info.0.room_id().to_owned();
|
||||
let name = store.application.get_room_info(room_id).name.clone().unwrap_or_default();
|
||||
let alias = room_info.0.canonical_alias();
|
||||
|
||||
DirectItem { room_info, name, alias }
|
||||
let info = store.application.rooms.get_or_default(room_id);
|
||||
let name = info.name.clone().unwrap_or_default();
|
||||
let unread = info.unreads(&store.application.settings);
|
||||
info.tags = room_info.deref().1.clone();
|
||||
|
||||
DirectItem { room_info, name, alias, unread }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -1015,6 +1075,14 @@ impl RoomLikeItem for DirectItem {
|
|||
fn room_id(&self) -> &RoomId {
|
||||
self.room().room_id()
|
||||
}
|
||||
|
||||
fn recent_ts(&self) -> Option<&MessageTimeStamp> {
|
||||
self.unread.latest()
|
||||
}
|
||||
|
||||
fn is_unread(&self) -> bool {
|
||||
self.unread.is_unread()
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for DirectItem {
|
||||
|
@ -1025,17 +1093,18 @@ impl ToString for DirectItem {
|
|||
|
||||
impl ListItem<IambInfo> for DirectItem {
|
||||
fn show(&self, selected: bool, _: &ViewportContext<ListCursor>, _: &mut ProgramStore) -> Text {
|
||||
if let Some(tags) = &self.tags() {
|
||||
let unread = self.unread.is_unread();
|
||||
let style = selected_style(selected);
|
||||
let mut spans = vec![Span::styled(self.name.as_str(), style)];
|
||||
let tags = tags.keys().map(|t| tag_to_span(t, style)).collect();
|
||||
let (name, mut labels) = name_and_labels(&self.name, unread, style);
|
||||
let mut spans = vec![name];
|
||||
|
||||
append_tags(tags, &mut spans, style);
|
||||
if let Some(tags) = &self.tags() {
|
||||
labels.extend(tags.keys().map(|t| tag_to_span(t, style)));
|
||||
}
|
||||
|
||||
append_tags(labels, &mut spans, style);
|
||||
|
||||
Text::from(Line::from(spans))
|
||||
} else {
|
||||
selected_text(self.name.as_str(), selected)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_word(&self) -> Option<String> {
|
||||
|
@ -1103,6 +1172,16 @@ impl RoomLikeItem for SpaceItem {
|
|||
// exposes them, so we'll just always return false here for now.
|
||||
false
|
||||
}
|
||||
|
||||
fn recent_ts(&self) -> Option<&MessageTimeStamp> {
|
||||
// XXX: this needs to determine the room with most recent message and return its timestamp.
|
||||
None
|
||||
}
|
||||
|
||||
fn is_unread(&self) -> bool {
|
||||
// XXX: this needs to check whether the space contains rooms with unread messages
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for SpaceItem {
|
||||
|
@ -1433,6 +1512,7 @@ mod tests {
|
|||
tags: Vec<TagName>,
|
||||
alias: Option<OwnedRoomAliasId>,
|
||||
name: &'static str,
|
||||
unread: UnreadInfo,
|
||||
}
|
||||
|
||||
impl RoomLikeItem for &TestRoomItem {
|
||||
|
@ -1451,6 +1531,14 @@ mod tests {
|
|||
fn name(&self) -> &str {
|
||||
self.name
|
||||
}
|
||||
|
||||
fn recent_ts(&self) -> Option<&MessageTimeStamp> {
|
||||
self.unread.latest()
|
||||
}
|
||||
|
||||
fn is_unread(&self) -> bool {
|
||||
self.unread.is_unread()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -1462,6 +1550,7 @@ mod tests {
|
|||
tags: vec![TagName::Favorite],
|
||||
alias: Some(room_alias_id!("#room1:example.com").to_owned()),
|
||||
name: "Z",
|
||||
unread: UnreadInfo::default(),
|
||||
};
|
||||
|
||||
let room2 = TestRoomItem {
|
||||
|
@ -1469,6 +1558,7 @@ mod tests {
|
|||
tags: vec![],
|
||||
alias: Some(room_alias_id!("#a:example.com").to_owned()),
|
||||
name: "Unnamed Room",
|
||||
unread: UnreadInfo::default(),
|
||||
};
|
||||
|
||||
let room3 = TestRoomItem {
|
||||
|
@ -1476,6 +1566,7 @@ mod tests {
|
|||
tags: vec![],
|
||||
alias: None,
|
||||
name: "Cool Room",
|
||||
unread: UnreadInfo::default(),
|
||||
};
|
||||
|
||||
// Sort by Name ascending.
|
||||
|
@ -1510,4 +1601,51 @@ mod tests {
|
|||
rooms.sort_by(|a, b| room_fields_cmp(a, b, fields));
|
||||
assert_eq!(rooms, vec![&room2, &room3, &room1]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sort_room_recents() {
|
||||
let server = server_name!("example.com");
|
||||
|
||||
let room1 = TestRoomItem {
|
||||
room_id: RoomId::new(server).to_owned(),
|
||||
tags: vec![],
|
||||
alias: None,
|
||||
name: "Room 1",
|
||||
unread: UnreadInfo { unread: false, latest: None },
|
||||
};
|
||||
|
||||
let room2 = TestRoomItem {
|
||||
room_id: RoomId::new(server).to_owned(),
|
||||
tags: vec![],
|
||||
alias: None,
|
||||
name: "Room 2",
|
||||
unread: UnreadInfo {
|
||||
unread: false,
|
||||
latest: Some(MessageTimeStamp::OriginServer(40u32.into())),
|
||||
},
|
||||
};
|
||||
|
||||
let room3 = TestRoomItem {
|
||||
room_id: RoomId::new(server).to_owned(),
|
||||
tags: vec![],
|
||||
alias: None,
|
||||
name: "Room 3",
|
||||
unread: UnreadInfo {
|
||||
unread: false,
|
||||
latest: Some(MessageTimeStamp::OriginServer(20u32.into())),
|
||||
},
|
||||
};
|
||||
|
||||
// Sort by Recent ascending.
|
||||
let mut rooms = vec![&room1, &room2, &room3];
|
||||
let fields = &[SortColumn(SortFieldRoom::Recent, SortOrder::Ascending)];
|
||||
rooms.sort_by(|a, b| room_fields_cmp(a, b, fields));
|
||||
assert_eq!(rooms, vec![&room2, &room3, &room1]);
|
||||
|
||||
// Sort by Recent descending.
|
||||
let mut rooms = vec![&room1, &room2, &room3];
|
||||
let fields = &[SortColumn(SortFieldRoom::Recent, SortOrder::Descending)];
|
||||
rooms.sort_by(|a, b| room_fields_cmp(a, b, fields));
|
||||
assert_eq!(rooms, vec![&room1, &room3, &room2]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1342,7 +1342,9 @@ impl<'a> StatefulWidget for Scrollback<'a> {
|
|||
state.cursor.timestamp.is_none()
|
||||
{
|
||||
// If the cursor is at the last message, then update the read marker.
|
||||
info.read_till = info.messages.last_key_value().map(|(k, _)| k.1.clone());
|
||||
if let Some((k, _)) = info.messages.last_key_value() {
|
||||
info.set_receipt(settings.profile.user_id.clone(), k.1.clone());
|
||||
}
|
||||
}
|
||||
|
||||
// Check whether we should load older messages for this room.
|
||||
|
|
|
@ -26,6 +26,7 @@ use matrix_sdk::{
|
|||
room::{Invited, Messages, MessagesOptions, Room as MatrixRoom, RoomMember},
|
||||
ruma::{
|
||||
api::client::{
|
||||
filter::{FilterDefinition, LazyLoadOptions, RoomEventFilter, RoomFilter},
|
||||
room::create_room::v3::{CreationContent, Request as CreateRoomRequest, RoomPreset},
|
||||
room::Visibility,
|
||||
space::get_hierarchy::v1::Request as SpaceHierarchyRequest,
|
||||
|
@ -424,9 +425,8 @@ async fn refresh_rooms_forever(client: &Client, store: &AsyncProgramStore) {
|
|||
let mut interval = tokio::time::interval(Duration::from_secs(5));
|
||||
|
||||
loop {
|
||||
interval.tick().await;
|
||||
|
||||
refresh_rooms(client, store).await;
|
||||
interval.tick().await;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -438,13 +438,14 @@ async fn send_receipts_forever(client: &Client, store: &AsyncProgramStore) {
|
|||
interval.tick().await;
|
||||
|
||||
let locked = store.lock().await;
|
||||
let user_id = &locked.application.settings.profile.user_id;
|
||||
let updates = client
|
||||
.joined_rooms()
|
||||
.into_iter()
|
||||
.filter_map(|room| {
|
||||
let room_id = room.room_id().to_owned();
|
||||
let info = locked.application.rooms.get(&room_id)?;
|
||||
let new_receipt = info.read_till.as_ref()?;
|
||||
let new_receipt = info.get_receipt(user_id)?;
|
||||
let old_receipt = sent.get(&room_id);
|
||||
if Some(new_receipt) != old_receipt {
|
||||
Some((room_id, new_receipt.clone()))
|
||||
|
@ -469,6 +470,42 @@ async fn send_receipts_forever(client: &Client, store: &AsyncProgramStore) {
|
|||
}
|
||||
}
|
||||
|
||||
pub async fn do_first_sync(client: Client, store: &AsyncProgramStore) {
|
||||
// Perform an initial, lazily-loaded sync.
|
||||
let mut room = RoomEventFilter::default();
|
||||
room.lazy_load_options = LazyLoadOptions::Enabled { include_redundant_members: false };
|
||||
|
||||
let mut room_ev = RoomFilter::default();
|
||||
room_ev.state = room;
|
||||
|
||||
let mut filter = FilterDefinition::default();
|
||||
filter.room = room_ev;
|
||||
|
||||
let settings = SyncSettings::new().filter(filter.into());
|
||||
|
||||
if let Err(e) = client.sync_once(settings).await {
|
||||
tracing::error!(err = e.to_string(), "Failed to perform initial sync; will retry later");
|
||||
return;
|
||||
}
|
||||
|
||||
// Populate sync_info with our initial set of rooms/dms/spaces.
|
||||
refresh_rooms(&client, store).await;
|
||||
|
||||
// Insert Need::Messages to fetch accurate recent timestamps in the background.
|
||||
let mut locked = store.lock().await;
|
||||
let ChatStore { sync_info, need_load, .. } = &mut locked.application;
|
||||
|
||||
for room in sync_info.rooms.iter() {
|
||||
let room_id = room.as_ref().0.room_id().to_owned();
|
||||
need_load.insert(room_id, Need::MESSAGES);
|
||||
}
|
||||
|
||||
for room in sync_info.dms.iter() {
|
||||
let room_id = room.as_ref().0.room_id().to_owned();
|
||||
need_load.insert(room_id, Need::MESSAGES);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum LoginStyle {
|
||||
SessionRestore(Session),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue