diff --git a/Cargo.lock b/Cargo.lock index 6272eed..5d998a8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1600,9 +1600,9 @@ dependencies = [ [[package]] name = "modalkit" -version = "0.0.7" +version = "0.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc4385b7136847cd063fbf093f0aa21a098398e26dc9213137fdd4e4f593d6bf" +checksum = "7f4e400066e546471efee517b7e5e3ca5af2c04014e76289aecc7af621011bba" dependencies = [ "anymap2", "bitflags", diff --git a/Cargo.toml b/Cargo.toml index 67a67eb..adecef1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ dirs = "4.0.0" futures = "0.3.21" gethostname = "0.4.1" matrix-sdk = {version = "0.6", default-features = false, features = ["e2e-encryption", "sled", "rustls-tls"]} -modalkit = "0.0.7" +modalkit = "0.0.8" regex = "^1.5" rpassword = "^7.2" serde = "^1.0" diff --git a/README.md b/README.md index 4219f71..2849335 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ two other TUI clients and Element Web: | Room tag showing | :x: ([#15]) | :heavy_check_mark: | :x: | :heavy_check_mark: | | Room tag editing | :x: ([#15]) | :heavy_check_mark: | :x: | :heavy_check_mark: | | Search joined rooms | :x: ([#16]) | :heavy_check_mark: | :x: | :heavy_check_mark: | -| Room user list | :x: ([#6]) | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | +| Room user list | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | | Display Room Description | :x: ([#12]) | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | | Edit Room Description | :x: ([#12]) | :x: | :heavy_check_mark: | :heavy_check_mark: | | Highlights | :x: ([#8]) | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | diff --git a/src/base.rs b/src/base.rs index 2b1decd..58f0087 100644 --- a/src/base.rs +++ b/src/base.rs @@ -26,7 +26,7 @@ use modalkit::{ store::Store, }, env::vim::{ - command::{VimCommand, VimCommandMachine}, + command::{CommandContext, VimCommand, VimCommandMachine}, keybindings::VimMachine, VimContext, }, @@ -59,8 +59,14 @@ pub enum VerifyAction { Mismatch, } +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum RoomAction { + Members(Box>), +} + #[derive(Clone, Debug, Eq, PartialEq)] pub enum IambAction { + Room(RoomAction), Verify(VerifyAction, String), VerifyRequest(String), SendMessage(OwnedRoomId, String), @@ -70,6 +76,7 @@ pub enum IambAction { impl ApplicationAction for IambAction { fn is_edit_sequence(&self, _: &C) -> SequenceStatus { match self { + IambAction::Room(..) => SequenceStatus::Break, IambAction::SendMessage(..) => SequenceStatus::Break, IambAction::ToggleScrollbackFocus => SequenceStatus::Break, IambAction::Verify(..) => SequenceStatus::Break, @@ -79,6 +86,7 @@ impl ApplicationAction for IambAction { fn is_last_action(&self, _: &C) -> SequenceStatus { match self { + IambAction::Room(..) => SequenceStatus::Atom, IambAction::SendMessage(..) => SequenceStatus::Atom, IambAction::ToggleScrollbackFocus => SequenceStatus::Atom, IambAction::Verify(..) => SequenceStatus::Atom, @@ -88,6 +96,7 @@ impl ApplicationAction for IambAction { fn is_last_selection(&self, _: &C) -> SequenceStatus { match self { + IambAction::Room(..) => SequenceStatus::Ignore, IambAction::SendMessage(..) => SequenceStatus::Ignore, IambAction::ToggleScrollbackFocus => SequenceStatus::Ignore, IambAction::Verify(..) => SequenceStatus::Ignore, @@ -97,6 +106,7 @@ impl ApplicationAction for IambAction { fn is_switchable(&self, _: &C) -> bool { match self { + IambAction::Room(..) => false, IambAction::SendMessage(..) => false, IambAction::ToggleScrollbackFocus => false, IambAction::Verify(..) => false, @@ -275,6 +285,14 @@ impl ChatStore { } } + pub fn get_room_title(&self, room_id: &RoomId) -> String { + self.rooms + .get(room_id) + .and_then(|i| i.name.as_ref()) + .map(String::from) + .unwrap_or_else(|| "Untitled Matrix Room".to_string()) + } + pub fn mark_for_load(&mut self, room_id: OwnedRoomId) { self.need_load.insert(room_id); } @@ -346,6 +364,7 @@ impl ApplicationStore for ChatStore {} pub enum IambId { Room(OwnedRoomId), DirectList, + MemberList(OwnedRoomId), RoomList, SpaceList, VerifyList, @@ -375,6 +394,7 @@ pub enum IambBufferId { Command, Room(OwnedRoomId, RoomFocus), DirectList, + MemberList(OwnedRoomId), RoomList, SpaceList, VerifyList, @@ -387,6 +407,7 @@ impl IambBufferId { IambBufferId::Command => None, IambBufferId::Room(room, _) => Some(IambId::Room(room.clone())), IambBufferId::DirectList => Some(IambId::DirectList), + IambBufferId::MemberList(room) => Some(IambId::MemberList(room.clone())), IambBufferId::RoomList => Some(IambId::RoomList), IambBufferId::SpaceList => Some(IambId::SpaceList), IambBufferId::VerifyList => Some(IambId::VerifyList), diff --git a/src/commands.rs b/src/commands.rs index b50a3b2..51c4567 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -1,5 +1,5 @@ use modalkit::{ - editing::{action::WindowAction, base::OpenTarget}, + editing::base::OpenTarget, env::vim::command::{CommandContext, CommandDescription}, input::commands::{CommandError, CommandResult, CommandStep}, input::InputContext, @@ -11,6 +11,7 @@ use crate::base::{ ProgramCommand, ProgramCommands, ProgramContext, + RoomAction, VerifyAction, }; @@ -22,8 +23,8 @@ fn iamb_verify(desc: CommandDescription, ctx: &mut ProgContext) -> ProgResult { match args.len() { 0 => { - let open = WindowAction::Switch(OpenTarget::Application(IambId::VerifyList)); - let step = CommandStep::Continue(open.into(), ctx.context.take()); + let open = ctx.switch(OpenTarget::Application(IambId::VerifyList)); + let step = CommandStep::Continue(open, ctx.context.take()); return Ok(step); }, @@ -61,7 +62,18 @@ fn iamb_dms(desc: CommandDescription, ctx: &mut ProgContext) -> ProgResult { return Result::Err(CommandError::InvalidArgument); } - let open = WindowAction::Switch(OpenTarget::Application(IambId::DirectList)); + let open = ctx.switch(OpenTarget::Application(IambId::DirectList)); + let step = CommandStep::Continue(open, ctx.context.take()); + + return Ok(step); +} + +fn iamb_members(desc: CommandDescription, ctx: &mut ProgContext) -> ProgResult { + if !desc.arg.text.is_empty() { + return Result::Err(CommandError::InvalidArgument); + } + + let open = IambAction::Room(RoomAction::Members(ctx.clone().into())); let step = CommandStep::Continue(open.into(), ctx.context.take()); return Ok(step); @@ -72,8 +84,8 @@ fn iamb_rooms(desc: CommandDescription, ctx: &mut ProgContext) -> ProgResult { return Result::Err(CommandError::InvalidArgument); } - let open = WindowAction::Switch(OpenTarget::Application(IambId::RoomList)); - let step = CommandStep::Continue(open.into(), ctx.context.take()); + let open = ctx.switch(OpenTarget::Application(IambId::RoomList)); + let step = CommandStep::Continue(open, ctx.context.take()); return Ok(step); } @@ -83,8 +95,8 @@ fn iamb_spaces(desc: CommandDescription, ctx: &mut ProgContext) -> ProgResult { return Result::Err(CommandError::InvalidArgument); } - let open = WindowAction::Switch(OpenTarget::Application(IambId::SpaceList)); - let step = CommandStep::Continue(open.into(), ctx.context.take()); + let open = ctx.switch(OpenTarget::Application(IambId::SpaceList)); + let step = CommandStep::Continue(open, ctx.context.take()); return Ok(step); } @@ -94,8 +106,8 @@ fn iamb_welcome(desc: CommandDescription, ctx: &mut ProgContext) -> ProgResult { return Result::Err(CommandError::InvalidArgument); } - let open = WindowAction::Switch(OpenTarget::Application(IambId::Welcome)); - let step = CommandStep::Continue(open.into(), ctx.context.take()); + let open = ctx.switch(OpenTarget::Application(IambId::Welcome)); + let step = CommandStep::Continue(open, ctx.context.take()); return Ok(step); } @@ -107,8 +119,8 @@ fn iamb_join(desc: CommandDescription, ctx: &mut ProgContext) -> ProgResult { return Result::Err(CommandError::InvalidArgument); } - let open = WindowAction::Switch(args.remove(0)); - let step = CommandStep::Continue(open.into(), ctx.context.take()); + let open = ctx.switch(args.remove(0)); + let step = CommandStep::Continue(open, ctx.context.take()); return Ok(step); } @@ -116,6 +128,7 @@ fn iamb_join(desc: CommandDescription, ctx: &mut ProgContext) -> ProgResult { fn add_iamb_commands(cmds: &mut ProgramCommands) { cmds.add_command(ProgramCommand { names: vec!["dms".into()], f: iamb_dms }); cmds.add_command(ProgramCommand { names: vec!["join".into()], f: iamb_join }); + cmds.add_command(ProgramCommand { names: vec!["members".into()], f: iamb_members }); cmds.add_command(ProgramCommand { names: vec!["rooms".into()], f: iamb_rooms }); cmds.add_command(ProgramCommand { names: vec!["spaces".into()], f: iamb_spaces }); cmds.add_command(ProgramCommand { names: vec!["verify".into()], f: iamb_verify }); @@ -133,6 +146,8 @@ pub fn setup_commands() -> ProgramCommands { #[cfg(test)] mod tests { use super::*; + use modalkit::editing::action::WindowAction; + #[test] fn test_cmd_verify() { let mut cmds = setup_commands(); diff --git a/src/main.rs b/src/main.rs index 95ea7cb..30d9740 100644 --- a/src/main.rs +++ b/src/main.rs @@ -317,7 +317,7 @@ impl Application { fn iamb_run( &mut self, action: IambAction, - _: ProgramContext, + ctx: ProgramContext, store: &mut ProgramStore, ) -> IambResult { let info = match action { @@ -327,6 +327,13 @@ impl Application { None }, + IambAction::Room(act) => { + let acts = self.screen.current_window_mut()?.room_command(act, ctx, store)?; + self.action_prepend(acts); + + None + }, + IambAction::SendMessage(room_id, msg) => { let (event_id, msg) = self.worker.send_message(room_id.clone(), msg)?; let user = store.application.settings.profile.user_id.clone(); diff --git a/src/windows/mod.rs b/src/windows/mod.rs index 851ff2b..db41e1e 100644 --- a/src/windows/mod.rs +++ b/src/windows/mod.rs @@ -3,22 +3,23 @@ use std::collections::hash_map::Entry; use matrix_sdk::{ encryption::verification::{format_emojis, SasVerification}, - room::Room as MatrixRoom, - ruma::RoomId, + room::{Room as MatrixRoom, RoomMember}, + ruma::{events::room::member::MembershipState, OwnedRoomId, RoomId}, DisplayName, }; use modalkit::tui::{ buffer::Buffer, - layout::Rect, + layout::{Alignment, Rect}, style::{Modifier as StyleModifier, Style}, text::{Span, Spans, Text}, - widgets::{Block, Borders, Widget}, + widgets::{Block, Borders, StatefulWidget, Widget}, }; use modalkit::{ editing::{ action::{ + Action, EditError, EditInfo, EditResult, @@ -42,7 +43,7 @@ use modalkit::{ }, }, widgets::{ - list::{ListCursor, ListItem, ListState}, + list::{List, ListCursor, ListItem, ListState}, TermOffset, TerminalCursor, Window, @@ -50,15 +51,19 @@ use modalkit::{ }, }; -use super::base::{ - ChatStore, - IambBufferId, - IambId, - IambInfo, - IambResult, - ProgramAction, - ProgramContext, - ProgramStore, +use crate::{ + base::{ + ChatStore, + IambBufferId, + IambId, + IambInfo, + IambResult, + ProgramAction, + ProgramContext, + ProgramStore, + RoomAction, + }, + message::user_style, }; use self::{room::RoomState, welcome::WelcomeState}; @@ -120,6 +125,7 @@ macro_rules! delegate { match $s { IambWindow::Room($id) => $e, IambWindow::DirectList($id) => $e, + IambWindow::MemberList($id, _) => $e, IambWindow::RoomList($id) => $e, IambWindow::SpaceList($id) => $e, IambWindow::VerifyList($id) => $e, @@ -130,6 +136,7 @@ macro_rules! delegate { pub enum IambWindow { DirectList(DirectListState), + MemberList(MemberListState, OwnedRoomId), Room(RoomState), VerifyList(VerifyListState), RoomList(RoomListState), @@ -146,19 +153,41 @@ impl IambWindow { } } + pub fn room_command( + &mut self, + act: RoomAction, + ctx: ProgramContext, + store: &mut ProgramStore, + ) -> IambResult, ProgramContext)>> { + if let IambWindow::Room(w) = self { + w.room_command(act, ctx, store) + } else { + let msg = "No room currently focused!"; + let err = UIError::Failure(msg.into()); + + return Err(err); + } + } + pub fn get_title(&self, store: &mut ProgramStore) -> String { match self { - IambWindow::Room(w) => w.get_title(store), IambWindow::DirectList(_) => "Direct Messages".to_string(), IambWindow::RoomList(_) => "Rooms".to_string(), IambWindow::SpaceList(_) => "Spaces".to_string(), IambWindow::VerifyList(_) => "Verifications".to_string(), IambWindow::Welcome(_) => "Welcome to iamb".to_string(), + + IambWindow::Room(w) => w.get_title(store), + IambWindow::MemberList(_, room_id) => { + let title = store.application.get_room_title(room_id.as_ref()); + format!("Room Members: {}", title) + }, } } } pub type DirectListState = ListState; +pub type MemberListState = ListState; pub type RoomListState = ListState; pub type SpaceListState = ListState; pub type VerifyListState = ListState; @@ -263,13 +292,35 @@ impl WindowOps for IambWindow { let dms = store.application.worker.direct_messages(); let items = dms.into_iter().map(|(id, name)| DirectItem::new(id, name, store)); state.set(items.collect()); - state.draw(inner, buf, focused, store); + + List::new(store) + .empty_message("No direct messages yet!") + .empty_alignment(Alignment::Center) + .focus(focused) + .render(inner, buf, state); + }, + IambWindow::MemberList(state, room_id) => { + if let Ok(mems) = store.application.worker.members(room_id.clone()) { + let items = mems.into_iter().map(MemberItem::new); + state.set(items.collect()); + } + + List::new(store) + .empty_message("No users here yet!") + .empty_alignment(Alignment::Center) + .focus(focused) + .render(inner, buf, state); }, IambWindow::RoomList(state) => { let joined = store.application.worker.joined_rooms(); let items = joined.into_iter().map(|(id, name)| RoomItem::new(id, name, store)); state.set(items.collect()); - state.draw(inner, buf, focused, store); + + List::new(store) + .empty_message("You haven't joined any rooms yet") + .empty_alignment(Alignment::Center) + .focus(focused) + .render(inner, buf, state); }, IambWindow::SpaceList(state) => { let spaces = store.application.worker.spaces(); @@ -277,6 +328,12 @@ impl WindowOps for IambWindow { spaces.into_iter().map(|(room, name)| SpaceItem::new(room, name, store)); state.set(items.collect()); state.draw(inner, buf, focused, store); + + List::new(store) + .empty_message("You haven't joined any spaces yet") + .empty_alignment(Alignment::Center) + .focus(focused) + .render(inner, buf, state); }, IambWindow::VerifyList(state) => { let verifications = &store.application.verifications; @@ -286,14 +343,29 @@ impl WindowOps for IambWindow { items.sort(); state.set(items); - state.draw(inner, buf, focused, store); + + List::new(store) + .empty_message("No in-progress verifications") + .empty_alignment(Alignment::Center) + .focus(focused) + .render(inner, buf, state); }, IambWindow::Welcome(state) => state.draw(inner, buf, focused, store), } } fn dup(&self, store: &mut ProgramStore) -> Self { - delegate!(self, w => w.dup(store).into()) + match self { + IambWindow::Room(w) => w.dup(store).into(), + IambWindow::DirectList(w) => w.dup(store).into(), + IambWindow::MemberList(w, room_id) => { + IambWindow::MemberList(w.dup(store), room_id.clone()) + }, + IambWindow::RoomList(w) => w.dup(store).into(), + IambWindow::SpaceList(w) => w.dup(store).into(), + IambWindow::VerifyList(w) => w.dup(store).into(), + IambWindow::Welcome(w) => w.dup(store).into(), + } } fn close(&mut self, flags: CloseFlags, store: &mut ProgramStore) -> bool { @@ -314,6 +386,7 @@ impl Window for IambWindow { match self { IambWindow::Room(room) => IambId::Room(room.id().to_owned()), IambWindow::DirectList(_) => IambId::DirectList, + IambWindow::MemberList(_, room_id) => IambId::MemberList(room_id.clone()), IambWindow::RoomList(_) => IambId::RoomList, IambWindow::SpaceList(_) => IambId::SpaceList, IambWindow::VerifyList(_) => IambId::VerifyList, @@ -334,6 +407,13 @@ impl Window for IambWindow { return Ok(list.into()); }, + IambId::MemberList(room_id) => { + let id = IambBufferId::MemberList(room_id.clone()); + let list = MemberListState::new(id, vec![]); + let win = IambWindow::MemberList(list, room_id); + + return Ok(win); + }, IambId::RoomList => { let list = RoomListState::new(IambBufferId::RoomList, vec![]); @@ -412,6 +492,10 @@ impl ListItem for RoomItem { fn show(&self, selected: bool, _: &ViewportContext, _: &mut ProgramStore) -> Text { selected_text(self.name.as_str(), selected) } + + fn get_word(&self) -> Option { + self.room.room_id().to_string().into() + } } impl Promptable for RoomItem { @@ -451,6 +535,10 @@ impl ListItem for DirectItem { fn show(&self, selected: bool, _: &ViewportContext, _: &mut ProgramStore) -> Text { selected_text(self.name.as_str(), selected) } + + fn get_word(&self) -> Option { + self.room.room_id().to_string().into() + } } impl Promptable for DirectItem { @@ -490,6 +578,10 @@ impl ListItem for SpaceItem { fn show(&self, selected: bool, _: &ViewportContext, _: &mut ProgramStore) -> Text { selected_text(self.name.as_str(), selected) } + + fn get_word(&self) -> Option { + self.room.room_id().to_string().into() + } } impl Promptable for SpaceItem { @@ -667,6 +759,10 @@ impl ListItem for VerifyItem { Text { lines } } + + fn get_word(&self) -> Option { + None + } } impl Promptable for VerifyItem { @@ -694,3 +790,77 @@ impl Promptable for VerifyItem { } } } + +#[derive(Clone)] +pub struct MemberItem { + member: RoomMember, +} + +impl MemberItem { + fn new(member: RoomMember) -> Self { + Self { member } + } +} + +impl ToString for MemberItem { + fn to_string(&self) -> String { + self.member.user_id().to_string() + } +} + +impl ListItem for MemberItem { + fn show(&self, selected: bool, _: &ViewportContext, _: &mut ProgramStore) -> Text { + let mut style = user_style(self.member.user_id().as_str()); + + if selected { + style = style.add_modifier(StyleModifier::REVERSED); + } + + let user = Span::styled(self.to_string(), style); + + let state = match self.member.membership() { + MembershipState::Ban => Span::raw(" (banned)").into(), + MembershipState::Invite => Span::raw(" (invited)").into(), + MembershipState::Knock => Span::raw(" (wants to join)").into(), + MembershipState::Leave => Span::raw(" (left)").into(), + MembershipState::Join => None, + _ => None, + }; + + if let Some(state) = state { + Spans(vec![user, state]).into() + } else { + user.into() + } + } + + fn get_word(&self) -> Option { + self.member.user_id().to_string().into() + } +} + +impl Promptable for MemberItem { + fn prompt( + &mut self, + act: &PromptAction, + _: &ProgramContext, + _: &mut ProgramStore, + ) -> EditResult, IambInfo> { + match act { + PromptAction::Submit => Ok(vec![]), + PromptAction::Abort(_) => { + let msg = "Cannot abort entry inside a list"; + let err = EditError::Failure(msg.into()); + + Err(err) + }, + PromptAction::Recall(_, _) => { + let msg = "Cannot recall history inside a list"; + let err = EditError::Failure(msg.into()); + + Err(err) + }, + _ => Err(EditError::Unimplemented("unknown prompt action".to_string())), + } + } +} diff --git a/src/windows/room/chat.rs b/src/windows/room/chat.rs index a6c84ae..3ce1e3e 100644 --- a/src/windows/room/chat.rs +++ b/src/windows/room/chat.rs @@ -94,10 +94,6 @@ impl ChatState { return; } - if matches!(act, EditorAction::History(_)) { - return; - } - if !store.application.settings.tunables.typing_notice { return; } diff --git a/src/windows/room/mod.rs b/src/windows/room/mod.rs index 0f5722a..e1d8624 100644 --- a/src/windows/room/mod.rs +++ b/src/windows/room/mod.rs @@ -6,6 +6,7 @@ use modalkit::tui::{buffer::Buffer, layout::Rect, widgets::StatefulWidget}; use modalkit::{ editing::action::{ + Action, EditInfo, EditResult, Editable, @@ -15,11 +16,29 @@ use modalkit::{ Promptable, Scrollable, }, - editing::base::{CloseFlags, MoveDir1D, PositionList, ScrollStyle, WordStyle}, + editing::base::{ + Axis, + CloseFlags, + Count, + MoveDir1D, + OpenTarget, + PositionList, + ScrollStyle, + WordStyle, + }, + input::InputContext, widgets::{TermOffset, TerminalCursor, WindowOps}, }; -use crate::base::{IambInfo, IambResult, ProgramAction, ProgramContext, ProgramStore}; +use crate::base::{ + IambId, + IambInfo, + IambResult, + ProgramAction, + ProgramContext, + ProgramStore, + RoomAction, +}; use self::chat::ChatState; use self::space::{Space, SpaceState}; @@ -67,14 +86,28 @@ impl RoomState { } } + pub fn room_command( + &mut self, + act: RoomAction, + _: ProgramContext, + _: &mut ProgramStore, + ) -> IambResult, ProgramContext)>> { + match act { + RoomAction::Members(mut cmd) => { + let width = Count::Exact(30); + let act = + cmd.default_axis(Axis::Vertical).default_relation(MoveDir1D::Next).window( + OpenTarget::Application(IambId::MemberList(self.id().to_owned())), + width.into(), + ); + + Ok(vec![(act, cmd.context.take())]) + }, + } + } + pub fn get_title(&self, store: &mut ProgramStore) -> String { - store - .application - .rooms - .get(self.id()) - .and_then(|i| i.name.as_ref()) - .map(String::from) - .unwrap_or_else(|| "Untitled Matrix Room".to_string()) + store.application.get_room_title(self.id()) } pub fn focus_toggle(&mut self) { diff --git a/src/windows/welcome.md b/src/windows/welcome.md index f3a1f50..2c967ec 100644 --- a/src/windows/welcome.md +++ b/src/windows/welcome.md @@ -3,8 +3,8 @@ ## Useful Keybindings - `` will send a typed message +- `^V^J` can be used in Insert mode to enter a newline without submitting - `O`/`o` can be used to insert blank lines before and after the cursor line -- `^O` can be used in Insert mode to enter a single Normal mode keybinding sequence - `^Wm` can be used to toggle whether the message bar or scrollback is selected - `^Wz` can be used to toggle whether the current window takes up the full screen @@ -12,6 +12,7 @@ - `:dms` will open a list of direct messages - `:rooms` will open a list of joined rooms +- `:members` will open a list of members for the currently focused room or space - `:spaces` will open a list of joined spaces - `:join` can be used to switch to join a new room or start a direct message - `:split` and `:vsplit` can be used to open rooms in a new window diff --git a/src/worker.rs b/src/worker.rs index 1889278..2b88ece 100644 --- a/src/worker.rs +++ b/src/worker.rs @@ -15,7 +15,7 @@ use matrix_sdk::{ encryption::verification::{SasVerification, Verification}, event_handler::Ctx, reqwest, - room::{Messages, MessagesOptions, Room as MatrixRoom}, + room::{Messages, MessagesOptions, Room as MatrixRoom, RoomMember}, ruma::{ api::client::{ room::create_room::v3::{Request as CreateRoomRequest, RoomPreset}, @@ -102,6 +102,7 @@ pub enum WorkerTask { GetRoom(OwnedRoomId, ClientReply>), JoinRoom(String, ClientReply>), JoinedRooms(ClientReply>), + Members(OwnedRoomId, ClientReply>>), SpaceMembers(OwnedRoomId, ClientReply>>), Spaces(ClientReply>), SendMessage(OwnedRoomId, String, ClientReply>), @@ -187,6 +188,14 @@ impl Requester { return response.recv(); } + pub fn members(&self, room_id: OwnedRoomId) -> IambResult> { + let (reply, response) = oneshot(); + + self.tx.send(WorkerTask::Members(room_id, reply)).unwrap(); + + return response.recv(); + } + pub fn space_members(&self, space: OwnedRoomId) -> IambResult> { let (reply, response) = oneshot(); @@ -327,6 +336,10 @@ impl ClientWorker { assert!(self.initialized); reply.send(self.login_and_sync(style).await); }, + WorkerTask::Members(room_id, reply) => { + assert!(self.initialized); + reply.send(self.members(room_id).await); + }, WorkerTask::SpaceMembers(space, reply) => { assert!(self.initialized); reply.send(self.space_members(space).await); @@ -744,6 +757,14 @@ impl ClientWorker { } } + async fn members(&mut self, room_id: OwnedRoomId) -> IambResult> { + if let Some(room) = self.client.get_room(room_id.as_ref()) { + Ok(room.active_members().await.map_err(IambError::from)?) + } else { + Err(IambError::UnknownRoom(room_id).into()) + } + } + async fn space_members(&mut self, space: OwnedRoomId) -> IambResult> { let mut req = SpaceHierarchyRequest::new(&space); req.limit = Some(1000u32.into());