Skip to content
This repository was archived by the owner on Sep 27, 2024. It is now read-only.

get_mentions_state() implemented on the Rust side (only for users and @room) #849

Merged
merged 5 commits into from
Oct 19, 2023
Merged
Show file tree
Hide file tree
Changes from 3 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
5 changes: 5 additions & 0 deletions bindings/wysiwyg-ffi/src/ffi_composer_model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use crate::ffi_composer_state::ComposerState;
use crate::ffi_composer_update::ComposerUpdate;
use crate::ffi_dom_creation_error::DomCreationError;
use crate::ffi_link_actions::LinkAction;
use crate::ffi_mentions_state::MentionsState;
use crate::into_ffi::IntoFfi;
use crate::{ActionState, ComposerAction, SuggestionPattern};

Expand Down Expand Up @@ -370,6 +371,10 @@ impl ComposerModel {
self.inner.lock().unwrap().get_link_action().into()
}

pub fn get_mentions_state(self: &Arc<Self>) -> MentionsState {
self.inner.lock().unwrap().get_mentions_state().into()
}

/// Force a panic for test purposes
pub fn debug_panic(self: &Arc<Self>) {
#[cfg(debug_assertions)]
Expand Down
14 changes: 14 additions & 0 deletions bindings/wysiwyg-ffi/src/ffi_mentions_state.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#[derive(uniffi::Record)]
pub struct MentionsState {
pub user_ids: Vec<String>,
pub has_at_room_mention: bool,
}

impl From<wysiwyg::MentionsState> for MentionsState {
fn from(value: wysiwyg::MentionsState) -> Self {
Self {
user_ids: value.user_ids.into_iter().collect(),
has_at_room_mention: value.has_at_room_mention,
}
}
}
2 changes: 2 additions & 0 deletions bindings/wysiwyg-ffi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ mod ffi_composer_state;
mod ffi_composer_update;
mod ffi_dom_creation_error;
mod ffi_link_actions;
mod ffi_mentions_state;
mod ffi_menu_action;
mod ffi_menu_state;
mod ffi_pattern_key;
Expand All @@ -38,6 +39,7 @@ pub use crate::ffi_composer_state::ComposerState;
pub use crate::ffi_composer_update::ComposerUpdate;
pub use crate::ffi_dom_creation_error::DomCreationError;
pub use crate::ffi_link_actions::LinkAction;
pub use crate::ffi_mentions_state::MentionsState;
pub use crate::ffi_menu_action::MenuAction;
pub use crate::ffi_menu_state::MenuState;
pub use crate::ffi_pattern_key::PatternKey;
Expand Down
34 changes: 31 additions & 3 deletions crates/wysiwyg/src/composer_model/mentions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,43 @@
// limitations under the License.

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

impl<S> ComposerModel<S>
where
S: UnicodeString,
{
/// Returns the current mentions state of the content of the RTE editor.
pub fn get_mentions_state(&self) -> MentionsState {
let mut mentions_state = MentionsState::default();
for node in self.state.dom.iter_mentions() {
match node.kind() {
MentionNodeKind::AtRoom => {
mentions_state.has_at_room_mention = true
}
MentionNodeKind::MatrixUri { mention } => {
match mention.kind() {
matrix_mentions::MentionKind::Room => {
// Not required yet
}
matrix_mentions::MentionKind::User => {
mentions_state
.user_ids
.insert(mention.mx_id().to_string());
}
}
}
}
}
mentions_state
}

/// 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(
Expand Down
8 changes: 7 additions & 1 deletion crates/wysiwyg/src/dom/iter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ use crate::{DomHandle, DomNode, UnicodeString};
use std::collections::HashSet;

use super::{
nodes::{ContainerNode, TextNode},
nodes::{ContainerNode, MentionNode, TextNode},
Dom,
};

Expand All @@ -48,6 +48,12 @@ where
self.iter().filter_map(DomNode::as_container)
}

/// Returns an iterator over all the mention nodes of this DOM, in depth-first
/// order
pub fn iter_mentions(&self) -> impl Iterator<Item = &MentionNode<S>> {
self.iter().filter_map(DomNode::as_mention)
}

/// Return an iterator over all nodes of the DOM from the passed node,
/// depth-first order (including self).
pub fn iter_from<'a>(&'a self, node: &'a DomNode<S>) -> DomNodeIterator<S> {
Expand Down
8 changes: 8 additions & 0 deletions crates/wysiwyg/src/dom/nodes/dom_node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,14 @@ where
}
}

pub(crate) fn as_mention(&self) -> Option<&MentionNode<S>> {
if let Self::Mention(v) = self {
Some(v)
} else {
None
}
}

