Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement set/unset/show for alternative and canonical aliases #279

Merged
merged 5 commits into from
Aug 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions docs/iamb.1
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,18 @@ Remove a tag from the currently focused room.
Set the topic of the currently focused room.
.It Sy ":room topic unset"
Unset the topic of the currently focused room.
.It Sy ":room alias set [alias]"
Create and point the given alias to the room.
.It Sy ":room alias unset [alias]"
Delete the provided alias from the room's alternative alias list.
.It Sy ":room alias show"
Show alternative aliases to the room, if any are set.
.It Sy ":room canon set [alias]"
Set the room's canonical alias to the one provided, and make the previous one an alternative alias.
.It Sy ":room canon unset [alias]"
Delete the room's canonical alias.
.It Sy ":room canon show"
Show the room's canonical alias, if any is set.
.El

.Sh "WINDOW COMMANDS"
Expand Down
20 changes: 20 additions & 0 deletions src/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,15 @@ pub enum RoomField {

/// The room topic.
Topic,

/// The room's entire list of alternative aliases.
Aliases,

/// A specific alternative alias to the room.
Alias(String),

/// The room's canonical alias.
CanonicalAlias,
}

/// An action that operates on a focused room.
Expand Down Expand Up @@ -397,6 +406,9 @@ pub enum RoomAction {

/// Unset a room property.
Unset(RoomField),

/// List the values in a list room property.
Show(RoomField),
}

/// An action that sends a message to a room.
Expand Down Expand Up @@ -596,6 +608,10 @@ pub enum IambError {
#[error("Invalid user identifier: {0}")]
InvalidUserId(String),

/// An invalid user identifier was specified.
#[error("Invalid room alias: {0}")]
InvalidRoomAlias(String),

/// An invalid verification identifier was specified.
#[error("Invalid verification user/device pair: {0}")]
InvalidVerificationId(String),
Expand Down Expand Up @@ -659,6 +675,10 @@ pub enum IambError {
#[error("Unknown room identifier: {0}")]
UnknownRoom(OwnedRoomId),

/// An invalid room alias id was specified.
#[error("Invalid room alias id: {0}")]
InvalidRoomAliasId(#[from] matrix_sdk::ruma::IdParseError),

/// A failure occurred during verification.
#[error("Verification request error: {0}")]
VerificationRequestError(#[from] matrix_sdk::encryption::identities::RequestVerificationError),
Expand Down
36 changes: 36 additions & 0 deletions src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,42 @@ fn iamb_room(desc: CommandDescription, ctx: &mut ProgContext) -> ProgResult {
("tag", "unset", Some(s)) => RoomAction::Unset(RoomField::Tag(tag_name(s)?)).into(),
("tag", "unset", None) => return Result::Err(CommandError::InvalidArgument),

// :room aliases show
("alias", "show", None) => RoomAction::Show(RoomField::Aliases).into(),
("alias", "show", Some(_)) => return Result::Err(CommandError::InvalidArgument),

// :room aliases unset <alias>
("alias", "unset", Some(s)) => RoomAction::Unset(RoomField::Alias(s)).into(),
("alias", "unset", None) => return Result::Err(CommandError::InvalidArgument),

// :room aliases set <alias>
("alias", "set", Some(s)) => RoomAction::Set(RoomField::Alias(s), "".into()).into(),
("alias", "set", None) => return Result::Err(CommandError::InvalidArgument),

// :room canonicalalias show
("canonicalalias" | "canon", "show", None) => {
RoomAction::Show(RoomField::CanonicalAlias).into()
},
("canonicalalias" | "canon", "show", Some(_)) => {
return Result::Err(CommandError::InvalidArgument)
},

// :room canonicalalias set
("canonicalalias" | "canon", "set", Some(s)) => {
RoomAction::Set(RoomField::CanonicalAlias, s).into()
},
("canonicalalias" | "canon", "set", None) => {
return Result::Err(CommandError::InvalidArgument)
},

// :room canonicalalias unset
("canonicalalias" | "canon", "unset", None) => {
RoomAction::Unset(RoomField::CanonicalAlias).into()
},
("canonicalalias" | "canon", "unset", Some(_)) => {
return Result::Err(CommandError::InvalidArgument)
},

_ => return Result::Err(CommandError::InvalidArgument),
};

Expand Down
211 changes: 209 additions & 2 deletions src/windows/room/mod.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,26 @@
//! # Windows for Matrix rooms and spaces
use std::collections::HashSet;

use matrix_sdk::{
room::Room as MatrixRoom,
ruma::{
api::client::{
alias::{
create_alias::v3::Request as CreateAliasRequest,
delete_alias::v3::Request as DeleteAliasRequest,
},
error::ErrorKind as ClientApiErrorKind,
},
events::{
room::{name::RoomNameEventContent, topic::RoomTopicEventContent},
room::{
canonical_alias::RoomCanonicalAliasEventContent,
name::RoomNameEventContent,
topic::RoomTopicEventContent,
},
tag::{TagInfo, Tags},
},
OwnedEventId,
OwnedRoomAliasId,
RoomId,
},
DisplayName,
Expand Down Expand Up @@ -53,6 +67,8 @@
use self::chat::ChatState;
use self::space::{Space, SpaceState};

use std::convert::TryFrom;

mod chat;
mod scrollback;
mod space;
Expand Down Expand Up @@ -182,7 +198,7 @@
pub async fn room_command(
&mut self,
act: RoomAction,
_: ProgramContext,
ctx: ProgramContext,
store: &mut ProgramStore,
) -> IambResult<Vec<(Action<IambInfo>, ProgramContext)>> {
match act {
Expand Down Expand Up @@ -280,6 +296,87 @@
let ev = RoomTopicEventContent::new(value);
let _ = room.send_state_event(ev).await.map_err(IambError::from)?;
},
RoomField::CanonicalAlias => {
let client = &mut store.application.worker.client;

let Ok(orai) = OwnedRoomAliasId::try_from(value.as_str()) else {
let err = IambError::InvalidRoomAlias(value);

return Err(err.into());
};

let mut alt_aliases =
room.alt_aliases().into_iter().collect::<HashSet<_>>();
let canonical_old = room.canonical_alias();

// If the room's alias is already that, ignore it
if canonical_old.as_ref() == Some(&orai) {
let msg = format!("The canonical room alias is already {orai}");

return Ok(vec![(Action::ShowInfoMessage(msg.into()), ctx)]);
}

// Try creating the room alias on the server.
let alias_create_req =
CreateAliasRequest::new(orai.clone(), room.room_id().into());
if let Err(e) = client.send(alias_create_req, None).await {
if let Some(ClientApiErrorKind::Unknown) = e.client_api_error_kind() {
// Ignore when it already exists.
} else {
return Err(IambError::from(e).into());
}
}

// Demote the previous one to an alt alias.
alt_aliases.extend(canonical_old);

// At this point the room alias definitely exists, and we can update the
// state event.
let mut ev = RoomCanonicalAliasEventContent::new();
ev.alias = Some(orai);
ev.alt_aliases = alt_aliases.into_iter().collect();
let _ = room.send_state_event(ev).await.map_err(IambError::from)?;
},
RoomField::Alias(alias) => {
let client = &mut store.application.worker.client;

let Ok(orai) = OwnedRoomAliasId::try_from(alias.as_str()) else {
let err = IambError::InvalidRoomAlias(alias);

return Err(err.into());
};

let mut alt_aliases =
room.alt_aliases().into_iter().collect::<HashSet<_>>();
let canonical = room.canonical_alias();

if alt_aliases.contains(&orai) || canonical.as_ref() == Some(&orai) {
let msg = format!("The alias {orai} already maps to this room");

return Ok(vec![(Action::ShowInfoMessage(msg.into()), ctx)]);
} else {
alt_aliases.insert(orai.clone());
}

// If the room alias does not exist on the server, create it
let alias_create_req = CreateAliasRequest::new(orai, room.room_id().into());
if let Err(e) = client.send(alias_create_req, None).await {
if let Some(ClientApiErrorKind::Unknown) = e.client_api_error_kind() {
// Ignore when it already exists.
} else {
return Err(IambError::from(e).into());
}
}

// And add it to the aliases in the state event.
let mut ev = RoomCanonicalAliasEventContent::new();
ev.alias = canonical;
ev.alt_aliases = alt_aliases.into_iter().collect();
let _ = room.send_state_event(ev).await.map_err(IambError::from)?;
},
RoomField::Aliases => {
// This never happens, aliases is only used for showing
},
}

Ok(vec![])
Expand All @@ -302,10 +399,120 @@
let ev = RoomTopicEventContent::new("".into());
let _ = room.send_state_event(ev).await.map_err(IambError::from)?;
},
RoomField::CanonicalAlias => {
let Some(alias_to_destroy) = room.canonical_alias() else {
let msg = format!("This room has no canonical alias to unset");

Check warning on line 404 in src/windows/room/mod.rs

View workflow job for this annotation

GitHub Actions / test (ubuntu-latest)

[clippy] reported by reviewdog 🐶 warning: useless use of `format!` --> src/windows/room/mod.rs:404:39 | 404 | ... let msg = format!("This room has no canonical alias to unset"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"This room has no canonical alias to unset".to_string()` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#useless_format = note: `#[warn(clippy::useless_format)]` on by default Raw Output: src/windows/room/mod.rs:404:39:w:warning: useless use of `format!` --> src/windows/room/mod.rs:404:39 | 404 | ... let msg = format!("This room has no canonical alias to unset"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"This room has no canonical alias to unset".to_string()` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#useless_format = note: `#[warn(clippy::useless_format)]` on by default __END__

return Ok(vec![(Action::ShowInfoMessage(msg.into()), ctx)]);
};

// Remove the canonical alias from the state event.
let mut ev = RoomCanonicalAliasEventContent::new();
ev.alias = None;
ev.alt_aliases = room.alt_aliases();
let _ = room.send_state_event(ev).await.map_err(IambError::from)?;

// And then unmap it on the server.
let del_req = DeleteAliasRequest::new(alias_to_destroy);
let _ = store
.application
.worker
.client
.send(del_req, None)
.await
.map_err(IambError::from)?;
},
RoomField::Alias(alias) => {
let Ok(orai) = OwnedRoomAliasId::try_from(alias.as_str()) else {
let err = IambError::InvalidRoomAlias(alias);

return Err(err.into());
};

let alt_aliases = room.alt_aliases();
let canonical = room.canonical_alias();

if !alt_aliases.contains(&orai) && canonical.as_ref() != Some(&orai) {
let msg = format!("The alias {orai:?} isn't mapped to this room");

return Ok(vec![(Action::ShowInfoMessage(msg.into()), ctx)]);
}

// Remove the alias from the state event if it's in it.
let mut ev = RoomCanonicalAliasEventContent::new();
ev.alias = canonical.filter(|canon| canon != &orai);
ev.alt_aliases = alt_aliases;
ev.alt_aliases.retain(|in_orai| in_orai != &orai);
let _ = room.send_state_event(ev).await.map_err(IambError::from)?;

// And then unmap it on the server.
let del_req = DeleteAliasRequest::new(orai);
let _ = store
.application
.worker
.client
.send(del_req, None)
.await
.map_err(IambError::from)?;
},
RoomField::Aliases => {
// This will not happen, you cannot unset all aliases
},
}

Ok(vec![])
},
RoomAction::Show(field) => {
let room = store
.application
.get_joined_room(self.id())
.ok_or(UIError::Application(IambError::NotJoined))?;

let msg = match field {
RoomField::Name => {
match room.name() {
None => "Room has no name".into(),
Some(name) => format!("Room name: {name:?}"),
}
},
RoomField::Topic => {
match room.topic() {
None => "Room has no topic".into(),
Some(topic) => format!("Room topic: {topic:?}"),
}
},
RoomField::Aliases => {
let aliases = room
.alt_aliases()
.iter()
.map(OwnedRoomAliasId::to_string)
.collect::<Vec<String>>();

if aliases.is_empty() {
"No alternative aliases in room".into()
} else {
format!("Alternative aliases: {}.", aliases.join(", "))
}
},
RoomField::CanonicalAlias => {
match room.canonical_alias() {
None => "No canonical alias for room".into(),
Some(can) => format!("Canonical alias: {can}"),
}
},
RoomField::Tag(_) => {
format!("Cannot currently show value for a tag")

Check warning on line 504 in src/windows/room/mod.rs

View workflow job for this annotation

GitHub Actions / test (ubuntu-latest)

[clippy] reported by reviewdog 🐶 warning: useless use of `format!` --> src/windows/room/mod.rs:504:25 | 504 | format!("Cannot currently show value for a tag") | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"Cannot currently show value for a tag".to_string()` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#useless_format Raw Output: src/windows/room/mod.rs:504:25:w:warning: useless use of `format!` --> src/windows/room/mod.rs:504:25 | 504 | format!("Cannot currently show value for a tag") | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"Cannot currently show value for a tag".to_string()` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#useless_format __END__
},
RoomField::Alias(_) => {
format!("Cannot show a single alias; use `:room aliases show` instead.")

Check warning on line 507 in src/windows/room/mod.rs

View workflow job for this annotation

GitHub Actions / test (ubuntu-latest)

[clippy] reported by reviewdog 🐶 warning: useless use of `format!` --> src/windows/room/mod.rs:507:25 | 507 | format!("Cannot show a single alias; use `:room aliases show` instead.") | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"Cannot show a single alias; use `:room aliases show` instead.".to_string()` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#useless_format Raw Output: src/windows/room/mod.rs:507:25:w:warning: useless use of `format!` --> src/windows/room/mod.rs:507:25 | 507 | format!("Cannot show a single alias; use `:room aliases show` instead.") | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"Cannot show a single alias; use `:room aliases show` instead.".to_string()` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#useless_format __END__
},
};

let msg = InfoMessage::Pager(msg);
let act = Action::ShowInfoMessage(msg);

Ok(vec![(act, ctx)])
},
}
}

Expand Down
Loading