mirror of
https://github.com/youwen5/iamb.git
synced 2025-06-19 21:29:52 -07:00
Support displaying and editing room tags (#15)
This commit is contained in:
parent
4f2261e66f
commit
a6888bbc93
8 changed files with 443 additions and 109 deletions
|
@ -49,7 +49,7 @@ two other TUI clients and Element Web:
|
|||
| --------------------------------------- | :---------- | :------: | :--------------: | :-----------------: |
|
||||
| Room directory | ❌ ([#14]) | ❌ | ✔️ | ✔️ |
|
||||
| Room tag showing | ❌ ([#15]) | ✔️ | ❌ | ✔️ |
|
||||
| Room tag editing | ❌ ([#15]) | ✔️ | ❌ | ✔️ |
|
||||
| Room tag editing | ✔️ | ✔️ | ❌ | ✔️ |
|
||||
| Search joined rooms | ❌ ([#16]) | ✔️ | ❌ | ✔️ |
|
||||
| Room user list | ✔️ | ✔️ | ✔️ | ✔️ |
|
||||
| Display Room Description | ✔️ | ✔️ | ✔️ | ✔️ |
|
||||
|
|
29
src/base.rs
29
src/base.rs
|
@ -8,6 +8,7 @@ use tracing::warn;
|
|||
|
||||
use matrix_sdk::{
|
||||
encryption::verification::SasVerification,
|
||||
room::Joined,
|
||||
ruma::{
|
||||
events::room::message::{
|
||||
OriginalRoomMessageEvent,
|
||||
|
@ -16,6 +17,7 @@ use matrix_sdk::{
|
|||
RoomMessageEvent,
|
||||
RoomMessageEventContent,
|
||||
},
|
||||
events::tag::{TagName, Tags},
|
||||
EventId,
|
||||
OwnedEventId,
|
||||
OwnedRoomId,
|
||||
|
@ -94,9 +96,10 @@ pub enum MessageAction {
|
|||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub enum SetRoomField {
|
||||
Name(String),
|
||||
Topic(String),
|
||||
pub enum RoomField {
|
||||
Name,
|
||||
Tag(TagName),
|
||||
Topic,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
|
@ -105,13 +108,8 @@ pub enum RoomAction {
|
|||
InviteReject,
|
||||
InviteSend(OwnedUserId),
|
||||
Members(Box<CommandContext<ProgramContext>>),
|
||||
Set(SetRoomField),
|
||||
}
|
||||
|
||||
impl From<SetRoomField> for RoomAction {
|
||||
fn from(act: SetRoomField) -> Self {
|
||||
RoomAction::Set(act)
|
||||
}
|
||||
Set(RoomField, String),
|
||||
Unset(RoomField),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
|
@ -194,6 +192,12 @@ impl ApplicationAction for IambAction {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<RoomAction> for ProgramAction {
|
||||
fn from(act: RoomAction) -> Self {
|
||||
IambAction::from(act).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<IambAction> for ProgramAction {
|
||||
fn from(act: IambAction) -> Self {
|
||||
Action::Application(act)
|
||||
|
@ -277,6 +281,7 @@ pub enum RoomFetchStatus {
|
|||
#[derive(Default)]
|
||||
pub struct RoomInfo {
|
||||
pub name: Option<String>,
|
||||
pub tags: Option<Tags>,
|
||||
|
||||
pub keys: HashMap<OwnedEventId, MessageKey>,
|
||||
pub messages: Messages,
|
||||
|
@ -437,6 +442,10 @@ impl ChatStore {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn get_joined_room(&self, room_id: &RoomId) -> Option<Joined> {
|
||||
self.worker.client.get_joined_room(room_id)
|
||||
}
|
||||
|
||||
pub fn get_room_title(&self, room_id: &RoomId) -> String {
|
||||
self.rooms
|
||||
.get(room_id)
|
||||
|
|
282
src/commands.rs
282
src/commands.rs
|
@ -1,6 +1,6 @@
|
|||
use std::convert::TryFrom;
|
||||
|
||||
use matrix_sdk::ruma::OwnedUserId;
|
||||
use matrix_sdk::ruma::{events::tag::TagName, OwnedUserId};
|
||||
|
||||
use modalkit::{
|
||||
editing::base::OpenTarget,
|
||||
|
@ -17,14 +17,38 @@ use crate::base::{
|
|||
ProgramCommands,
|
||||
ProgramContext,
|
||||
RoomAction,
|
||||
RoomField,
|
||||
SendAction,
|
||||
SetRoomField,
|
||||
VerifyAction,
|
||||
};
|
||||
|
||||
type ProgContext = CommandContext<ProgramContext>;
|
||||
type ProgResult = CommandResult<ProgramCommand>;
|
||||
|
||||
/// Convert strings the user types into a tag name.
|
||||
fn tag_name(name: String) -> Result<TagName, CommandError> {
|
||||
let tag = match name.as_str() {
|
||||
"fav" | "favorite" | "favourite" | "m.favourite" => TagName::Favorite,
|
||||
"low" | "lowpriority" | "low_priority" | "low-priority" | "m.lowpriority" => {
|
||||
TagName::LowPriority
|
||||
},
|
||||
"servernotice" | "server_notice" | "server-notice" | "m.server_notice" => {
|
||||
TagName::ServerNotice
|
||||
},
|
||||
_ => {
|
||||
if let Ok(tag) = name.parse() {
|
||||
TagName::User(tag)
|
||||
} else {
|
||||
let msg = format!("Invalid user tag name: {}", name);
|
||||
|
||||
return Err(CommandError::Error(msg));
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
Ok(tag)
|
||||
}
|
||||
|
||||
fn iamb_invite(desc: CommandDescription, ctx: &mut ProgContext) -> ProgResult {
|
||||
let args = desc.arg.strings()?;
|
||||
|
||||
|
@ -225,22 +249,46 @@ fn iamb_join(desc: CommandDescription, ctx: &mut ProgContext) -> ProgResult {
|
|||
return Ok(step);
|
||||
}
|
||||
|
||||
fn iamb_set(desc: CommandDescription, ctx: &mut ProgContext) -> ProgResult {
|
||||
fn iamb_room(desc: CommandDescription, ctx: &mut ProgContext) -> ProgResult {
|
||||
let mut args = desc.arg.strings()?;
|
||||
|
||||
if args.len() != 2 {
|
||||
if args.len() < 2 {
|
||||
return Result::Err(CommandError::InvalidArgument);
|
||||
}
|
||||
|
||||
let field = args.remove(0);
|
||||
let value = args.remove(0);
|
||||
let action = 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);
|
||||
},
|
||||
if args.len() > 1 {
|
||||
return Result::Err(CommandError::InvalidArgument);
|
||||
}
|
||||
|
||||
let act: IambAction = match (field.as_str(), action.as_str(), args.pop()) {
|
||||
// :room name set <room-name>
|
||||
("name", "set", Some(s)) => RoomAction::Set(RoomField::Name, s).into(),
|
||||
("name", "set", None) => return Result::Err(CommandError::InvalidArgument),
|
||||
|
||||
// :room name unset
|
||||
("name", "unset", None) => RoomAction::Unset(RoomField::Name).into(),
|
||||
("name", "unset", Some(_)) => return Result::Err(CommandError::InvalidArgument),
|
||||
|
||||
// :room topic set <topic>
|
||||
("topic", "set", Some(s)) => RoomAction::Set(RoomField::Topic, s).into(),
|
||||
("topic", "set", None) => return Result::Err(CommandError::InvalidArgument),
|
||||
|
||||
// :room topic unset
|
||||
("topic", "unset", None) => RoomAction::Unset(RoomField::Topic).into(),
|
||||
("topic", "unset", Some(_)) => return Result::Err(CommandError::InvalidArgument),
|
||||
|
||||
// :room tag set <tag-name>
|
||||
("tag", "set", Some(s)) => RoomAction::Set(RoomField::Tag(tag_name(s)?), "".into()).into(),
|
||||
("tag", "set", None) => return Result::Err(CommandError::InvalidArgument),
|
||||
|
||||
// :room tag unset <tag-name>
|
||||
("tag", "unset", Some(s)) => RoomAction::Unset(RoomField::Tag(tag_name(s)?)).into(),
|
||||
("tag", "unset", None) => return Result::Err(CommandError::InvalidArgument),
|
||||
|
||||
_ => return Result::Err(CommandError::InvalidArgument),
|
||||
};
|
||||
|
||||
let step = CommandStep::Continue(act.into(), ctx.context.take());
|
||||
|
@ -287,7 +335,7 @@ fn add_iamb_commands(cmds: &mut ProgramCommands) {
|
|||
cmds.add_command(ProgramCommand { names: vec!["redact".into()], f: iamb_redact });
|
||||
cmds.add_command(ProgramCommand { names: vec!["reply".into()], f: iamb_reply });
|
||||
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!["room".into()], f: iamb_room });
|
||||
cmds.add_command(ProgramCommand { names: vec!["spaces".into()], f: iamb_spaces });
|
||||
cmds.add_command(ProgramCommand { names: vec!["upload".into()], f: iamb_upload });
|
||||
cmds.add_command(ProgramCommand { names: vec!["verify".into()], f: iamb_verify });
|
||||
|
@ -376,47 +424,227 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn test_cmd_set() {
|
||||
fn test_cmd_room_invalid() {
|
||||
let mut cmds = setup_commands();
|
||||
let ctx = ProgramContext::default();
|
||||
|
||||
let res = cmds.input_cmd("room", ctx.clone());
|
||||
assert_eq!(res, Err(CommandError::InvalidArgument));
|
||||
|
||||
let res = cmds.input_cmd("room foo", ctx.clone());
|
||||
assert_eq!(res, Err(CommandError::InvalidArgument));
|
||||
|
||||
let res = cmds.input_cmd("room set topic", ctx.clone());
|
||||
assert_eq!(res, Err(CommandError::InvalidArgument));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cmd_room_topic_set() {
|
||||
let mut cmds = setup_commands();
|
||||
let ctx = ProgramContext::default();
|
||||
|
||||
let res = cmds
|
||||
.input_cmd("set room.topic \"Lots of fun discussion!\"", ctx.clone())
|
||||
.input_cmd("room topic set \"Lots of fun discussion!\"", ctx.clone())
|
||||
.unwrap();
|
||||
let act = IambAction::Room(SetRoomField::Topic("Lots of fun discussion!".into()).into());
|
||||
let act = RoomAction::Set(RoomField::Topic, "Lots of fun discussion!".into());
|
||||
assert_eq!(res, vec![(act.into(), ctx.clone())]);
|
||||
|
||||
let res = cmds
|
||||
.input_cmd("set room.topic The\\ Discussion\\ Room", ctx.clone())
|
||||
.input_cmd("room topic set The\\ Discussion\\ Room", ctx.clone())
|
||||
.unwrap();
|
||||
let act = IambAction::Room(SetRoomField::Topic("The Discussion Room".into()).into());
|
||||
let act = RoomAction::Set(RoomField::Topic, "The Discussion Room".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());
|
||||
let res = cmds.input_cmd("room topic set Development", ctx.clone()).unwrap();
|
||||
let act = RoomAction::Set(RoomField::Topic, "Development".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());
|
||||
let res = cmds.input_cmd("room topic", ctx.clone());
|
||||
assert_eq!(res, Err(CommandError::InvalidArgument));
|
||||
|
||||
let res = cmds.input_cmd("room topic set", ctx.clone());
|
||||
assert_eq!(res, Err(CommandError::InvalidArgument));
|
||||
|
||||
let res = cmds.input_cmd("room topic set A B C", ctx.clone());
|
||||
assert_eq!(res, Err(CommandError::InvalidArgument));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cmd_room_name_invalid() {
|
||||
let mut cmds = setup_commands();
|
||||
let ctx = ProgramContext::default();
|
||||
|
||||
let res = cmds.input_cmd("room name", ctx.clone());
|
||||
assert_eq!(res, Err(CommandError::InvalidArgument));
|
||||
|
||||
let res = cmds.input_cmd("room name foo", ctx.clone());
|
||||
assert_eq!(res, Err(CommandError::InvalidArgument));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cmd_room_name_set() {
|
||||
let mut cmds = setup_commands();
|
||||
let ctx = ProgramContext::default();
|
||||
|
||||
let res = cmds.input_cmd("room name set Development", ctx.clone()).unwrap();
|
||||
let act = RoomAction::Set(RoomField::Name, "Development".into());
|
||||
assert_eq!(res, vec![(act.into(), ctx.clone())]);
|
||||
|
||||
let res = cmds
|
||||
.input_cmd("set room.name \"Application Development\"", ctx.clone())
|
||||
.input_cmd("room name set \"Application Development\"", ctx.clone())
|
||||
.unwrap();
|
||||
let act = IambAction::Room(SetRoomField::Name("Application Development".into()).into());
|
||||
let act = RoomAction::Set(RoomField::Name, "Application Development".into());
|
||||
assert_eq!(res, vec![(act.into(), ctx.clone())]);
|
||||
|
||||
let res = cmds.input_cmd("set", ctx.clone());
|
||||
let res = cmds.input_cmd("room name set", ctx.clone());
|
||||
assert_eq!(res, Err(CommandError::InvalidArgument));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cmd_room_name_unset() {
|
||||
let mut cmds = setup_commands();
|
||||
let ctx = ProgramContext::default();
|
||||
|
||||
let res = cmds.input_cmd("room name unset", ctx.clone()).unwrap();
|
||||
let act = RoomAction::Unset(RoomField::Name);
|
||||
assert_eq!(res, vec![(act.into(), ctx.clone())]);
|
||||
|
||||
let res = cmds.input_cmd("room name unset foo", ctx.clone());
|
||||
assert_eq!(res, Err(CommandError::InvalidArgument));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cmd_room_tag_set() {
|
||||
let mut cmds = setup_commands();
|
||||
let ctx = ProgramContext::default();
|
||||
|
||||
let res = cmds.input_cmd("room tag set favourite", ctx.clone()).unwrap();
|
||||
let act = RoomAction::Set(RoomField::Tag(TagName::Favorite), "".into());
|
||||
assert_eq!(res, vec![(act.into(), ctx.clone())]);
|
||||
|
||||
let res = cmds.input_cmd("room tag set favorite", ctx.clone()).unwrap();
|
||||
let act = RoomAction::Set(RoomField::Tag(TagName::Favorite), "".into());
|
||||
assert_eq!(res, vec![(act.into(), ctx.clone())]);
|
||||
|
||||
let res = cmds.input_cmd("room tag set fav", ctx.clone()).unwrap();
|
||||
let act = RoomAction::Set(RoomField::Tag(TagName::Favorite), "".into());
|
||||
assert_eq!(res, vec![(act.into(), ctx.clone())]);
|
||||
|
||||
let res = cmds.input_cmd("room tag set low_priority", ctx.clone()).unwrap();
|
||||
let act = RoomAction::Set(RoomField::Tag(TagName::LowPriority), "".into());
|
||||
assert_eq!(res, vec![(act.into(), ctx.clone())]);
|
||||
|
||||
let res = cmds.input_cmd("room tag set low-priority", ctx.clone()).unwrap();
|
||||
let act = RoomAction::Set(RoomField::Tag(TagName::LowPriority), "".into());
|
||||
assert_eq!(res, vec![(act.into(), ctx.clone())]);
|
||||
|
||||
let res = cmds.input_cmd("room tag set low", ctx.clone()).unwrap();
|
||||
let act = RoomAction::Set(RoomField::Tag(TagName::LowPriority), "".into());
|
||||
assert_eq!(res, vec![(act.into(), ctx.clone())]);
|
||||
|
||||
let res = cmds.input_cmd("room tag set servernotice", ctx.clone()).unwrap();
|
||||
let act = RoomAction::Set(RoomField::Tag(TagName::ServerNotice), "".into());
|
||||
assert_eq!(res, vec![(act.into(), ctx.clone())]);
|
||||
|
||||
let res = cmds.input_cmd("room tag set server_notice", ctx.clone()).unwrap();
|
||||
let act = RoomAction::Set(RoomField::Tag(TagName::ServerNotice), "".into());
|
||||
assert_eq!(res, vec![(act.into(), ctx.clone())]);
|
||||
|
||||
let res = cmds.input_cmd("room tag set server_notice", ctx.clone()).unwrap();
|
||||
let act = RoomAction::Set(RoomField::Tag(TagName::ServerNotice), "".into());
|
||||
assert_eq!(res, vec![(act.into(), ctx.clone())]);
|
||||
|
||||
let res = cmds.input_cmd("room tag set u.custom-tag", ctx.clone()).unwrap();
|
||||
let act = RoomAction::Set(
|
||||
RoomField::Tag(TagName::User("u.custom-tag".parse().unwrap())),
|
||||
"".into(),
|
||||
);
|
||||
assert_eq!(res, vec![(act.into(), ctx.clone())]);
|
||||
|
||||
let res = cmds.input_cmd("room tag set u.irc", ctx.clone()).unwrap();
|
||||
let act =
|
||||
RoomAction::Set(RoomField::Tag(TagName::User("u.irc".parse().unwrap())), "".into());
|
||||
assert_eq!(res, vec![(act.into(), ctx.clone())]);
|
||||
|
||||
let res = cmds.input_cmd("room tag", ctx.clone());
|
||||
assert_eq!(res, Err(CommandError::InvalidArgument));
|
||||
|
||||
let res = cmds.input_cmd("set room.name", ctx.clone());
|
||||
let res = cmds.input_cmd("room tag set", ctx.clone());
|
||||
assert_eq!(res, Err(CommandError::InvalidArgument));
|
||||
|
||||
let res = cmds.input_cmd("set room.topic", ctx.clone());
|
||||
let res = cmds.input_cmd("room tag set unknown", ctx.clone());
|
||||
assert_eq!(res, Err(CommandError::Error("Invalid user tag name: unknown".into())));
|
||||
|
||||
let res = cmds.input_cmd("room tag set needs-leading-u-dot", ctx.clone());
|
||||
assert_eq!(
|
||||
res,
|
||||
Err(CommandError::Error("Invalid user tag name: needs-leading-u-dot".into()))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cmd_room_tag_unset() {
|
||||
let mut cmds = setup_commands();
|
||||
let ctx = ProgramContext::default();
|
||||
|
||||
let res = cmds.input_cmd("room tag unset favourite", ctx.clone()).unwrap();
|
||||
let act = RoomAction::Unset(RoomField::Tag(TagName::Favorite));
|
||||
assert_eq!(res, vec![(act.into(), ctx.clone())]);
|
||||
|
||||
let res = cmds.input_cmd("room tag unset favorite", ctx.clone()).unwrap();
|
||||
let act = RoomAction::Unset(RoomField::Tag(TagName::Favorite));
|
||||
assert_eq!(res, vec![(act.into(), ctx.clone())]);
|
||||
|
||||
let res = cmds.input_cmd("room tag unset fav", ctx.clone()).unwrap();
|
||||
let act = RoomAction::Unset(RoomField::Tag(TagName::Favorite));
|
||||
assert_eq!(res, vec![(act.into(), ctx.clone())]);
|
||||
|
||||
let res = cmds.input_cmd("room tag unset low_priority", ctx.clone()).unwrap();
|
||||
let act = RoomAction::Unset(RoomField::Tag(TagName::LowPriority));
|
||||
assert_eq!(res, vec![(act.into(), ctx.clone())]);
|
||||
|
||||
let res = cmds.input_cmd("room tag unset low-priority", ctx.clone()).unwrap();
|
||||
let act = RoomAction::Unset(RoomField::Tag(TagName::LowPriority));
|
||||
assert_eq!(res, vec![(act.into(), ctx.clone())]);
|
||||
|
||||
let res = cmds.input_cmd("room tag unset low", ctx.clone()).unwrap();
|
||||
let act = RoomAction::Unset(RoomField::Tag(TagName::LowPriority));
|
||||
assert_eq!(res, vec![(act.into(), ctx.clone())]);
|
||||
|
||||
let res = cmds.input_cmd("room tag unset servernotice", ctx.clone()).unwrap();
|
||||
let act = RoomAction::Unset(RoomField::Tag(TagName::ServerNotice));
|
||||
assert_eq!(res, vec![(act.into(), ctx.clone())]);
|
||||
|
||||
let res = cmds.input_cmd("room tag unset server_notice", ctx.clone()).unwrap();
|
||||
let act = RoomAction::Unset(RoomField::Tag(TagName::ServerNotice));
|
||||
assert_eq!(res, vec![(act.into(), ctx.clone())]);
|
||||
|
||||
let res = cmds.input_cmd("room tag unset server_notice", ctx.clone()).unwrap();
|
||||
let act = RoomAction::Unset(RoomField::Tag(TagName::ServerNotice));
|
||||
assert_eq!(res, vec![(act.into(), ctx.clone())]);
|
||||
|
||||
let res = cmds.input_cmd("room tag unset u.custom-tag", ctx.clone()).unwrap();
|
||||
let act = RoomAction::Unset(RoomField::Tag(TagName::User("u.custom-tag".parse().unwrap())));
|
||||
assert_eq!(res, vec![(act.into(), ctx.clone())]);
|
||||
|
||||
let res = cmds.input_cmd("room tag unset u.irc", ctx.clone()).unwrap();
|
||||
let act = RoomAction::Unset(RoomField::Tag(TagName::User("u.irc".parse().unwrap())));
|
||||
assert_eq!(res, vec![(act.into(), ctx.clone())]);
|
||||
|
||||
let res = cmds.input_cmd("room tag", ctx.clone());
|
||||
assert_eq!(res, Err(CommandError::InvalidArgument));
|
||||
|
||||
let res = cmds.input_cmd("set room.topic A B C", ctx.clone());
|
||||
let res = cmds.input_cmd("room tag set", ctx.clone());
|
||||
assert_eq!(res, Err(CommandError::InvalidArgument));
|
||||
|
||||
let res = cmds.input_cmd("room tag unset unknown", ctx.clone());
|
||||
assert_eq!(res, Err(CommandError::Error("Invalid user tag name: unknown".into())));
|
||||
|
||||
let res = cmds.input_cmd("room tag unset needs-leading-u-dot", ctx.clone());
|
||||
assert_eq!(
|
||||
res,
|
||||
Err(CommandError::Error("Invalid user tag name: needs-leading-u-dot".into()))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -141,6 +141,7 @@ pub fn mock_room() -> RoomInfo {
|
|||
|
||||
keys: mock_keys(),
|
||||
messages: mock_messages(),
|
||||
tags: None,
|
||||
|
||||
fetch_id: RoomFetchStatus::NotStarted,
|
||||
fetch_last: None,
|
||||
|
|
|
@ -4,7 +4,12 @@ use std::collections::hash_map::Entry;
|
|||
use matrix_sdk::{
|
||||
encryption::verification::{format_emojis, SasVerification},
|
||||
room::{Room as MatrixRoom, RoomMember},
|
||||
ruma::{events::room::member::MembershipState, OwnedRoomId, RoomId},
|
||||
ruma::{
|
||||
events::room::member::MembershipState,
|
||||
events::tag::{TagName, Tags},
|
||||
OwnedRoomId,
|
||||
RoomId,
|
||||
},
|
||||
DisplayName,
|
||||
};
|
||||
|
||||
|
@ -346,7 +351,7 @@ impl WindowOps<IambInfo> for IambWindow {
|
|||
let joined = store.application.worker.active_rooms();
|
||||
let mut items = joined
|
||||
.into_iter()
|
||||
.map(|(room, name)| RoomItem::new(room, name, store))
|
||||
.map(|(room, name, tags)| RoomItem::new(room, name, tags, store))
|
||||
.collect::<Vec<_>>();
|
||||
items.sort();
|
||||
|
||||
|
@ -471,8 +476,8 @@ impl Window<IambInfo> for IambWindow {
|
|||
fn open(id: IambId, store: &mut ProgramStore) -> IambResult<Self> {
|
||||
match id {
|
||||
IambId::Room(room_id) => {
|
||||
let (room, name) = store.application.worker.get_room(room_id)?;
|
||||
let room = RoomState::new(room, name, store);
|
||||
let (room, name, tags) = store.application.worker.get_room(room_id)?;
|
||||
let room = RoomState::new(room, name, tags, store);
|
||||
|
||||
return Ok(room.into());
|
||||
},
|
||||
|
@ -519,8 +524,8 @@ impl Window<IambInfo> for IambWindow {
|
|||
let room_id = worker.join_room(v.key().to_string())?;
|
||||
v.insert(room_id.clone());
|
||||
|
||||
let (room, name) = store.application.worker.get_room(room_id)?;
|
||||
let room = RoomState::new(room, name, store);
|
||||
let (room, name, tags) = store.application.worker.get_room(room_id)?;
|
||||
let room = RoomState::new(room, name, tags, store);
|
||||
|
||||
Ok(room.into())
|
||||
},
|
||||
|
@ -547,16 +552,24 @@ impl Window<IambInfo> for IambWindow {
|
|||
#[derive(Clone)]
|
||||
pub struct RoomItem {
|
||||
room: MatrixRoom,
|
||||
tags: Option<Tags>,
|
||||
name: String,
|
||||
}
|
||||
|
||||
impl RoomItem {
|
||||
fn new(room: MatrixRoom, name: DisplayName, store: &mut ProgramStore) -> Self {
|
||||
fn new(
|
||||
room: MatrixRoom,
|
||||
name: DisplayName,
|
||||
tags: Option<Tags>,
|
||||
store: &mut ProgramStore,
|
||||
) -> Self {
|
||||
let name = name.to_string();
|
||||
|
||||
store.application.set_room_name(room.room_id(), name.as_str());
|
||||
let info = store.application.get_room_info(room.room_id().to_owned());
|
||||
info.name = name.clone().into();
|
||||
info.tags = tags.clone();
|
||||
|
||||
RoomItem { room, name }
|
||||
RoomItem { room, tags, name }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -570,7 +583,29 @@ impl Eq for RoomItem {}
|
|||
|
||||
impl Ord for RoomItem {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
room_cmp(&self.room, &other.room)
|
||||
let (fava, lowa) = self
|
||||
.tags
|
||||
.as_ref()
|
||||
.map(|tags| {
|
||||
(tags.contains_key(&TagName::Favorite), tags.contains_key(&TagName::LowPriority))
|
||||
})
|
||||
.unwrap_or((false, false));
|
||||
|
||||
let (favb, lowb) = other
|
||||
.tags
|
||||
.as_ref()
|
||||
.map(|tags| {
|
||||
(tags.contains_key(&TagName::Favorite), tags.contains_key(&TagName::LowPriority))
|
||||
})
|
||||
.unwrap_or((false, false));
|
||||
|
||||
// If self has Favorite and other doesn't, it should sort earlier in room list.
|
||||
let cmpf = favb.cmp(&fava);
|
||||
|
||||
// If self has LowPriority and other doesn't, it should sort later in room list.
|
||||
let cmpl = lowa.cmp(&lowb);
|
||||
|
||||
cmpl.then(cmpf).then_with(|| room_cmp(&self.room, &other.room))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -588,7 +623,39 @@ impl ToString for RoomItem {
|
|||
|
||||
impl ListItem<IambInfo> for RoomItem {
|
||||
fn show(&self, selected: bool, _: &ViewportContext<ListCursor>, _: &mut ProgramStore) -> Text {
|
||||
selected_text(self.name.as_str(), selected)
|
||||
if let Some(tags) = &self.tags {
|
||||
let style = selected_style(selected);
|
||||
let mut spans = vec![Span::styled(self.name.as_str(), style)];
|
||||
|
||||
if tags.is_empty() {
|
||||
return Text::from(Spans(spans));
|
||||
}
|
||||
|
||||
spans.push(Span::styled(" (", style));
|
||||
|
||||
for (i, tag) in tags.keys().enumerate() {
|
||||
if i > 0 {
|
||||
spans.push(Span::styled(", ", style));
|
||||
}
|
||||
|
||||
match tag {
|
||||
TagName::Favorite => spans.push(Span::styled("Favorite", style)),
|
||||
TagName::LowPriority => spans.push(Span::styled("Low Priority", style)),
|
||||
TagName::ServerNotice => spans.push(Span::styled("Server Notice", style)),
|
||||
TagName::User(tag) => {
|
||||
spans.push(Span::styled("User Tag: ", style));
|
||||
spans.push(Span::styled(tag.as_ref(), style));
|
||||
},
|
||||
tag => spans.push(Span::styled(format!("{:?}", tag), style)),
|
||||
}
|
||||
}
|
||||
|
||||
spans.push(Span::styled(")", style));
|
||||
|
||||
Text::from(Spans(spans))
|
||||
} else {
|
||||
selected_text(self.name.as_str(), selected)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_word(&self) -> Option<String> {
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
use matrix_sdk::{
|
||||
room::{Invited, Room as MatrixRoom},
|
||||
ruma::RoomId,
|
||||
ruma::{
|
||||
events::{
|
||||
room::{name::RoomNameEventContent, topic::RoomTopicEventContent},
|
||||
tag::{TagInfo, Tags},
|
||||
},
|
||||
RoomId,
|
||||
},
|
||||
DisplayName,
|
||||
};
|
||||
|
||||
|
@ -23,6 +29,7 @@ use modalkit::{
|
|||
PromptAction,
|
||||
Promptable,
|
||||
Scrollable,
|
||||
UIError,
|
||||
},
|
||||
editing::base::{
|
||||
Axis,
|
||||
|
@ -48,6 +55,7 @@ use crate::base::{
|
|||
ProgramContext,
|
||||
ProgramStore,
|
||||
RoomAction,
|
||||
RoomField,
|
||||
SendAction,
|
||||
};
|
||||
|
||||
|
@ -85,10 +93,16 @@ impl From<SpaceState> for RoomState {
|
|||
}
|
||||
|
||||
impl RoomState {
|
||||
pub fn new(room: MatrixRoom, name: DisplayName, store: &mut ProgramStore) -> Self {
|
||||
pub fn new(
|
||||
room: MatrixRoom,
|
||||
name: DisplayName,
|
||||
tags: Option<Tags>,
|
||||
store: &mut ProgramStore,
|
||||
) -> Self {
|
||||
let room_id = room.room_id().to_owned();
|
||||
let info = store.application.get_room_info(room_id);
|
||||
info.name = name.to_string().into();
|
||||
info.tags = tags;
|
||||
|
||||
if room.is_space() {
|
||||
SpaceState::new(room).into()
|
||||
|
@ -207,8 +221,50 @@ impl RoomState {
|
|||
|
||||
Ok(vec![(act, cmd.context.take())])
|
||||
},
|
||||
RoomAction::Set(field) => {
|
||||
store.application.worker.set_room(self.id().to_owned(), field)?;
|
||||
RoomAction::Set(field, value) => {
|
||||
let room = store
|
||||
.application
|
||||
.get_joined_room(self.id())
|
||||
.ok_or(UIError::Application(IambError::NotJoined))?;
|
||||
|
||||
match field {
|
||||
RoomField::Name => {
|
||||
let ev = RoomNameEventContent::new(value.into());
|
||||
let _ = room.send_state_event(ev).await.map_err(IambError::from)?;
|
||||
},
|
||||
RoomField::Tag(tag) => {
|
||||
let mut info = TagInfo::new();
|
||||
info.order = Some(1.0);
|
||||
|
||||
let _ = room.set_tag(tag, info).await.map_err(IambError::from)?;
|
||||
},
|
||||
RoomField::Topic => {
|
||||
let ev = RoomTopicEventContent::new(value);
|
||||
let _ = room.send_state_event(ev).await.map_err(IambError::from)?;
|
||||
},
|
||||
}
|
||||
|
||||
Ok(vec![])
|
||||
},
|
||||
RoomAction::Unset(field) => {
|
||||
let room = store
|
||||
.application
|
||||
.get_joined_room(self.id())
|
||||
.ok_or(UIError::Application(IambError::NotJoined))?;
|
||||
|
||||
match field {
|
||||
RoomField::Name => {
|
||||
let ev = RoomNameEventContent::new(None);
|
||||
let _ = room.send_state_event(ev).await.map_err(IambError::from)?;
|
||||
},
|
||||
RoomField::Tag(tag) => {
|
||||
let _ = room.remove_tag(tag).await.map_err(IambError::from)?;
|
||||
},
|
||||
RoomField::Topic => {
|
||||
let ev = RoomTopicEventContent::new("".into());
|
||||
let _ = room.send_state_event(ev).await.map_err(IambError::from)?;
|
||||
},
|
||||
}
|
||||
|
||||
Ok(vec![])
|
||||
},
|
||||
|
|
|
@ -104,10 +104,10 @@ impl<'a> StatefulWidget for Space<'a> {
|
|||
let items = members
|
||||
.into_iter()
|
||||
.filter_map(|id| {
|
||||
let (room, name) = self.store.application.worker.get_room(id.clone()).ok()?;
|
||||
let (room, name, tags) = self.store.application.worker.get_room(id.clone()).ok()?;
|
||||
|
||||
if id != state.room_id {
|
||||
Some(RoomItem::new(room, name, self.store))
|
||||
Some(RoomItem::new(room, name, tags, self.store))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
|
|
@ -36,8 +36,8 @@ use matrix_sdk::{
|
|||
message::{MessageType, RoomMessageEventContent},
|
||||
name::RoomNameEventContent,
|
||||
redaction::{OriginalSyncRoomRedactionEvent, SyncRoomRedactionEvent},
|
||||
topic::RoomTopicEventContent,
|
||||
},
|
||||
tag::Tags,
|
||||
typing::SyncTypingEvent,
|
||||
AnyMessageLikeEvent,
|
||||
AnyTimelineEvent,
|
||||
|
@ -57,7 +57,7 @@ use matrix_sdk::{
|
|||
use modalkit::editing::action::{EditInfo, InfoMessage, UIError};
|
||||
|
||||
use crate::{
|
||||
base::{AsyncProgramStore, IambError, IambResult, SetRoomField, VerifyAction},
|
||||
base::{AsyncProgramStore, IambError, IambResult, VerifyAction},
|
||||
message::MessageFetchResult,
|
||||
ApplicationSettings,
|
||||
};
|
||||
|
@ -100,18 +100,17 @@ fn oneshot<T>() -> (ClientReply<T>, ClientResponse<T>) {
|
|||
}
|
||||
|
||||
pub enum WorkerTask {
|
||||
ActiveRooms(ClientReply<Vec<(MatrixRoom, DisplayName)>>),
|
||||
ActiveRooms(ClientReply<Vec<(MatrixRoom, DisplayName, Option<Tags>)>>),
|
||||
DirectMessages(ClientReply<Vec<(MatrixRoom, DisplayName)>>),
|
||||
Init(AsyncProgramStore, ClientReply<()>),
|
||||
LoadOlder(OwnedRoomId, Option<String>, u32, ClientReply<MessageFetchResult>),
|
||||
Login(LoginStyle, ClientReply<IambResult<EditInfo>>),
|
||||
GetInviter(Invited, ClientReply<IambResult<Option<RoomMember>>>),
|
||||
GetRoom(OwnedRoomId, ClientReply<IambResult<(MatrixRoom, DisplayName)>>),
|
||||
GetRoom(OwnedRoomId, ClientReply<IambResult<(MatrixRoom, DisplayName, Option<Tags>)>>),
|
||||
JoinRoom(String, ClientReply<IambResult<OwnedRoomId>>),
|
||||
Members(OwnedRoomId, ClientReply<IambResult<Vec<RoomMember>>>),
|
||||
SpaceMembers(OwnedRoomId, ClientReply<IambResult<Vec<OwnedRoomId>>>),
|
||||
Spaces(ClientReply<Vec<(MatrixRoom, DisplayName)>>),
|
||||
SetRoom(OwnedRoomId, SetRoomField, ClientReply<IambResult<()>>),
|
||||
TypingNotice(OwnedRoomId),
|
||||
Verify(VerifyAction, SasVerification, ClientReply<IambResult<EditInfo>>),
|
||||
VerifyRequest(OwnedUserId, ClientReply<IambResult<EditInfo>>),
|
||||
|
@ -178,13 +177,6 @@ impl Debug for WorkerTask {
|
|||
WorkerTask::Spaces(_) => {
|
||||
f.debug_tuple("WorkerTask::Spaces").field(&format_args!("_")).finish()
|
||||
},
|
||||
WorkerTask::SetRoom(room_id, field, _) => {
|
||||
f.debug_tuple("WorkerTask::SetRoom")
|
||||
.field(room_id)
|
||||
.field(field)
|
||||
.field(&format_args!("_"))
|
||||
.finish()
|
||||
},
|
||||
WorkerTask::TypingNotice(room_id) => {
|
||||
f.debug_tuple("WorkerTask::TypingNotice").field(room_id).finish()
|
||||
},
|
||||
|
@ -259,7 +251,10 @@ impl Requester {
|
|||
return response.recv();
|
||||
}
|
||||
|
||||
pub fn get_room(&self, room_id: OwnedRoomId) -> IambResult<(MatrixRoom, DisplayName)> {
|
||||
pub fn get_room(
|
||||
&self,
|
||||
room_id: OwnedRoomId,
|
||||
) -> IambResult<(MatrixRoom, DisplayName, Option<Tags>)> {
|
||||
let (reply, response) = oneshot();
|
||||
|
||||
self.tx.send(WorkerTask::GetRoom(room_id, reply)).unwrap();
|
||||
|
@ -275,7 +270,7 @@ impl Requester {
|
|||
return response.recv();
|
||||
}
|
||||
|
||||
pub fn active_rooms(&self) -> Vec<(MatrixRoom, DisplayName)> {
|
||||
pub fn active_rooms(&self) -> Vec<(MatrixRoom, DisplayName, Option<Tags>)> {
|
||||
let (reply, response) = oneshot();
|
||||
|
||||
self.tx.send(WorkerTask::ActiveRooms(reply)).unwrap();
|
||||
|
@ -299,14 +294,6 @@ 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();
|
||||
|
||||
|
@ -444,10 +431,6 @@ 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);
|
||||
|
@ -721,10 +704,15 @@ impl ClientWorker {
|
|||
Ok(Some(InfoMessage::from("Successfully logged in!")))
|
||||
}
|
||||
|
||||
async fn direct_message(&mut self, user: OwnedUserId) -> IambResult<(MatrixRoom, DisplayName)> {
|
||||
async fn direct_message(
|
||||
&mut self,
|
||||
user: OwnedUserId,
|
||||
) -> IambResult<(MatrixRoom, DisplayName, Option<Tags>)> {
|
||||
for (room, name) in self.direct_messages().await {
|
||||
if room.get_member(user.as_ref()).await.map_err(IambError::from)?.is_some() {
|
||||
return Ok((room, name));
|
||||
let tags = room.tags().await.map_err(IambError::from)?;
|
||||
|
||||
return Ok((room, name, tags));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -758,11 +746,15 @@ impl ClientWorker {
|
|||
Ok(details.inviter)
|
||||
}
|
||||
|
||||
async fn get_room(&mut self, room_id: OwnedRoomId) -> IambResult<(MatrixRoom, DisplayName)> {
|
||||
async fn get_room(
|
||||
&mut self,
|
||||
room_id: OwnedRoomId,
|
||||
) -> IambResult<(MatrixRoom, DisplayName, Option<Tags>)> {
|
||||
if let Some(room) = self.client.get_room(&room_id) {
|
||||
let name = room.display_name().await.map_err(IambError::from)?;
|
||||
let tags = room.tags().await.map_err(IambError::from)?;
|
||||
|
||||
Ok((room, name))
|
||||
Ok((room, name, tags))
|
||||
} else {
|
||||
Err(IambError::UnknownRoom(room_id).into())
|
||||
}
|
||||
|
@ -817,7 +809,7 @@ impl ClientWorker {
|
|||
return rooms;
|
||||
}
|
||||
|
||||
async fn active_rooms(&self) -> Vec<(MatrixRoom, DisplayName)> {
|
||||
async fn active_rooms(&self) -> Vec<(MatrixRoom, DisplayName, Option<Tags>)> {
|
||||
let mut rooms = vec![];
|
||||
|
||||
for room in self.client.invited_rooms().into_iter() {
|
||||
|
@ -826,8 +818,9 @@ impl ClientWorker {
|
|||
}
|
||||
|
||||
let name = room.display_name().await.unwrap_or(DisplayName::Empty);
|
||||
let tags = room.tags().await.unwrap_or_default();
|
||||
|
||||
rooms.push((room.into(), name));
|
||||
rooms.push((room.into(), name, tags));
|
||||
}
|
||||
|
||||
for room in self.client.joined_rooms().into_iter() {
|
||||
|
@ -836,8 +829,9 @@ impl ClientWorker {
|
|||
}
|
||||
|
||||
let name = room.display_name().await.unwrap_or(DisplayName::Empty);
|
||||
let tags = room.tags().await.unwrap_or_default();
|
||||
|
||||
rooms.push((room.into(), name));
|
||||
rooms.push((room.into(), name, tags));
|
||||
}
|
||||
|
||||
return rooms;
|
||||
|
@ -886,27 +880,6 @@ 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