pub fn kind(&self) -> DomNodeKind {
match self {
DomNode::Text(_) => DomNodeKind::Text,
Expand Down
2 changes: 2 additions & 0 deletions crates/wysiwyg/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ mod format_type;
mod link_action;
mod list_type;
mod location;
mod mentions_state;
mod menu_action;
mod menu_state;
mod pattern_key;
Expand Down Expand Up @@ -51,6 +52,7 @@ pub use crate::link_action::LinkAction;
pub use crate::link_action::LinkActionUpdate;
pub use crate::list_type::ListType;
pub use crate::location::Location;
pub use crate::mentions_state::MentionsState;
pub use crate::menu_action::MenuAction;
pub use crate::menu_action::MenuActionSuggestion;
pub use crate::menu_state::MenuState;
Expand Down
21 changes: 21 additions & 0 deletions crates/wysiwyg/src/mentions_state.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright 2022 The Matrix.org Foundation C.I.C.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// 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 std::collections::HashSet;

#[derive(Default, Debug, PartialEq, Eq)]
pub struct MentionsState {
pub user_ids: HashSet<String>,
pub has_at_room_mention: bool,
}
69 changes: 68 additions & 1 deletion crates/wysiwyg/src/tests/test_mentions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use widestring::Utf16String;

use crate::{
tests::testutils_composer_model::{cm, tx},
ComposerModel, MenuAction,
ComposerModel, MentionsState, MenuAction,
};
/**
* INSERTING WITH PARSING
Expand Down Expand Up @@ -594,6 +594,73 @@ fn can_insert_at_room_mention() {
assert_eq!(tx(&model), "<a style=\"some css\" data-mention-type=\"at-room\" href=\"#\" contenteditable=\"false\">@room</a>&nbsp;|")
}

#[test]
fn get_mentions_state_for_no_mentions() {
let model = cm("<p>hello!|</p>");
assert_eq!(model.get_mentions_state(), MentionsState::default())
}

#[test]
fn get_mentions_state_for_user_mention() {
let model = cm("<p>hello <a data-mention-type=\"user\" href=\"https://matrix.to/#/@alice:matrix.org\" contenteditable=\"false\">Alice</a>!|</p>");
let mut state = MentionsState::default();
state.user_ids.insert("@alice:matrix.org".into());
assert_eq!(model.get_mentions_state(), state)
}

#[test]
fn get_mentions_state_for_multiple_user_mentions() {
let model = cm("<p>hello <a data-mention-type=\"user\" href=\"https://matrix.to/#/@alice:matrix.org\" contenteditable=\"false\">Alice</a> and <a data-mention-type=\"user\" href=\"https://matrix.to/#/@bob:matrix.org\" contenteditable=\"false\">Bob</a>!|</p>");
let mut state = MentionsState::default();
state.user_ids.insert("@alice:matrix.org".into());
state.user_ids.insert("@bob:matrix.org".into());
assert_eq!(model.get_mentions_state(), state)
}

#[test]
fn get_mentions_state_for_at_room_mention() {
let model = cm("<p>hello <a data-mention-type=\"at-room\" href=\"#\" contenteditable=\"false\">@room</a>|");
let state = MentionsState {
user_ids: Default::default(),
has_at_room_mention: true,
};
assert_eq!(model.get_mentions_state(), state)
}

#[test]
fn get_mentions_state_for_at_room_plain_mention() {
let model = cm("<p>hello @room|");
let state = MentionsState {
user_ids: Default::default(),
has_at_room_mention: true,
};
assert_eq!(model.get_mentions_state(), state)
}

#[test]
fn get_mentions_state_for_multiple_user_and_at_room_mentions() {
let model = cm("<p>hello <a data-mention-type=\"user\" href=\"https://matrix.to/#/@alice:matrix.org\" contenteditable=\"false\">Alice</a>, <a data-mention-type=\"user\" href=\"https://matrix.to/#/@bob:matrix.org\" contenteditable=\"false\">Bob</a> and <a data-mention-type=\"at-room\" href=\"#\" contenteditable=\"false\">@room</a>!|</p>");
let mut state = MentionsState::default();
state.user_ids.insert("@alice:matrix.org".into());
state.user_ids.insert("@bob:matrix.org".into());
state.has_at_room_mention = true;
assert_eq!(model.get_mentions_state(), state)
}

#[test]
fn get_mentions_state_for_user_mention_with_custom_link() {
let model = cm("<p>hello <a data-mention-type=\"user\" href=\"https://custom.link/#/@alice:matrix.org\" contenteditable=\"false\">Alice</a>!|</p>");
let mut state = MentionsState::default();
state.user_ids.insert("@alice:matrix.org".into());
assert_eq!(model.get_mentions_state(), state)
}

#[test]
fn get_mentions_state_empty_for_non_intentional_at_room_mention() {
let model = cm("<pre>hello @room!|</pre>");
assert_eq!(model.get_mentions_state(), MentionsState::default())
}

/**
* HELPER FUNCTIONS
*/
Expand Down