Reduce number of Tokio workers (#129)

This commit is contained in:
Ulyssa 2023-07-05 15:25:42 -07:00
parent 8d4539831f
commit 61aba80be1
No known key found for this signature in database
GPG key ID: 1B3965A3D18B9B64
7 changed files with 232 additions and 258 deletions

1
Cargo.lock generated
View file

@ -1522,6 +1522,7 @@ dependencies = [
"css-color-parser", "css-color-parser",
"dirs", "dirs",
"emojis", "emojis",
"futures",
"gethostname 0.4.1", "gethostname 0.4.1",
"html5ever", "html5ever",
"image", "image",

View file

@ -31,6 +31,7 @@ comrak = {version = "0.18.0", features = ["shortcodes"]}
css-color-parser = "0.1.2" css-color-parser = "0.1.2"
dirs = "4.0.0" dirs = "4.0.0"
emojis = "~0.5.2" emojis = "~0.5.2"
futures = "0.3"
gethostname = "0.4.1" gethostname = "0.4.1"
html5ever = "0.26.0" html5ever = "0.26.0"
image = "0.24.5" image = "0.24.5"

View file

@ -21,7 +21,7 @@ use url::Url;
use matrix_sdk::{ use matrix_sdk::{
encryption::verification::SasVerification, encryption::verification::SasVerification,
room::Joined, room::{Joined, Room as MatrixRoom},
ruma::{ ruma::{
events::{ events::{
reaction::ReactionEvent, reaction::ReactionEvent,
@ -644,6 +644,13 @@ fn emoji_map() -> CompletionMap<String, &'static Emoji> {
return emojis; return emojis;
} }
#[derive(Default)]
pub struct SyncInfo {
pub spaces: Vec<MatrixRoom>,
pub rooms: Vec<Arc<(MatrixRoom, Option<Tags>)>>,
pub dms: Vec<Arc<(MatrixRoom, Option<Tags>)>>,
}
pub struct ChatStore { pub struct ChatStore {
pub cmds: ProgramCommands, pub cmds: ProgramCommands,
pub worker: Requester, pub worker: Requester,
@ -654,6 +661,7 @@ pub struct ChatStore {
pub settings: ApplicationSettings, pub settings: ApplicationSettings,
pub need_load: HashSet<OwnedRoomId>, pub need_load: HashSet<OwnedRoomId>,
pub emojis: CompletionMap<String, &'static Emoji>, pub emojis: CompletionMap<String, &'static Emoji>,
pub sync_info: SyncInfo,
} }
impl ChatStore { impl ChatStore {
@ -663,12 +671,14 @@ impl ChatStore {
settings, settings,
cmds: crate::commands::setup_commands(), cmds: crate::commands::setup_commands(),
emojis: emoji_map(),
names: Default::default(), names: Default::default(),
rooms: Default::default(), rooms: Default::default(),
presences: Default::default(), presences: Default::default(),
verifications: Default::default(), verifications: Default::default(),
need_load: Default::default(), need_load: Default::default(),
emojis: emoji_map(), sync_info: Default::default(),
} }
} }

View file

@ -712,6 +712,7 @@ fn main() -> IambResult<()> {
let rt = tokio::runtime::Builder::new_multi_thread() let rt = tokio::runtime::Builder::new_multi_thread()
.enable_all() .enable_all()
.worker_threads(2)
.thread_name_fn(|| { .thread_name_fn(|| {
static ATOMIC_ID: AtomicUsize = AtomicUsize::new(0); static ATOMIC_ID: AtomicUsize = AtomicUsize::new(0);
let id = ATOMIC_ID.fetch_add(1, Ordering::SeqCst); let id = ATOMIC_ID.fetch_add(1, Ordering::SeqCst);

View file

@ -1,4 +1,6 @@
use std::cmp::{Ord, Ordering, PartialOrd}; use std::cmp::{Ord, Ordering, PartialOrd};
use std::ops::Deref;
use std::sync::Arc;
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use matrix_sdk::{ use matrix_sdk::{
@ -10,7 +12,6 @@ use matrix_sdk::{
OwnedRoomId, OwnedRoomId,
RoomId, RoomId,
}, },
DisplayName,
}; };
use modalkit::tui::{ use modalkit::tui::{
@ -78,6 +79,8 @@ use self::{room::RoomState, welcome::WelcomeState};
pub mod room; pub mod room;
pub mod welcome; pub mod welcome;
type MatrixRoomInfo = Arc<(MatrixRoom, Option<Tags>)>;
const MEMBER_FETCH_DEBOUNCE: Duration = Duration::from_secs(5); const MEMBER_FETCH_DEBOUNCE: Duration = Duration::from_secs(5);
#[inline] #[inline]
@ -380,10 +383,13 @@ impl WindowOps<IambInfo> for IambWindow {
match self { match self {
IambWindow::Room(state) => state.draw(area, buf, focused, store), IambWindow::Room(state) => state.draw(area, buf, focused, store),
IambWindow::DirectList(state) => { IambWindow::DirectList(state) => {
let dms = store.application.worker.direct_messages(); let mut items = store
let mut items = dms .application
.sync_info
.dms
.clone()
.into_iter() .into_iter()
.map(|(id, name, tags)| DirectItem::new(id, name, tags, store)) .map(|room_info| DirectItem::new(room_info, store))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
items.sort(); items.sort();
@ -416,10 +422,13 @@ impl WindowOps<IambInfo> for IambWindow {
.render(area, buf, state); .render(area, buf, state);
}, },
IambWindow::RoomList(state) => { IambWindow::RoomList(state) => {
let joined = store.application.worker.active_rooms(); let mut items = store
let mut items = joined .application
.sync_info
.rooms
.clone()
.into_iter() .into_iter()
.map(|(room, name, tags)| RoomItem::new(room, name, tags, store)) .map(|room_info| RoomItem::new(room_info, store))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
items.sort(); items.sort();
@ -432,9 +441,13 @@ impl WindowOps<IambInfo> for IambWindow {
.render(area, buf, state); .render(area, buf, state);
}, },
IambWindow::SpaceList(state) => { IambWindow::SpaceList(state) => {
let spaces = store.application.worker.spaces(); let items = store
let items = .application
spaces.into_iter().map(|(room, name)| SpaceItem::new(room, name, store)); .sync_info
.spaces
.clone()
.into_iter()
.map(|room| SpaceItem::new(room, store));
state.set(items.collect()); state.set(items.collect());
state.draw(area, buf, focused, store); state.draw(area, buf, focused, store);
@ -639,36 +652,45 @@ impl Window<IambInfo> for IambWindow {
#[derive(Clone)] #[derive(Clone)]
pub struct RoomItem { pub struct RoomItem {
room: MatrixRoom, room_info: MatrixRoomInfo,
tags: Option<Tags>,
name: String, name: String,
} }
impl RoomItem { impl RoomItem {
fn new( fn new(room_info: MatrixRoomInfo, store: &mut ProgramStore) -> Self {
room: MatrixRoom, let room = &room_info.deref().0;
name: DisplayName,
tags: Option<Tags>,
store: &mut ProgramStore,
) -> Self {
let name = name.to_string();
let room_id = room.room_id(); let room_id = room.room_id();
let info = store.application.get_room_info(room_id.to_owned()); let info = store.application.get_room_info(room_id.to_owned());
info.name = name.clone().into(); let name = info.name.clone().unwrap_or_default();
info.tags = tags.clone(); info.tags = room_info.deref().1.clone();
if let Some(alias) = room.canonical_alias() { if let Some(alias) = room.canonical_alias() {
store.application.names.insert(alias.to_string(), room_id.to_owned()); store.application.names.insert(alias.to_string(), room_id.to_owned());
} }
RoomItem { room, tags, name } RoomItem { room_info, name }
}
#[inline]
fn room(&self) -> &MatrixRoom {
&self.room_info.deref().0
}
#[inline]
fn room_id(&self) -> &RoomId {
self.room().room_id()
}
#[inline]
fn tags(&self) -> &Option<Tags> {
&self.room_info.deref().1
} }
} }
impl PartialEq for RoomItem { impl PartialEq for RoomItem {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
self.room.room_id() == other.room.room_id() self.room_id() == other.room_id()
} }
} }
@ -676,7 +698,7 @@ impl Eq for RoomItem {}
impl Ord for RoomItem { impl Ord for RoomItem {
fn cmp(&self, other: &Self) -> Ordering { fn cmp(&self, other: &Self) -> Ordering {
tag_cmp(&self.tags, &other.tags).then_with(|| room_cmp(&self.room, &other.room)) tag_cmp(self.tags(), other.tags()).then_with(|| room_cmp(self.room(), other.room()))
} }
} }
@ -694,7 +716,7 @@ impl ToString for RoomItem {
impl ListItem<IambInfo> for RoomItem { impl ListItem<IambInfo> for RoomItem {
fn show(&self, selected: bool, _: &ViewportContext<ListCursor>, _: &mut ProgramStore) -> Text { fn show(&self, selected: bool, _: &ViewportContext<ListCursor>, _: &mut ProgramStore) -> Text {
if let Some(tags) = &self.tags { if let Some(tags) = &self.tags() {
let style = selected_style(selected); let style = selected_style(selected);
let mut spans = vec![Span::styled(self.name.as_str(), style)]; let mut spans = vec![Span::styled(self.name.as_str(), style)];
@ -707,7 +729,7 @@ impl ListItem<IambInfo> for RoomItem {
} }
fn get_word(&self) -> Option<String> { fn get_word(&self) -> Option<String> {
self.room.room_id().to_string().into() self.room_id().to_string().into()
} }
} }
@ -718,29 +740,37 @@ impl Promptable<ProgramContext, ProgramStore, IambInfo> for RoomItem {
ctx: &ProgramContext, ctx: &ProgramContext,
_: &mut ProgramStore, _: &mut ProgramStore,
) -> EditResult<Vec<(ProgramAction, ProgramContext)>, IambInfo> { ) -> EditResult<Vec<(ProgramAction, ProgramContext)>, IambInfo> {
room_prompt(self.room.room_id(), act, ctx) room_prompt(self.room_id(), act, ctx)
} }
} }
#[derive(Clone)] #[derive(Clone)]
pub struct DirectItem { pub struct DirectItem {
room: MatrixRoom, room_info: MatrixRoomInfo,
tags: Option<Tags>,
name: String, name: String,
} }
impl DirectItem { impl DirectItem {
fn new( fn new(room_info: MatrixRoomInfo, store: &mut ProgramStore) -> Self {
room: MatrixRoom, let room_id = room_info.deref().0.room_id().to_owned();
name: DisplayName, let name = store.application.get_room_info(room_id).name.clone().unwrap_or_default();
tags: Option<Tags>,
store: &mut ProgramStore,
) -> Self {
let name = name.to_string();
store.application.set_room_name(room.room_id(), name.as_str()); DirectItem { room_info, name }
}
DirectItem { room, tags, name } #[inline]
fn room(&self) -> &MatrixRoom {
&self.room_info.deref().0
}
#[inline]
fn room_id(&self) -> &RoomId {
self.room().room_id()
}
#[inline]
fn tags(&self) -> &Option<Tags> {
&self.room_info.deref().1
} }
} }
@ -752,7 +782,7 @@ impl ToString for DirectItem {
impl ListItem<IambInfo> for DirectItem { impl ListItem<IambInfo> for DirectItem {
fn show(&self, selected: bool, _: &ViewportContext<ListCursor>, _: &mut ProgramStore) -> Text { fn show(&self, selected: bool, _: &ViewportContext<ListCursor>, _: &mut ProgramStore) -> Text {
if let Some(tags) = &self.tags { if let Some(tags) = &self.tags() {
let style = selected_style(selected); let style = selected_style(selected);
let mut spans = vec![Span::styled(self.name.as_str(), style)]; let mut spans = vec![Span::styled(self.name.as_str(), style)];
@ -765,13 +795,13 @@ impl ListItem<IambInfo> for DirectItem {
} }
fn get_word(&self) -> Option<String> { fn get_word(&self) -> Option<String> {
self.room.room_id().to_string().into() self.room_id().to_string().into()
} }
} }
impl PartialEq for DirectItem { impl PartialEq for DirectItem {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
self.room.room_id() == other.room.room_id() self.room_id() == other.room_id()
} }
} }
@ -779,7 +809,7 @@ impl Eq for DirectItem {}
impl Ord for DirectItem { impl Ord for DirectItem {
fn cmp(&self, other: &Self) -> Ordering { fn cmp(&self, other: &Self) -> Ordering {
tag_cmp(&self.tags, &other.tags).then_with(|| room_cmp(&self.room, &other.room)) tag_cmp(self.tags(), other.tags()).then_with(|| room_cmp(self.room(), other.room()))
} }
} }
@ -796,7 +826,7 @@ impl Promptable<ProgramContext, ProgramStore, IambInfo> for DirectItem {
ctx: &ProgramContext, ctx: &ProgramContext,
_: &mut ProgramStore, _: &mut ProgramStore,
) -> EditResult<Vec<(ProgramAction, ProgramContext)>, IambInfo> { ) -> EditResult<Vec<(ProgramAction, ProgramContext)>, IambInfo> {
room_prompt(self.room.room_id(), act, ctx) room_prompt(self.room_id(), act, ctx)
} }
} }
@ -807,11 +837,14 @@ pub struct SpaceItem {
} }
impl SpaceItem { impl SpaceItem {
fn new(room: MatrixRoom, name: DisplayName, store: &mut ProgramStore) -> Self { fn new(room: MatrixRoom, store: &mut ProgramStore) -> Self {
let name = name.to_string();
let room_id = room.room_id(); let room_id = room.room_id();
let name = store
store.application.set_room_name(room_id, name.as_str()); .application
.get_room_info(room_id.to_owned())
.name
.clone()
.unwrap_or_default();
if let Some(alias) = room.canonical_alias() { if let Some(alias) = room.canonical_alias() {
store.application.names.insert(alias.to_string(), room_id.to_owned()); store.application.names.insert(alias.to_string(), room_id.to_owned());

View file

@ -120,11 +120,12 @@ impl<'a> StatefulWidget for Space<'a> {
let items = members let items = members
.into_iter() .into_iter()
.filter_map(|id| { .filter_map(|id| {
let (room, name, tags) = let (room, _, tags) =
self.store.application.worker.get_room(id.clone()).ok()?; self.store.application.worker.get_room(id.clone()).ok()?;
let room_info = std::sync::Arc::new((room, tags));
if id != state.room_id { if id != state.room_id {
Some(RoomItem::new(room, name, tags, self.store)) Some(RoomItem::new(room_info, self.store))
} else { } else {
None None
} }

View file

@ -8,6 +8,7 @@ use std::sync::mpsc::{sync_channel, Receiver, SyncSender};
use std::sync::Arc; use std::sync::Arc;
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use futures::{stream::FuturesUnordered, StreamExt};
use gethostname::gethostname; use gethostname::gethostname;
use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender}; use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender};
use tokio::task::JoinHandle; use tokio::task::JoinHandle;
@ -260,19 +261,122 @@ async fn load_insert(room_id: OwnedRoomId, res: MessageFetchResult, store: Async
} }
} }
async fn load_older(client: &Client, store: &AsyncProgramStore) { async fn load_older(client: &Client, store: &AsyncProgramStore) -> usize {
let limit = MIN_MSG_LOAD; let limit = MIN_MSG_LOAD;
let plan = load_plan(store).await;
// Fetch each room separately, so they don't block each other. // Fetch each room separately, so they don't block each other.
for (room_id, fetch_id) in plan.into_iter() { load_plan(store)
let client = client.clone(); .await
let store = store.clone(); .into_iter()
.map(|(room_id, fetch_id)| {
let client = client.clone();
let store = store.clone();
tokio::spawn(async move { async move {
let res = load_older_one(client, room_id.as_ref(), fetch_id, limit).await; let res = load_older_one(client, room_id.as_ref(), fetch_id, limit).await;
load_insert(room_id, res, store).await; load_insert(room_id, res, store).await;
}); }
})
.collect::<FuturesUnordered<_>>()
.count()
.await
}
async fn load_older_forever(client: &Client, store: &AsyncProgramStore) {
// Load older messages every 2 seconds.
let mut interval = tokio::time::interval(Duration::from_secs(2));
loop {
interval.tick().await;
load_older(client, store).await;
}
}
async fn refresh_rooms(client: &Client, store: &AsyncProgramStore) {
let mut names = vec![];
let mut spaces = vec![];
let mut rooms = vec![];
let mut dms = vec![];
for room in client.invited_rooms().into_iter() {
let name = room.display_name().await.unwrap_or(DisplayName::Empty).to_string();
names.push((room.room_id().to_owned(), name));
if room.is_direct() {
let tags = room.tags().await.unwrap_or_default();
dms.push(Arc::new((room.into(), tags)));
} else if room.is_space() {
spaces.push(room.into());
} else {
let tags = room.tags().await.unwrap_or_default();
rooms.push(Arc::new((room.into(), tags)));
}
}
for room in client.joined_rooms().into_iter() {
let name = room.display_name().await.unwrap_or(DisplayName::Empty).to_string();
names.push((room.room_id().to_owned(), name));
if room.is_direct() {
let tags = room.tags().await.unwrap_or_default();
dms.push(Arc::new((room.into(), tags)));
} else if room.is_space() {
spaces.push(room.into());
} else {
let tags = room.tags().await.unwrap_or_default();
rooms.push(Arc::new((room.into(), tags)));
}
}
let mut locked = store.lock().await;
locked.application.sync_info.spaces = spaces;
locked.application.sync_info.rooms = rooms;
locked.application.sync_info.dms = dms;
for (room_id, name) in names {
locked.application.set_room_name(&room_id, &name);
}
}
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;
}
}
async fn refresh_receipts_forever(client: &Client, store: &AsyncProgramStore) {
// Update the displayed read receipts every 5 seconds.
let mut interval = tokio::time::interval(Duration::from_secs(5));
let mut sent = HashMap::<OwnedRoomId, OwnedEventId>::default();
loop {
interval.tick().await;
let receipts = update_receipts(client).await;
let read = store.lock().await.application.set_receipts(receipts).await;
for (room_id, read_till) in read.into_iter() {
if let Some(read_sent) = sent.get(&room_id) {
if read_sent == &read_till {
// Skip unchanged receipts.
continue;
}
}
if let Some(room) = client.get_joined_room(&room_id) {
if room.read_receipt(&read_till).await.is_ok() {
sent.insert(room_id, read_till);
}
}
}
} }
} }
@ -331,8 +435,6 @@ async fn update_receipts(client: &Client) -> Vec<(OwnedRoomId, Receipts)> {
pub type FetchedRoom = (MatrixRoom, DisplayName, Option<Tags>); pub type FetchedRoom = (MatrixRoom, DisplayName, Option<Tags>);
pub enum WorkerTask { pub enum WorkerTask {
ActiveRooms(ClientReply<Vec<FetchedRoom>>),
DirectMessages(ClientReply<Vec<FetchedRoom>>),
Init(AsyncProgramStore, ClientReply<()>), Init(AsyncProgramStore, ClientReply<()>),
Login(LoginStyle, ClientReply<IambResult<EditInfo>>), Login(LoginStyle, ClientReply<IambResult<EditInfo>>),
GetInviter(Invited, ClientReply<IambResult<Option<RoomMember>>>), GetInviter(Invited, ClientReply<IambResult<Option<RoomMember>>>),
@ -340,7 +442,6 @@ pub enum WorkerTask {
JoinRoom(String, ClientReply<IambResult<OwnedRoomId>>), JoinRoom(String, ClientReply<IambResult<OwnedRoomId>>),
Members(OwnedRoomId, ClientReply<IambResult<Vec<RoomMember>>>), Members(OwnedRoomId, ClientReply<IambResult<Vec<RoomMember>>>),
SpaceMembers(OwnedRoomId, ClientReply<IambResult<Vec<OwnedRoomId>>>), SpaceMembers(OwnedRoomId, ClientReply<IambResult<Vec<OwnedRoomId>>>),
Spaces(ClientReply<Vec<(MatrixRoom, DisplayName)>>),
TypingNotice(OwnedRoomId), TypingNotice(OwnedRoomId),
Verify(VerifyAction, SasVerification, ClientReply<IambResult<EditInfo>>), Verify(VerifyAction, SasVerification, ClientReply<IambResult<EditInfo>>),
VerifyRequest(OwnedUserId, ClientReply<IambResult<EditInfo>>), VerifyRequest(OwnedUserId, ClientReply<IambResult<EditInfo>>),
@ -349,14 +450,6 @@ pub enum WorkerTask {
impl Debug for WorkerTask { impl Debug for WorkerTask {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> { fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
match self { match self {
WorkerTask::ActiveRooms(_) => {
f.debug_tuple("WorkerTask::ActiveRooms").field(&format_args!("_")).finish()
},
WorkerTask::DirectMessages(_) => {
f.debug_tuple("WorkerTask::DirectMessages")
.field(&format_args!("_"))
.finish()
},
WorkerTask::Init(_, _) => { WorkerTask::Init(_, _) => {
f.debug_tuple("WorkerTask::Init") f.debug_tuple("WorkerTask::Init")
.field(&format_args!("_")) .field(&format_args!("_"))
@ -396,9 +489,6 @@ impl Debug for WorkerTask {
.field(&format_args!("_")) .field(&format_args!("_"))
.finish() .finish()
}, },
WorkerTask::Spaces(_) => {
f.debug_tuple("WorkerTask::Spaces").field(&format_args!("_")).finish()
},
WorkerTask::TypingNotice(room_id) => { WorkerTask::TypingNotice(room_id) => {
f.debug_tuple("WorkerTask::TypingNotice").field(room_id).finish() f.debug_tuple("WorkerTask::TypingNotice").field(room_id).finish()
}, },
@ -442,14 +532,6 @@ impl Requester {
return response.recv(); return response.recv();
} }
pub fn direct_messages(&self) -> Vec<FetchedRoom> {
let (reply, response) = oneshot();
self.tx.send(WorkerTask::DirectMessages(reply)).unwrap();
return response.recv();
}
pub fn get_inviter(&self, invite: Invited) -> IambResult<Option<RoomMember>> { pub fn get_inviter(&self, invite: Invited) -> IambResult<Option<RoomMember>> {
let (reply, response) = oneshot(); let (reply, response) = oneshot();
@ -474,14 +556,6 @@ impl Requester {
return response.recv(); return response.recv();
} }
pub fn active_rooms(&self) -> Vec<FetchedRoom> {
let (reply, response) = oneshot();
self.tx.send(WorkerTask::ActiveRooms(reply)).unwrap();
return response.recv();
}
pub fn members(&self, room_id: OwnedRoomId) -> IambResult<Vec<RoomMember>> { pub fn members(&self, room_id: OwnedRoomId) -> IambResult<Vec<RoomMember>> {
let (reply, response) = oneshot(); let (reply, response) = oneshot();
@ -498,14 +572,6 @@ impl Requester {
return response.recv(); return response.recv();
} }
pub fn spaces(&self) -> Vec<(MatrixRoom, DisplayName)> {
let (reply, response) = oneshot();
self.tx.send(WorkerTask::Spaces(reply)).unwrap();
return response.recv();
}
pub fn typing_notice(&self, room_id: OwnedRoomId) { pub fn typing_notice(&self, room_id: OwnedRoomId) {
self.tx.send(WorkerTask::TypingNotice(room_id)).unwrap(); self.tx.send(WorkerTask::TypingNotice(room_id)).unwrap();
} }
@ -532,7 +598,6 @@ pub struct ClientWorker {
settings: ApplicationSettings, settings: ApplicationSettings,
client: Client, client: Client,
load_handle: Option<JoinHandle<()>>, load_handle: Option<JoinHandle<()>>,
rcpt_handle: Option<JoinHandle<()>>,
sync_handle: Option<JoinHandle<()>>, sync_handle: Option<JoinHandle<()>>,
} }
@ -571,7 +636,6 @@ impl ClientWorker {
settings, settings,
client: client.clone(), client: client.clone(),
load_handle: None, load_handle: None,
rcpt_handle: None,
sync_handle: None, sync_handle: None,
}; };
@ -597,18 +661,10 @@ impl ClientWorker {
if let Some(handle) = self.sync_handle.take() { if let Some(handle) = self.sync_handle.take() {
handle.abort(); handle.abort();
} }
if let Some(handle) = self.rcpt_handle.take() {
handle.abort();
}
} }
async fn run(&mut self, task: WorkerTask) { async fn run(&mut self, task: WorkerTask) {
match task { match task {
WorkerTask::DirectMessages(reply) => {
assert!(self.initialized);
reply.send(self.direct_messages().await);
},
WorkerTask::Init(store, reply) => { WorkerTask::Init(store, reply) => {
assert_eq!(self.initialized, false); assert_eq!(self.initialized, false);
self.init(store).await; self.init(store).await;
@ -626,10 +682,6 @@ impl ClientWorker {
assert!(self.initialized); assert!(self.initialized);
reply.send(self.get_room(room_id).await); reply.send(self.get_room(room_id).await);
}, },
WorkerTask::ActiveRooms(reply) => {
assert!(self.initialized);
reply.send(self.active_rooms().await);
},
WorkerTask::Login(style, reply) => { WorkerTask::Login(style, reply) => {
assert!(self.initialized); assert!(self.initialized);
reply.send(self.login_and_sync(style).await); reply.send(self.login_and_sync(style).await);
@ -642,10 +694,6 @@ impl ClientWorker {
assert!(self.initialized); assert!(self.initialized);
reply.send(self.space_members(space).await); reply.send(self.space_members(space).await);
}, },
WorkerTask::Spaces(reply) => {
assert!(self.initialized);
reply.send(self.spaces().await);
},
WorkerTask::TypingNotice(room_id) => { WorkerTask::TypingNotice(room_id) => {
assert!(self.initialized); assert!(self.initialized);
self.typing_notice(room_id).await; self.typing_notice(room_id).await;
@ -903,50 +951,14 @@ impl ClientWorker {
}, },
); );
self.rcpt_handle = tokio::spawn({
let store = store.clone();
let client = self.client.clone();
let mut sent = HashMap::<OwnedRoomId, OwnedEventId>::default();
async move {
// Update the displayed read receipts every 5 seconds.
let mut interval = tokio::time::interval(Duration::from_secs(5));
loop {
interval.tick().await;
let receipts = update_receipts(&client).await;
let read = store.lock().await.application.set_receipts(receipts).await;
for (room_id, read_till) in read.into_iter() {
if let Some(read_sent) = sent.get(&room_id) {
if read_sent == &read_till {
// Skip unchanged receipts.
continue;
}
}
if let Some(room) = client.get_joined_room(&room_id) {
if room.read_receipt(&read_till).await.is_ok() {
sent.insert(room_id, read_till);
}
}
}
}
}
})
.into();
self.load_handle = tokio::spawn({ self.load_handle = tokio::spawn({
let client = self.client.clone(); let client = self.client.clone();
async move { async move {
// Load older messages every 2 seconds. let load = load_older_forever(&client, &store);
let mut interval = tokio::time::interval(Duration::from_secs(2)); let rcpt = refresh_receipts_forever(&client, &store);
loop { let room = refresh_rooms_forever(&client, &store);
interval.tick().await; let ((), (), ()) = tokio::join!(load, rcpt, room);
load_older(&client, &store).await;
}
} }
}) })
.into(); .into();
@ -1002,31 +1014,30 @@ impl ClientWorker {
Ok(Some(InfoMessage::from("Successfully logged in!"))) Ok(Some(InfoMessage::from("Successfully logged in!")))
} }
async fn direct_message(&mut self, user: OwnedUserId) -> IambResult<FetchedRoom> { async fn direct_message(&mut self, user: OwnedUserId) -> IambResult<OwnedRoomId> {
for (room, name, tags) in self.direct_messages().await { for room in self.client.rooms() {
if !room.is_direct() {
continue;
}
if room.get_member(user.as_ref()).await.map_err(IambError::from)?.is_some() { if room.get_member(user.as_ref()).await.map_err(IambError::from)?.is_some() {
return Ok((room, name, tags)); return Ok(room.room_id().to_owned());
} }
} }
let rt = CreateRoomType::Direct(user.clone()); let rt = CreateRoomType::Direct(user.clone());
let flags = CreateRoomFlags::ENCRYPTED; let flags = CreateRoomFlags::ENCRYPTED;
match create_room(&self.client, None, rt, flags).await { create_room(&self.client, None, rt, flags).await.map_err(|e| {
Ok(room_id) => self.get_room(room_id).await, error!(
Err(e) => { user_id = user.as_str(),
error!( err = e.to_string(),
user_id = user.as_str(), "Failed to create direct message room"
err = e.to_string(), );
"Failed to create direct message room"
);
let msg = format!("Could not open a room with {user}"); let msg = format!("Could not open a room with {user}");
let err = UIError::Failure(msg); UIError::Failure(msg)
})
Err(err)
},
}
} }
async fn get_inviter(&mut self, invited: Invited) -> IambResult<Option<RoomMember>> { async fn get_inviter(&mut self, invited: Invited) -> IambResult<Option<RoomMember>> {
@ -1058,9 +1069,7 @@ impl ClientWorker {
}, },
} }
} else if let Ok(user) = OwnedUserId::try_from(name.as_str()) { } else if let Ok(user) = OwnedUserId::try_from(name.as_str()) {
let room = self.direct_message(user).await?.0; self.direct_message(user).await
return Ok(room.room_id().to_owned());
} else { } else {
let msg = format!("{:?} is not a valid room or user name", name.as_str()); let msg = format!("{:?} is not a valid room or user name", name.as_str());
let err = UIError::Failure(msg); let err = UIError::Failure(msg);
@ -1069,62 +1078,6 @@ impl ClientWorker {
} }
} }
async fn direct_messages(&self) -> Vec<FetchedRoom> {
let mut rooms = vec![];
for room in self.client.invited_rooms().into_iter() {
if !room.is_direct() {
continue;
}
let name = room.display_name().await.unwrap_or(DisplayName::Empty);
let tags = room.tags().await.unwrap_or_default();
rooms.push((room.into(), name, tags));
}
for room in self.client.joined_rooms().into_iter() {
if !room.is_direct() {
continue;
}
let name = room.display_name().await.unwrap_or(DisplayName::Empty);
let tags = room.tags().await.unwrap_or_default();
rooms.push((room.into(), name, tags));
}
return rooms;
}
async fn active_rooms(&self) -> Vec<FetchedRoom> {
let mut rooms = vec![];
for room in self.client.invited_rooms().into_iter() {
if room.is_space() || room.is_direct() {
continue;
}
let name = room.display_name().await.unwrap_or(DisplayName::Empty);
let tags = room.tags().await.unwrap_or_default();
rooms.push((room.into(), name, tags));
}
for room in self.client.joined_rooms().into_iter() {
if room.is_space() || room.is_direct() {
continue;
}
let name = room.display_name().await.unwrap_or(DisplayName::Empty);
let tags = room.tags().await.unwrap_or_default();
rooms.push((room.into(), name, tags));
}
return rooms;
}
async fn members(&mut self, room_id: OwnedRoomId) -> IambResult<Vec<RoomMember>> { async fn members(&mut self, room_id: OwnedRoomId) -> IambResult<Vec<RoomMember>> {
if let Some(room) = self.client.get_room(room_id.as_ref()) { if let Some(room) = self.client.get_room(room_id.as_ref()) {
Ok(room.active_members().await.map_err(IambError::from)?) Ok(room.active_members().await.map_err(IambError::from)?)
@ -1145,32 +1098,6 @@ impl ClientWorker {
Ok(rooms) Ok(rooms)
} }
async fn spaces(&self) -> Vec<(MatrixRoom, DisplayName)> {
let mut spaces = vec![];
for room in self.client.invited_rooms().into_iter() {
if !room.is_space() {
continue;
}
let name = room.display_name().await.unwrap_or(DisplayName::Empty);
spaces.push((room.into(), name));
}
for room in self.client.joined_rooms().into_iter() {
if !room.is_space() {
continue;
}
let name = room.display_name().await.unwrap_or(DisplayName::Empty);
spaces.push((room.into(), name));
}
return spaces;
}
async fn typing_notice(&mut self, room_id: OwnedRoomId) { async fn typing_notice(&mut self, room_id: OwnedRoomId) {
if let Some(room) = self.client.get_joined_room(room_id.as_ref()) { if let Some(room) = self.client.get_joined_room(room_id.as_ref()) {
let _ = room.typing_notice(true).await; let _ = room.typing_notice(true).await;