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

Commit

Permalink
Amend mention message html output (#716)
Browse files Browse the repository at this point in the history
* if it's an at-room mention, only output the raw text `@room`
* if it's a room or user mention, output the mention as <a href="...">display_text</a>
  • Loading branch information
artcodespace authored Jun 13, 2023
1 parent b431568 commit 6b3425e
Show file tree
Hide file tree
Showing 8 changed files with 165 additions and 21 deletions.
11 changes: 10 additions & 1 deletion crates/wysiwyg/src/composer_model/mentions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,16 @@ where
let (start, end) = self.safe_selection();
let range = self.state.dom.find_range(start, end);

let new_node = DomNode::new_mention(url, text, attributes);
// use the display text decide the mention type
// TODO extract this into a util function if it is reused when parsing the html prior to editing a message
// TODO decide if this do* function should be separated to handle mention vs at-room mention
// TODO handle invalid mention urls after permalink parsing methods have been created
let new_node = if text == "@room".into() {
DomNode::new_at_room_mention(attributes)
} else {
DomNode::new_mention(url, text, attributes)
};

let new_cursor_index = start + new_node.text_len();

let handle = self.state.dom.insert_node_at_cursor(&range, new_node);
Expand Down
32 changes: 22 additions & 10 deletions crates/wysiwyg/src/dom/nodes/mention_node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,22 +129,34 @@ impl<S: UnicodeString> MentionNode<S> {
let cur_pos = formatter.len();
match self.kind() {
MentionNodeKind::MatrixUrl { display_text, url } => {
let mut attributes = self.attributes.clone();
attributes.push(("href".into(), url.clone()));

if !as_message {
attributes.push(("contenteditable".into(), "false".into()))
// TODO: data-mention-type = "user" | "room"
}
// if formatting as a message, only include the href attribute
let attributes = if as_message {
vec![("href".into(), url.clone())]
} else {
let mut attributes_for_composer = self.attributes.clone();
attributes_for_composer.push(("href".into(), url.clone()));
attributes_for_composer
.push(("contenteditable".into(), "false".into()));
attributes_for_composer
};

self.fmt_tag_open(tag, formatter, &Some(attributes));

formatter.push(display_text.clone());

self.fmt_tag_close(tag, formatter);
}
MentionNodeKind::AtRoom => {
formatter.push(self.display_text());
// if formatting as a message, simply use the display text (@room)
if as_message {
formatter.push(self.display_text())
} else {
let mut attributes = self.attributes.clone();
attributes.push(("href".into(), "#".into())); // designates a placeholder link in html
attributes.push(("contenteditable".into(), "false".into()));

self.fmt_tag_open(tag, formatter, &Some(attributes));
formatter.push(self.display_text());
self.fmt_tag_close(tag, formatter);
};
}
}

Expand Down
1 change: 1 addition & 0 deletions crates/wysiwyg/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ pub mod test_selection;
pub mod test_set_content;
pub mod test_suggestions;
pub mod test_to_markdown;
pub mod test_to_message_html;
pub mod test_to_plain_text;
pub mod test_to_raw_text;
pub mod test_to_tree;
Expand Down
89 changes: 89 additions & 0 deletions crates/wysiwyg/src/tests/test_to_message_html.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// Copyright 2023 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 crate::tests::testutils_composer_model::{cm, tx};

#[test]
fn replaces_empty_paragraphs_with_newline_characters() {
let mut model = cm("|");
model.replace_text("hello".into());
model.enter();
model.enter();
model.enter();
model.enter();
model.replace_text("Alice".into());

assert_eq!(
tx(&model),
"<p>hello</p><p>&nbsp;</p><p>&nbsp;</p><p>&nbsp;</p><p>Alice|</p>"
);
let message_output = model.get_content_as_message_html();
assert_eq!(message_output, "<p>hello</p>\n\n\n<p>Alice</p>");
}
#[test]
fn only_outputs_href_attribute_on_user_mention() {
let mut model = cm("|");
model.insert_mention(
"www.url.com".into(),
"inner text".into(),
vec![
("data-mention-type".into(), "user".into()),
("style".into(), "some css".into()),
],
);
assert_eq!(tx(&model), "<a data-mention-type=\"user\" style=\"some css\" href=\"www.url.com\" contenteditable=\"false\">inner text</a>&nbsp;|");

let message_output = model.get_content_as_message_html();
assert_eq!(
message_output,
"<a href=\"www.url.com\">inner text</a>\u{a0}"
);
}

#[test]
fn only_outputs_href_attribute_on_room_mention() {
let mut model = cm("|");
model.insert_mention(
"www.url.com".into(),
"inner text".into(),
vec![
("data-mention-type".into(), "room".into()),
("style".into(), "some css".into()),
],
);
assert_eq!(tx(&model), "<a data-mention-type=\"room\" style=\"some css\" href=\"www.url.com\" contenteditable=\"false\">inner text</a>&nbsp;|");

let message_output = model.get_content_as_message_html();
assert_eq!(
message_output,
"<a href=\"www.url.com\">inner text</a>\u{a0}"
);
}

#[test]
fn only_outputs_href_inner_text_for_at_room_mention() {
let mut model = cm("|");
model.insert_mention(
"anything".into(), // this should be ignored in favour of a # placeholder
"@room".into(),
vec![
("data-mention-type".into(), "at-room".into()),
("style".into(), "some css".into()),
],
);
assert_eq!(tx(&model), "<a data-mention-type=\"at-room\" style=\"some css\" href=\"#\" contenteditable=\"false\">@room</a>&nbsp;|");

let message_output = model.get_content_as_message_html();
assert_eq!(message_output, "@room\u{a0}");
}
1 change: 1 addition & 0 deletions platforms/web/lib/composer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ export function processInput(
{
actions: formattingFunctions,
content: () => composerModel.get_content_as_html(),
messageContent: () => composerModel.get_content_as_message_html(),
},
editor,
inputEventProcessor,
Expand Down
1 change: 1 addition & 0 deletions platforms/web/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export type FormattingFunctions = Record<
export type Wysiwyg = {
actions: FormattingFunctions;
content: () => string;
messageContent: () => string;
};

export type InputEventProcessor = (
Expand Down
1 change: 1 addition & 0 deletions platforms/web/lib/useListeners/event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ function getInputFromKeyDown(
{
actions: formattingFunctions,
content: () => composerModel.get_content_as_html(),
messageContent: () => composerModel.get_content_as_message_html(),
},
editor,
inputEventProcessor,
Expand Down
50 changes: 40 additions & 10 deletions platforms/web/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ function App() {
if (debug.testRef.current) {
debug.traceAction(null, 'send', `${wysiwyg.content()}`);
}
console.log(`SENDING: ${wysiwyg.content()}`);
console.log(`SENDING MESSAGE HTML: ${wysiwyg.messageContent()}`);
wysiwyg.actions.clear();
return null;
}
Expand All @@ -97,6 +97,11 @@ function App() {
actionStates.unorderedList === 'reversed' ||
actionStates.orderedList === 'reversed';

const commandExists = suggestion && suggestion.type === 'command';
const mentionExists = suggestion && suggestion.type === 'mention';
const shouldDisplayAtMention = mentionExists && suggestion.keyChar === '@';
const shouldDisplayHashMention =
mentionExists && suggestion.keyChar === '#';
return (
<div className="wrapper">
<div>
Expand Down Expand Up @@ -187,26 +192,51 @@ function App() {
<button type="button" onClick={(_e) => wysiwyg.clear()}>
clear
</button>
{suggestion && suggestion.type === 'mention' && (
{shouldDisplayAtMention && (
<>
<button
type="button"
onClick={(_e) =>
wysiwyg.mention(
'https://matrix.to/#/@alice_user:element.io',
'Alice',
{
'data-mention-type': 'user',
},
)
}
>
Add User mention
</button>
<button
type="button"
onClick={(_e) =>
wysiwyg.mention('#', '@room', {
'data-mention-type': 'at-room',
})
}
>
Add at-room mention
</button>
</>
)}
{shouldDisplayHashMention && (
<button
type="button"
onClick={(_e) =>
wysiwyg.mention(
'https://matrix.to/#/@alice_user:element.io',
'Alice',
'https://matrix.to/#/#my_room:element.io',
'My room',
{
'data-mention-type':
suggestion.keyChar === '@'
? 'user'
: 'room',
'data-mention-type': 'room',
},
)
}
>
Add {suggestion.keyChar}mention
Add Room mention
</button>
)}
{suggestion && suggestion.type === 'command' && (
{commandExists && (
<button
type="button"
onClick={(_e) => wysiwyg.command('/spoiler')}
Expand Down

0 comments on commit 6b3425e

Please sign in to comment.