mirror of
https://github.com/youwen5/iamb.git
synced 2025-06-19 21:29:52 -07:00
Support displaying and editing room descriptions (#12)
This commit is contained in:
parent
8ed037afca
commit
38f4795886
13 changed files with 286 additions and 55 deletions
4
Cargo.lock
generated
4
Cargo.lock
generated
|
@ -1600,9 +1600,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "modalkit"
|
||||
version = "0.0.8"
|
||||
version = "0.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f4e400066e546471efee517b7e5e3ca5af2c04014e76289aecc7af621011bba"
|
||||
checksum = "28a676fc7ab6a9fd329ff82d9d291370aafcf904ac3ff9f72397f64529cb1b2d"
|
||||
dependencies = [
|
||||
"anymap2",
|
||||
"bitflags",
|
||||
|
|
|
@ -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.8"
|
||||
modalkit = "0.0.9"
|
||||
regex = "^1.5"
|
||||
rpassword = "^7.2"
|
||||
serde = "^1.0"
|
||||
|
|
|
@ -43,8 +43,8 @@ two other TUI clients and Element Web:
|
|||
| 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 | :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: |
|
||||
| Display Room Description | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
|
||||
| Edit Room Description | :heavy_check_mark: | :x: | :heavy_check_mark: | :heavy_check_mark: |
|
||||
| Highlights | :x: ([#8]) | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
|
||||
| Pushrules | :x: | :heavy_check_mark: | :x: | :heavy_check_mark: |
|
||||
| Send read markers | :x: ([#11]) | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
|
||||
|
|
19
src/base.rs
19
src/base.rs
|
@ -59,9 +59,22 @@ pub enum VerifyAction {
|
|||
Mismatch,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub enum SetRoomField {
|
||||
Name(String),
|
||||
Topic(String),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub enum RoomAction {
|
||||
Members(Box<CommandContext<ProgramContext>>),
|
||||
Set(SetRoomField),
|
||||
}
|
||||
|
||||
impl From<SetRoomField> for RoomAction {
|
||||
fn from(act: SetRoomField) -> Self {
|
||||
RoomAction::Set(act)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
|
@ -73,6 +86,12 @@ pub enum IambAction {
|
|||
ToggleScrollbackFocus,
|
||||
}
|
||||
|
||||
impl From<RoomAction> for IambAction {
|
||||
fn from(act: RoomAction) -> Self {
|
||||
IambAction::Room(act)
|
||||
}
|
||||
}
|
||||
|
||||
impl ApplicationAction for IambAction {
|
||||
fn is_edit_sequence<C: EditContext>(&self, _: &C) -> SequenceStatus {
|
||||
match self {
|
||||
|
|
|
@ -12,6 +12,7 @@ use crate::base::{
|
|||
ProgramCommands,
|
||||
ProgramContext,
|
||||
RoomAction,
|
||||
SetRoomField,
|
||||
VerifyAction,
|
||||
};
|
||||
|
||||
|
@ -125,11 +126,35 @@ fn iamb_join(desc: CommandDescription, ctx: &mut ProgContext) -> ProgResult {
|
|||
return Ok(step);
|
||||
}
|
||||
|
||||
fn iamb_set(desc: CommandDescription, ctx: &mut ProgContext) -> ProgResult {
|
||||
let mut args = desc.arg.strings()?;
|
||||
|
||||
if args.len() != 2 {
|
||||
return Result::Err(CommandError::InvalidArgument);
|
||||
}
|
||||
|
||||
let field = args.remove(0);
|
||||
let value = args.remove(0);
|
||||
|
||||
let act: IambAction = match field.as_str() {
|
||||
"room.name" => RoomAction::Set(SetRoomField::Name(value)).into(),
|
||||
"room.topic" => RoomAction::Set(SetRoomField::Topic(value)).into(),
|
||||
_ => {
|
||||
return Result::Err(CommandError::InvalidArgument);
|
||||
},
|
||||
};
|
||||
|
||||
let step = CommandStep::Continue(act.into(), ctx.context.take());
|
||||
|
||||
return Ok(step);
|
||||
}
|
||||
|
||||
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!["set".into()], f: iamb_set });
|
||||
cmds.add_command(ProgramCommand { names: vec!["spaces".into()], f: iamb_spaces });
|
||||
cmds.add_command(ProgramCommand { names: vec!["verify".into()], f: iamb_verify });
|
||||
cmds.add_command(ProgramCommand { names: vec!["welcome".into()], f: iamb_welcome });
|
||||
|
@ -214,4 +239,48 @@ mod tests {
|
|||
let res = cmds.input_cmd("join foo bar", ctx.clone());
|
||||
assert_eq!(res, Err(CommandError::InvalidArgument));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cmd_set() {
|
||||
let mut cmds = setup_commands();
|
||||
let ctx = ProgramContext::default();
|
||||
|
||||
let res = cmds
|
||||
.input_cmd("set room.topic \"Lots of fun discussion!\"", ctx.clone())
|
||||
.unwrap();
|
||||
let act = IambAction::Room(SetRoomField::Topic("Lots of fun discussion!".into()).into());
|
||||
assert_eq!(res, vec![(act.into(), ctx.clone())]);
|
||||
|
||||
let res = cmds
|
||||
.input_cmd("set room.topic The\\ Discussion\\ Room", ctx.clone())
|
||||
.unwrap();
|
||||
let act = IambAction::Room(SetRoomField::Topic("The Discussion Room".into()).into());
|
||||
assert_eq!(res, vec![(act.into(), ctx.clone())]);
|
||||
|
||||
let res = cmds.input_cmd("set room.topic Development", ctx.clone()).unwrap();
|
||||
let act = IambAction::Room(SetRoomField::Topic("Development".into()).into());
|
||||
assert_eq!(res, vec![(act.into(), ctx.clone())]);
|
||||
|
||||
let res = cmds.input_cmd("set room.name Development", ctx.clone()).unwrap();
|
||||
let act = IambAction::Room(SetRoomField::Name("Development".into()).into());
|
||||
assert_eq!(res, vec![(act.into(), ctx.clone())]);
|
||||
|
||||
let res = cmds
|
||||
.input_cmd("set room.name \"Application Development\"", ctx.clone())
|
||||
.unwrap();
|
||||
let act = IambAction::Room(SetRoomField::Name("Application Development".into()).into());
|
||||
assert_eq!(res, vec![(act.into(), ctx.clone())]);
|
||||
|
||||
let res = cmds.input_cmd("set", ctx.clone());
|
||||
assert_eq!(res, Err(CommandError::InvalidArgument));
|
||||
|
||||
let res = cmds.input_cmd("set room.name", ctx.clone());
|
||||
assert_eq!(res, Err(CommandError::InvalidArgument));
|
||||
|
||||
let res = cmds.input_cmd("set room.topic", ctx.clone());
|
||||
assert_eq!(res, Err(CommandError::InvalidArgument));
|
||||
|
||||
let res = cmds.input_cmd("set room.topic A B C", ctx.clone());
|
||||
assert_eq!(res, Err(CommandError::InvalidArgument));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -100,25 +100,44 @@ impl Tunables {
|
|||
#[derive(Clone)]
|
||||
pub struct DirectoryValues {
|
||||
pub cache: PathBuf,
|
||||
pub logs: PathBuf,
|
||||
pub downloads: PathBuf,
|
||||
}
|
||||
|
||||
#[derive(Clone, Default, Deserialize)]
|
||||
pub struct Directories {
|
||||
pub cache: Option<PathBuf>,
|
||||
pub logs: Option<PathBuf>,
|
||||
pub downloads: Option<PathBuf>,
|
||||
}
|
||||
|
||||
impl Directories {
|
||||
fn merge(self, other: Self) -> Self {
|
||||
Directories { cache: self.cache.or(other.cache) }
|
||||
Directories {
|
||||
cache: self.cache.or(other.cache),
|
||||
logs: self.logs.or(other.logs),
|
||||
downloads: self.downloads.or(other.downloads),
|
||||
}
|
||||
}
|
||||
|
||||
fn values(self) -> DirectoryValues {
|
||||
DirectoryValues {
|
||||
cache: self
|
||||
.cache
|
||||
.or_else(dirs::cache_dir)
|
||||
.expect("no dirs.cache value configured!"),
|
||||
}
|
||||
let cache = self
|
||||
.cache
|
||||
.or_else(dirs::cache_dir)
|
||||
.expect("no dirs.cache value configured!");
|
||||
|
||||
let logs = self.logs.unwrap_or_else(|| {
|
||||
let mut dir = cache.clone();
|
||||
dir.push("logs");
|
||||
dir
|
||||
});
|
||||
|
||||
let downloads = self
|
||||
.downloads
|
||||
.or_else(dirs::download_dir)
|
||||
.expect("no dirs.download value configured!");
|
||||
|
||||
DirectoryValues { cache, logs, downloads }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -162,7 +162,7 @@ impl Application {
|
|||
term.draw(|f| {
|
||||
let area = f.size();
|
||||
|
||||
let screen = Screen::new(store).showmode(modestr);
|
||||
let screen = Screen::new(store).showmode(modestr).borders(true);
|
||||
f.render_stateful_widget(screen, area, sstate);
|
||||
|
||||
if let Some((cx, cy)) = sstate.get_term_cursor() {
|
||||
|
@ -457,11 +457,10 @@ async fn main() -> IambResult<()> {
|
|||
|
||||
// Set up the tracing subscriber so we can log client messages.
|
||||
let log_prefix = format!("iamb-log-{}", settings.profile_name);
|
||||
let mut log_dir = settings.dirs.cache.clone();
|
||||
log_dir.push("logs");
|
||||
let log_dir = settings.dirs.logs.as_path();
|
||||
|
||||
create_dir_all(settings.matrix_dir.as_path())?;
|
||||
create_dir_all(log_dir.as_path())?;
|
||||
create_dir_all(log_dir)?;
|
||||
|
||||
let appender = tracing_appender::rolling::daily(log_dir, log_prefix);
|
||||
let (appender, _) = tracing_appender::non_blocking(appender);
|
||||
|
|
10
src/tests.rs
10
src/tests.rs
|
@ -110,6 +110,14 @@ pub fn mock_room() -> RoomInfo {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn mock_dirs() -> DirectoryValues {
|
||||
DirectoryValues {
|
||||
cache: PathBuf::new(),
|
||||
logs: PathBuf::new(),
|
||||
downloads: PathBuf::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mock_settings() -> ApplicationSettings {
|
||||
ApplicationSettings {
|
||||
matrix_dir: PathBuf::new(),
|
||||
|
@ -122,7 +130,7 @@ pub fn mock_settings() -> ApplicationSettings {
|
|||
dirs: None,
|
||||
},
|
||||
tunables: TunableValues { typing_notice: true, typing_notice_display: true },
|
||||
dirs: DirectoryValues { cache: PathBuf::new() },
|
||||
dirs: mock_dirs(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ use modalkit::tui::{
|
|||
layout::{Alignment, Rect},
|
||||
style::{Modifier as StyleModifier, Style},
|
||||
text::{Span, Spans, Text},
|
||||
widgets::{Block, Borders, StatefulWidget, Widget},
|
||||
widgets::StatefulWidget,
|
||||
};
|
||||
|
||||
use modalkit::{
|
||||
|
@ -71,6 +71,21 @@ use self::{room::RoomState, welcome::WelcomeState};
|
|||
pub mod room;
|
||||
pub mod welcome;
|
||||
|
||||
#[inline]
|
||||
fn bold_style() -> Style {
|
||||
Style::default().add_modifier(StyleModifier::BOLD)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn bold_span(s: &str) -> Span {
|
||||
Span::styled(s, bold_style())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn bold_spans(s: &str) -> Spans {
|
||||
bold_span(s).into()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn selected_style(selected: bool) -> Style {
|
||||
if selected {
|
||||
|
@ -168,22 +183,6 @@ impl IambWindow {
|
|||
return Err(err);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_title(&self, store: &mut ProgramStore) -> String {
|
||||
match self {
|
||||
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<DirectItem, IambInfo>;
|
||||
|
@ -281,13 +280,8 @@ impl TerminalCursor for IambWindow {
|
|||
|
||||
impl WindowOps<IambInfo> for IambWindow {
|
||||
fn draw(&mut self, area: Rect, buf: &mut Buffer, focused: bool, store: &mut ProgramStore) {
|
||||
let title = self.get_title(store);
|
||||
let block = Block::default().title(title.as_str()).borders(Borders::ALL);
|
||||
let inner = block.inner(area);
|
||||
block.render(area, buf);
|
||||
|
||||
match self {
|
||||
IambWindow::Room(state) => state.draw(inner, buf, focused, store),
|
||||
IambWindow::Room(state) => state.draw(area, buf, focused, store),
|
||||
IambWindow::DirectList(state) => {
|
||||
let dms = store.application.worker.direct_messages();
|
||||
let items = dms.into_iter().map(|(id, name)| DirectItem::new(id, name, store));
|
||||
|
@ -297,7 +291,7 @@ impl WindowOps<IambInfo> for IambWindow {
|
|||
.empty_message("No direct messages yet!")
|
||||
.empty_alignment(Alignment::Center)
|
||||
.focus(focused)
|
||||
.render(inner, buf, state);
|
||||
.render(area, buf, state);
|
||||
},
|
||||
IambWindow::MemberList(state, room_id) => {
|
||||
if let Ok(mems) = store.application.worker.members(room_id.clone()) {
|
||||
|
@ -309,7 +303,7 @@ impl WindowOps<IambInfo> for IambWindow {
|
|||
.empty_message("No users here yet!")
|
||||
.empty_alignment(Alignment::Center)
|
||||
.focus(focused)
|
||||
.render(inner, buf, state);
|
||||
.render(area, buf, state);
|
||||
},
|
||||
IambWindow::RoomList(state) => {
|
||||
let joined = store.application.worker.joined_rooms();
|
||||
|
@ -320,20 +314,20 @@ impl WindowOps<IambInfo> for IambWindow {
|
|||
.empty_message("You haven't joined any rooms yet")
|
||||
.empty_alignment(Alignment::Center)
|
||||
.focus(focused)
|
||||
.render(inner, buf, state);
|
||||
.render(area, buf, state);
|
||||
},
|
||||
IambWindow::SpaceList(state) => {
|
||||
let spaces = store.application.worker.spaces();
|
||||
let items =
|
||||
spaces.into_iter().map(|(room, name)| SpaceItem::new(room, name, store));
|
||||
state.set(items.collect());
|
||||
state.draw(inner, buf, focused, store);
|
||||
state.draw(area, 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);
|
||||
.render(area, buf, state);
|
||||
},
|
||||
IambWindow::VerifyList(state) => {
|
||||
let verifications = &store.application.verifications;
|
||||
|
@ -348,9 +342,9 @@ impl WindowOps<IambInfo> for IambWindow {
|
|||
.empty_message("No in-progress verifications")
|
||||
.empty_alignment(Alignment::Center)
|
||||
.focus(focused)
|
||||
.render(inner, buf, state);
|
||||
.render(area, buf, state);
|
||||
},
|
||||
IambWindow::Welcome(state) => state.draw(inner, buf, focused, store),
|
||||
IambWindow::Welcome(state) => state.draw(area, buf, focused, store),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -394,6 +388,44 @@ impl Window<IambInfo> for IambWindow {
|
|||
}
|
||||
}
|
||||
|
||||
fn get_tab_title(&self, store: &mut ProgramStore) -> Spans {
|
||||
match self {
|
||||
IambWindow::DirectList(_) => bold_spans("Direct Messages"),
|
||||
IambWindow::RoomList(_) => bold_spans("Rooms"),
|
||||
IambWindow::SpaceList(_) => bold_spans("Spaces"),
|
||||
IambWindow::VerifyList(_) => bold_spans("Verifications"),
|
||||
IambWindow::Welcome(_) => bold_spans("Welcome to iamb"),
|
||||
|
||||
IambWindow::Room(w) => {
|
||||
let title = store.application.get_room_title(w.id());
|
||||
|
||||
Spans::from(title)
|
||||
},
|
||||
IambWindow::MemberList(_, room_id) => {
|
||||
let title = store.application.get_room_title(room_id.as_ref());
|
||||
|
||||
Spans(vec![bold_span("Room Members: "), title.into()])
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn get_win_title(&self, store: &mut ProgramStore) -> Spans {
|
||||
match self {
|
||||
IambWindow::DirectList(_) => bold_spans("Direct Messages"),
|
||||
IambWindow::RoomList(_) => bold_spans("Rooms"),
|
||||
IambWindow::SpaceList(_) => bold_spans("Spaces"),
|
||||
IambWindow::VerifyList(_) => bold_spans("Verifications"),
|
||||
IambWindow::Welcome(_) => bold_spans("Welcome to iamb"),
|
||||
|
||||
IambWindow::Room(w) => w.get_title(store),
|
||||
IambWindow::MemberList(_, room_id) => {
|
||||
let title = store.application.get_room_title(room_id.as_ref());
|
||||
|
||||
Spans(vec![bold_span("Room Members: "), title.into()])
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn open(id: IambId, store: &mut ProgramStore) -> IambResult<Self> {
|
||||
match id {
|
||||
IambId::Room(room_id) => {
|
||||
|
@ -464,6 +496,10 @@ impl Window<IambInfo> for IambWindow {
|
|||
|
||||
Err(err)
|
||||
}
|
||||
|
||||
fn unnamed(store: &mut ProgramStore) -> IambResult<Self> {
|
||||
Self::open(IambId::RoomList, store)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
|
|
|
@ -44,6 +44,7 @@ use super::scrollback::{Scrollback, ScrollbackState};
|
|||
|
||||
pub struct ChatState {
|
||||
room_id: OwnedRoomId,
|
||||
room: MatrixRoom,
|
||||
|
||||
tbox: TextBoxState<IambInfo>,
|
||||
sent: HistoryList<EditRope>,
|
||||
|
@ -63,6 +64,7 @@ impl ChatState {
|
|||
|
||||
ChatState {
|
||||
room_id,
|
||||
room,
|
||||
|
||||
tbox,
|
||||
sent: HistoryList::new(EditRope::from(""), 100),
|
||||
|
@ -80,6 +82,10 @@ impl ChatState {
|
|||
};
|
||||
}
|
||||
|
||||
pub fn room(&self) -> &MatrixRoom {
|
||||
&self.room
|
||||
}
|
||||
|
||||
pub fn id(&self) -> &RoomId {
|
||||
&self.room_id
|
||||
}
|
||||
|
@ -133,6 +139,7 @@ impl WindowOps<IambInfo> for ChatState {
|
|||
|
||||
ChatState {
|
||||
room_id: self.room_id.clone(),
|
||||
room: self.room.clone(),
|
||||
|
||||
tbox,
|
||||
sent: self.sent.clone(),
|
||||
|
|
|
@ -2,7 +2,13 @@ use matrix_sdk::room::Room as MatrixRoom;
|
|||
use matrix_sdk::ruma::RoomId;
|
||||
use matrix_sdk::DisplayName;
|
||||
|
||||
use modalkit::tui::{buffer::Buffer, layout::Rect, widgets::StatefulWidget};
|
||||
use modalkit::tui::{
|
||||
buffer::Buffer,
|
||||
layout::Rect,
|
||||
style::{Modifier as StyleModifier, Style},
|
||||
text::{Span, Spans},
|
||||
widgets::StatefulWidget,
|
||||
};
|
||||
|
||||
use modalkit::{
|
||||
editing::action::{
|
||||
|
@ -90,7 +96,7 @@ impl RoomState {
|
|||
&mut self,
|
||||
act: RoomAction,
|
||||
_: ProgramContext,
|
||||
_: &mut ProgramStore,
|
||||
store: &mut ProgramStore,
|
||||
) -> IambResult<Vec<(Action<IambInfo>, ProgramContext)>> {
|
||||
match act {
|
||||
RoomAction::Members(mut cmd) => {
|
||||
|
@ -103,11 +109,29 @@ impl RoomState {
|
|||
|
||||
Ok(vec![(act, cmd.context.take())])
|
||||
},
|
||||
RoomAction::Set(field) => {
|
||||
store.application.worker.set_room(self.id().to_owned(), field)?;
|
||||
|
||||
Ok(vec![])
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_title(&self, store: &mut ProgramStore) -> String {
|
||||
store.application.get_room_title(self.id())
|
||||
pub fn get_title(&self, store: &mut ProgramStore) -> Spans {
|
||||
let title = store.application.get_room_title(self.id());
|
||||
let style = Style::default().add_modifier(StyleModifier::BOLD);
|
||||
let mut spans = vec![Span::styled(title, style)];
|
||||
|
||||
match self.room().topic() {
|
||||
Some(desc) if !desc.is_empty() => {
|
||||
spans.push(" (".into());
|
||||
spans.push(desc.into());
|
||||
spans.push(")".into());
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
|
||||
Spans(spans)
|
||||
}
|
||||
|
||||
pub fn focus_toggle(&mut self) {
|
||||
|
@ -117,6 +141,13 @@ impl RoomState {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn room(&self) -> &MatrixRoom {
|
||||
match self {
|
||||
RoomState::Chat(chat) => chat.room(),
|
||||
RoomState::Space(space) => space.room(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn id(&self) -> &RoomId {
|
||||
match self {
|
||||
RoomState::Chat(chat) => chat.id(),
|
||||
|
|
|
@ -18,6 +18,7 @@ use crate::windows::RoomItem;
|
|||
|
||||
pub struct SpaceState {
|
||||
room_id: OwnedRoomId,
|
||||
room: MatrixRoom,
|
||||
list: ListState<RoomItem, IambInfo>,
|
||||
}
|
||||
|
||||
|
@ -27,7 +28,11 @@ impl SpaceState {
|
|||
let content = IambBufferId::Room(room_id.clone(), RoomFocus::Scrollback);
|
||||
let list = ListState::new(content, vec![]);
|
||||
|
||||
SpaceState { room_id, list }
|
||||
SpaceState { room_id, room, list }
|
||||
}
|
||||
|
||||
pub fn room(&self) -> &MatrixRoom {
|
||||
&self.room
|
||||
}
|
||||
|
||||
pub fn id(&self) -> &RoomId {
|
||||
|
@ -37,6 +42,7 @@ impl SpaceState {
|
|||
pub fn dup(&self, store: &mut ProgramStore) -> Self {
|
||||
SpaceState {
|
||||
room_id: self.room_id.clone(),
|
||||
room: self.room.clone(),
|
||||
list: self.list.dup(store),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,8 +30,11 @@ use matrix_sdk::{
|
|||
start::{OriginalSyncKeyVerificationStartEvent, ToDeviceKeyVerificationStartEvent},
|
||||
VerificationMethod,
|
||||
},
|
||||
room::message::{MessageType, RoomMessageEventContent, TextMessageEventContent},
|
||||
room::name::RoomNameEventContent,
|
||||
room::{
|
||||
message::{MessageType, RoomMessageEventContent, TextMessageEventContent},
|
||||
name::RoomNameEventContent,
|
||||
topic::RoomTopicEventContent,
|
||||
},
|
||||
typing::SyncTypingEvent,
|
||||
AnyMessageLikeEvent,
|
||||
AnyTimelineEvent,
|
||||
|
@ -51,7 +54,7 @@ use matrix_sdk::{
|
|||
use modalkit::editing::action::{EditInfo, InfoMessage, UIError};
|
||||
|
||||
use crate::{
|
||||
base::{AsyncProgramStore, IambError, IambResult, VerifyAction},
|
||||
base::{AsyncProgramStore, IambError, IambResult, SetRoomField, VerifyAction},
|
||||
message::{Message, MessageFetchResult, MessageTimeStamp},
|
||||
ApplicationSettings,
|
||||
};
|
||||
|
@ -106,6 +109,7 @@ pub enum WorkerTask {
|
|||
SpaceMembers(OwnedRoomId, ClientReply<IambResult<Vec<OwnedRoomId>>>),
|
||||
Spaces(ClientReply<Vec<(MatrixRoom, DisplayName)>>),
|
||||
SendMessage(OwnedRoomId, String, ClientReply<IambResult<EchoPair>>),
|
||||
SetRoom(OwnedRoomId, SetRoomField, ClientReply<IambResult<()>>),
|
||||
TypingNotice(OwnedRoomId),
|
||||
Verify(VerifyAction, SasVerification, ClientReply<IambResult<EditInfo>>),
|
||||
VerifyRequest(OwnedUserId, ClientReply<IambResult<EditInfo>>),
|
||||
|
@ -204,6 +208,14 @@ impl Requester {
|
|||
return response.recv();
|
||||
}
|
||||
|
||||
pub fn set_room(&self, room_id: OwnedRoomId, ev: SetRoomField) -> IambResult<()> {
|
||||
let (reply, response) = oneshot();
|
||||
|
||||
self.tx.send(WorkerTask::SetRoom(room_id, ev, reply)).unwrap();
|
||||
|
||||
return response.recv();
|
||||
}
|
||||
|
||||
pub fn spaces(&self) -> Vec<(MatrixRoom, DisplayName)> {
|
||||
let (reply, response) = oneshot();
|
||||
|
||||
|
@ -340,6 +352,10 @@ impl ClientWorker {
|
|||
assert!(self.initialized);
|
||||
reply.send(self.members(room_id).await);
|
||||
},
|
||||
WorkerTask::SetRoom(room_id, field, reply) => {
|
||||
assert!(self.initialized);
|
||||
reply.send(self.set_room(room_id, field).await);
|
||||
},
|
||||
WorkerTask::SpaceMembers(space, reply) => {
|
||||
assert!(self.initialized);
|
||||
reply.send(self.space_members(space).await);
|
||||
|
@ -765,6 +781,27 @@ impl ClientWorker {
|
|||
}
|
||||
}
|
||||
|
||||
async fn set_room(&mut self, room_id: OwnedRoomId, field: SetRoomField) -> IambResult<()> {
|
||||
let room = if let Some(r) = self.client.get_joined_room(&room_id) {
|
||||
r
|
||||
} else {
|
||||
return Err(IambError::UnknownRoom(room_id).into());
|
||||
};
|
||||
|
||||
match field {
|
||||
SetRoomField::Name(name) => {
|
||||
let ev = RoomNameEventContent::new(name.into());
|
||||
let _ = room.send_state_event(ev).await.map_err(IambError::from)?;
|
||||
},
|
||||
SetRoomField::Topic(topic) => {
|
||||
let ev = RoomTopicEventContent::new(topic);
|
||||
let _ = room.send_state_event(ev).await.map_err(IambError::from)?;
|
||||
},
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn space_members(&mut self, space: OwnedRoomId) -> IambResult<Vec<OwnedRoomId>> {
|
||||
let mut req = SpaceHierarchyRequest::new(&space);
|
||||
req.limit = Some(1000u32.into());
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue