Skip to content
This repository was archived by the owner on Sep 27, 2024. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
df6b832
add new mention node types
Jun 13, 2023
93c97c8
add TODOs to clear match errors
Jun 13, 2023
1e2b95f
bit more type manipulation
Jun 13, 2023
437feee
handle invalid URIs early in `insert*` methods
Jun 14, 2023
5fb76eb
move at-room logic check to dom node layer
Jun 14, 2023
64de4f6
export MentionKind
Jun 14, 2023
d60112f
move to `Room` and `User` mention types
Jun 14, 2023
2d2cdad
fix most tests
Jun 14, 2023
6e16114
get all tests passing
Jun 14, 2023
4a56495
make `new` Mention return a `Result`, fix all tests/uses
Jun 14, 2023
3a82ef8
tidy up the logic
Jun 14, 2023
65bfbaf
rename function
Jun 14, 2023
8712fc1
update comment
Jun 14, 2023
8e159f8
update comments, strip out unused code
Jun 14, 2023
8e7c42d
make the message output use mx_id for room mentions
Jun 14, 2023
e0d2f1c
tidy up parsing
Jun 14, 2023
834e5cc
tidy up match branches and functions
Jun 14, 2023
1b0568b
tidy
Jun 14, 2023
1cd484a
fix clippy errors
Jun 14, 2023
d011a5b
fix typo
Jun 14, 2023
d3a0b74
extract `@room` checking/setting
Jun 14, 2023
d2f0edf
add import
Jun 14, 2023
1108eca
add test
Jun 14, 2023
e018d42
try to fix wasm error
Jun 14, 2023
b6d86e6
put import in correct place
Jun 14, 2023
7a6bf6f
get solution for custom links working
Jun 15, 2023
09e6c5d
add test and get it passing
Jun 15, 2023
e28717c
tidy up code
Jun 15, 2023
dde0295
add more tests
Jun 15, 2023
e383123
fix clippy error
Jun 15, 2023
4c08a5f
reinstate removed comment
Jun 15, 2023
a2e3361
separate out user and room in markdown
Jun 15, 2023
cacc965
extract AT_ROOM to constant in other crate
Jun 15, 2023
66ede7e
extract at room utils to matrix mention crate
Jun 15, 2023
6e90343
first hack at splitting at-room behaviour
Jun 15, 2023
38dc7cc
split dom node methods apart
Jun 15, 2023
e46895e
fix broken tests
Jun 15, 2023
554c049
remove unused import
Jun 15, 2023
3720b6c
add special case for example app
Jun 15, 2023
c5802f4
change function call
Jun 15, 2023
c946c94
change function name
Jun 15, 2023
b6b07bd
read new functions across to mobile
Jun 15, 2023
ca571d7
add test
Jun 15, 2023
a1aaf8b
remove unused code
Jun 15, 2023
393d0d9
reduce repeated code
Jun 15, 2023
afd8efb
refactor
Jun 15, 2023
a5f4fd5
redo comments
Jun 15, 2023
4d55895
get all tests passing
Jun 15, 2023
24e03cb
move AT_ROOM back where it belongs
Jun 15, 2023
2b81075
reinstate whitespace
Jun 15, 2023
98bb0f3
correct bindings error, update udl
Jun 15, 2023
c7fc5ec
update comment
Jun 15, 2023
18910a0
add TODOs, fix issues from call
Jun 16, 2023
8417740
add the configuration for the matrix_mentions crate
Jun 16, 2023
4028efc
fix TODO
Jun 16, 2023
46f48cb
address comments
Jun 16, 2023
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
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions bindings/wysiwyg-ffi/src/ffi_composer_model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ impl ComposerModel {

/// Creates an at-room mention node and inserts it into the composer at the current selection
pub fn insert_at_room_mention(
&mut self,
self: &Arc<Self>,
attributes: Vec<Attribute>,
) -> Arc<ComposerUpdate> {
let attrs = attributes
Expand Down Expand Up @@ -303,7 +303,7 @@ impl ComposerModel {
/// Creates an at-room mention node and inserts it into the composer, replacing the
/// text content defined by the suggestion
pub fn insert_at_room_mention_at_suggestion(
&mut self,
self: &Arc<Self>,
suggestion: SuggestionPattern,
attributes: Vec<Attribute>,
) -> Arc<ComposerUpdate> {
Expand Down
2 changes: 2 additions & 0 deletions bindings/wysiwyg-ffi/src/wysiwyg_composer.udl
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,9 @@ interface ComposerModel {
ComposerUpdate set_link(string url, sequence<Attribute> attributes);
ComposerUpdate set_link_with_text(string url, string text, sequence<Attribute> attributes);
ComposerUpdate remove_links();
ComposerUpdate insert_at_room_mention(sequence<Attribute> attributes);
ComposerUpdate insert_mention(string url, string text, sequence<Attribute> attributes);
ComposerUpdate insert_at_room_mention_at_suggestion(SuggestionPattern suggestion, sequence<Attribute> attributes);
ComposerUpdate insert_mention_at_suggestion(string url, string text, SuggestionPattern suggestion, sequence<Attribute> attributes);
ComposerUpdate code_block();
ComposerUpdate quote();
Expand Down
6 changes: 5 additions & 1 deletion crates/matrix_mentions/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ name = "matrix_mentions"
version = "0.1.0"
edition = "2021"

[dependencies]
[features]
default = ["custom-matrix-urls"]
custom-matrix-urls = []

[dependencies]
cfg-if = "1.0.0"
ruma-common = "0.11.3"
4 changes: 1 addition & 3 deletions crates/matrix_mentions/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,4 @@

mod mention;

pub use crate::mention::{
get_at_room_display_text, Mention, MentionKind, AT_ROOM,
};
pub use crate::mention::{Mention, MentionKind};
26 changes: 14 additions & 12 deletions crates/matrix_mentions/src/mention.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,6 @@
use ruma_common::{matrix_uri::MatrixId, IdParseError, MatrixToUri, MatrixUri};

const MATRIX_TO_BASE_URL: &str = "https://matrix.to/#/";
pub const AT_ROOM: &str = "@room";

/// Util function to get the display text for an at-room mention
pub fn get_at_room_display_text() -> &'static str {
AT_ROOM
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Mention {
Expand Down Expand Up @@ -164,20 +158,28 @@ impl Mention {
/// If any of the above succeed, return Some<MatrixIdI. Else return None.
fn parse_matrix_id(uri: &str) -> Option<MatrixId> {
if let Ok(matrix_uri) = MatrixUri::parse(uri) {
Some(matrix_uri.id().to_owned())
return Some(matrix_uri.id().to_owned());
} else if let Ok(matrix_to_uri) = MatrixToUri::parse(uri) {
Some(matrix_to_uri.id().to_owned())
} else if let Ok(matrix_to_uri) = parse_external_id(uri) {
Some(matrix_to_uri.id().to_owned())
} else {
None
return Some(matrix_to_uri.id().to_owned());
}

cfg_if::cfg_if! {
if #[cfg(any(test, feature = "custom-matrix-urls"))] {
if let Ok(matrix_to_uri) = parse_external_id(uri) {
return Some(matrix_to_uri.id().to_owned());
}
}
}

None
}

/// Attempts to split an external id on `/#/`, rebuild as a matrix to style permalink then parse
/// using ruma.
///
/// Returns the result of calling `parse` in ruma.

#[cfg(any(test, feature = "custom-matrix-urls"))]
fn parse_external_id(uri: &str) -> Result<MatrixToUri, IdParseError> {
// first split the string into the parts we need
let parts: Vec<&str> = uri.split("/#/").collect();
Expand Down
126 changes: 51 additions & 75 deletions crates/wysiwyg/src/composer_model/mentions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,102 +11,95 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use matrix_mentions::Mention;

use crate::{
dom::DomLocation, ComposerModel, ComposerUpdate, DomNode, Location,
SuggestionPattern, UnicodeString,
dom::{nodes::MentionNode, DomLocation},
ComposerModel, ComposerUpdate, DomNode, Location, SuggestionPattern,
UnicodeString,
};

impl<S> ComposerModel<S>
where
S: UnicodeString,
{
/// Remove the suggestion text and then insert a mention into the composer, using the following rules
/// - Do not do anthing if the uri is invalid
/// - Do not do anthing if the range includes link or code leaves
/// - If the composer contains a selection, remove the contents of the selection
/// prior to inserting a mention at the cursor.
/// - If the composer contains a cursor, insert a mention at the cursor
/// Checks to see if the mention should be inserted and also if the mention can be created.
/// If both of these checks are passed it will remove the suggestion and then insert a mention.
pub fn insert_mention_at_suggestion(
&mut self,
url: S,
text: S,
suggestion: SuggestionPattern,
attributes: Vec<(S, S)>,
) -> ComposerUpdate<S> {
if self.should_not_insert_mention(&url) {
if self.range_contains_link_or_code_leaves() {
return ComposerUpdate::keep();
}

self.push_state_to_history();
self.do_replace_text_in(S::default(), suggestion.start, suggestion.end);
self.state.start = Location::from(suggestion.start);
self.state.end = self.state.start;

if let Ok(mention_node) = DomNode::new_mention(url, text, attributes) {
self.push_state_to_history();
self.do_replace_text_in(
S::default(),
suggestion.start,
suggestion.end,
);
self.state.start = Location::from(suggestion.start);
self.state.end = self.state.start;
self.do_insert_mention(mention_node)
} else {
ComposerUpdate::keep()
}
}

/// Remove the suggestion text and then insert an at-room mention into the composer, using the following rules
/// - Do not do anthing if the range includes link or code leaves
/// - If the composer contains a selection, remove the contents of the selection
/// prior to inserting a mention at the cursor.
/// - If the composer contains a cursor, insert a mention at the cursor
pub fn insert_at_room_mention_at_suggestion(
/// Checks to see if the mention should be inserted and also if the mention can be created.
/// If both of these checks are passed it will remove any selection if present and then insert a mention.
pub fn insert_mention(
&mut self,
suggestion: SuggestionPattern,
url: S,
text: S,
attributes: Vec<(S, S)>,
) -> ComposerUpdate<S> {
if self.should_not_insert_at_room_mention() {
if self.range_contains_link_or_code_leaves() {
return ComposerUpdate::keep();
}

self.push_state_to_history();
self.do_replace_text_in(S::default(), suggestion.start, suggestion.end);
self.state.start = Location::from(suggestion.start);
self.state.end = self.state.start;

let mention_node = DomNode::new_at_room_mention(attributes);
self.do_insert_mention(mention_node)
if let Ok(mention_node) = DomNode::new_mention(url, text, attributes) {
self.push_state_to_history();
if self.has_selection() {
self.do_replace_text(S::default());
}
self.do_insert_mention(mention_node)
} else {
ComposerUpdate::keep()
}
}

/// Inserts a mention into the composer. It uses the following rules:
/// - Do not insert a mention if the uri is invalid
/// - Do not insert a mention if the range includes link or code leaves
/// - If the composer contains a selection, remove the contents of the selection
/// prior to inserting a mention at the cursor.
/// - If the composer contains a cursor, insert a mention at the cursor
pub fn insert_mention(
/// Checks to see if the at-room mention should be inserted.
/// If so it will remove the suggestion and then insert an at-room mention.
pub fn insert_at_room_mention_at_suggestion(
&mut self,
url: S,
text: S,
suggestion: SuggestionPattern,
attributes: Vec<(S, S)>,
) -> ComposerUpdate<S> {
if self.should_not_insert_mention(&url) {
if self.range_contains_link_or_code_leaves() {
return ComposerUpdate::keep();
}

self.push_state_to_history();
if self.has_selection() {
self.do_replace_text(S::default());
}
self.do_replace_text_in(S::default(), suggestion.start, suggestion.end);
self.state.start = Location::from(suggestion.start);
self.state.end = self.state.start;

if let Ok(mention_node) = DomNode::new_mention(url, text, attributes) {
self.do_insert_mention(mention_node)
} else {
ComposerUpdate::keep()
}
let mention_node = DomNode::new_at_room_mention(attributes);
self.do_insert_mention(mention_node)
}

/// Checks to see if the at-room mention should be inserted.
/// If so it will remove any selection if present and then insert an at-room mention.
pub fn insert_at_room_mention(
&mut self,
attributes: Vec<(S, S)>,
) -> ComposerUpdate<S> {
if self.should_not_insert_at_room_mention() {
if self.range_contains_link_or_code_leaves() {
return ComposerUpdate::keep();
}

Expand All @@ -119,23 +112,21 @@ where
self.do_insert_mention(mention_node)
}

/// Creates a new mention node then inserts the node at the cursor position. If creation fails due to
/// an invalid uri, it will return `ComposerUpdate::keep()`.
/// It adds a trailing space when the inserted mention is the last node in it's parent.
/// Inserts the node at the cursor position. It adds a trailing space when the inserted
/// mention is the last node in it's parent.
fn do_insert_mention(
&mut self,
mention_node: DomNode<S>,
mention_node: MentionNode<S>, // TODO can we type this as a mention node somehow
) -> ComposerUpdate<S> {
if !mention_node.is_mention_node() {
return ComposerUpdate::keep();
}

let (start, end) = self.safe_selection();
let range = self.state.dom.find_range(start, end);

let new_cursor_index = start + mention_node.text_len();

let handle = self.state.dom.insert_node_at_cursor(&range, mention_node);
let handle = self
.state
.dom
.insert_node_at_cursor(&range, DomNode::Mention(mention_node));

// manually move the cursor to the end of the mention
self.state.start = Location::from(new_cursor_index);
Expand All @@ -149,23 +140,8 @@ where
}
}

/// Utility functions for the insert_mention* methods. It returns false if:
/// - the range includes any link or code type leaves
/// - the url is not a valid matrix uri
///
/// Related issue is here:
/// https://github.com/matrix-org/matrix-rich-text-editor/issues/702
/// We do not allow mentions to be inserted into links, the planned behaviour is
/// detailed in the above issue.
fn should_not_insert_mention(&self, url: &S) -> bool {
!Mention::is_valid_uri(url.to_string().as_str())
|| self.range_contains_link_or_code_leaves()
}

fn should_not_insert_at_room_mention(&self) -> bool {
self.range_contains_link_or_code_leaves()
}

/// We should not insert a mention if the uri is invalid or the range contains link
/// or code leaves. See issue https://github.com/matrix-org/matrix-rich-text-editor/issues/702.
fn range_contains_link_or_code_leaves(&self) -> bool {
let (start, end) = self.safe_selection();
let range = self.state.dom.find_range(start, end);
Expand Down
15 changes: 7 additions & 8 deletions crates/wysiwyg/src/dom/nodes/dom_node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use crate::composer_model::example_format::SelectionWriter;
use crate::dom::dom_handle::DomHandle;
use crate::dom::nodes::{
Expand Down Expand Up @@ -138,26 +139,24 @@ where
DomNode::Container(ContainerNode::new_link(url, children, attributes))
}

/// Create a new mention node. This function will perform a single check of the display
/// text and return an at-room mention if that text exactly matches `@room`
///
/// Returns a result as creating a mention node can fail with an invalid uri
/// Attempts to create a new mention node. Returns a result as creating a
/// mention node can fail if attempted with an invalid uri.
pub fn new_mention(
url: S,
display_text: S,
attributes: Vec<(S, S)>,
) -> Result<DomNode<S>, UriParseError> {
) -> Result<MentionNode<S>, UriParseError> {
if let Ok(mention_node) =
MentionNode::new(url, display_text, attributes)
{
Ok(DomNode::Mention(mention_node))
Ok(mention_node)
} else {
Err(UriParseError)
}
}

pub fn new_at_room_mention(attributes: Vec<(S, S)>) -> DomNode<S> {
DomNode::Mention(MentionNode::new_at_room(attributes))
pub fn new_at_room_mention(attributes: Vec<(S, S)>) -> MentionNode<S> {
MentionNode::new_at_room(attributes)
}

pub fn is_container_node(&self) -> bool {
Expand Down
Loading