summaryrefslogtreecommitdiffstats
path: root/webapp/utils/text_formatting.jsx
diff options
context:
space:
mode:
authorGeorge Goldberg <george@gberg.me>2016-09-19 13:21:22 +0100
committerJoram Wilander <jwawilander@gmail.com>2016-09-19 08:21:22 -0400
commit8443ca58289055cde25a3cdaaa3987c6f8cfde4a (patch)
treeec957cd1903e76ba5e62eac710777abd74ee0330 /webapp/utils/text_formatting.jsx
parent781ff323db4c70e4ca476f9ef13a04e5aa063585 (diff)
downloadchat-8443ca58289055cde25a3cdaaa3987c6f8cfde4a.tar.gz
chat-8443ca58289055cde25a3cdaaa3987c6f8cfde4a.tar.bz2
chat-8443ca58289055cde25a3cdaaa3987c6f8cfde4a.zip
PLT-1759 - Auto-complete for !channels when posting messages. (#3890)
* Auto-complete for !channels when posting messages. This is part 1 of the fix for PLT-1759 to make channels linkable. Still to do: - Make the !channels clickable when they appear in messages. This is blocked until PR #3865 is resolved as it looks like that refactors some of the code that would be touched by making this change. - Unit tests. Again, I think the above referenced PR should be merged before tackling this. * Fix style problems. * Highlighting of !channel-names in messages. This only identifies the !channel-name (not the display name). The implementation of the auto-complete on channel names now needs to be modified to convert to the channel handle before sending the message. * Display !channel-name as !Display Name. When we encounter !channel-name in a message, display it as a link using the channel's actual name rather than it's handle (name). * Match on names and display name, and use name. * Autocomplete channels matching on both the name and the the display name. * Use the name as the text we fill in instead of the display name. It's potentially a bit ugly, but it minimises complexity for now as otherwise we'd have to do complicated things to the message box. * Fix style issues. * Load more channels everywhere. Whenever we load the list of channels, we should also load the list of more channels. This is to enable auto-completing and auto-linking of all channels whether or not the user is in them currently. * Include more channels in the map for linking. * Listen for channel list updates for autolinking. * Remove accidental console.log. * Autocomplete on more channels too. * i18n for channel autocomplete. * Link directly to channels in !channel mentions. This currently does not work if you aren't a member of that channel. Need to decide what the correct behaviour is in that case. * Fix style issues. * Show channel name and handle in suggestion. * Match channels only at start or after space. * Better matching in text-formatting. Only match channels after a space-type character or at the start in the posts list too. * Move the route construction to make tests work. Moves route-construction out of text_formatting.jsx and into utils.jsx so that the unit tests work once again.
Diffstat (limited to 'webapp/utils/text_formatting.jsx')
-rw-r--r--webapp/utils/text_formatting.jsx63
1 files changed, 60 insertions, 3 deletions
diff --git a/webapp/utils/text_formatting.jsx b/webapp/utils/text_formatting.jsx
index f97c74625..174620d47 100644
--- a/webapp/utils/text_formatting.jsx
+++ b/webapp/utils/text_formatting.jsx
@@ -13,9 +13,9 @@ import XRegExp from 'xregexp';
// http://stackoverflow.com/questions/15033196/using-javascript-to-check-whether-a-string-contains-japanese-characters-includi
const cjkPattern = /[\u3000-\u303f\u3040-\u309f\u30a0-\u30ff\uff00-\uff9f\u4e00-\u9faf\u3400-\u4dbf]/;
-// Performs formatting of user posts including highlighting mentions and search terms and converting urls, hashtags, and
-// @mentions to links by taking a user's message and returning a string of formatted html. Also takes a number of options
-// as part of the second parameter:
+// Performs formatting of user posts including highlighting mentions and search terms and converting urls, hashtags,
+// @mentions and !channels to links by taking a user's message and returning a string of formatted html. Also takes
+// a number of options as part of the second parameter:
// - searchTerm - If specified, this word is highlighted in the resulting html. Defaults to nothing.
// - mentionHighlight - Specifies whether or not to highlight mentions of the current user. Defaults to true.
// - mentionKeys - A list of mention keys for the current user to highlight.
@@ -26,6 +26,8 @@ const cjkPattern = /[\u3000-\u303f\u3040-\u309f\u30a0-\u30ff\uff00-\uff9f\u4e00-
// links that can be handled by a special click handler.
// - usernameMap - An object mapping usernames to users. If provided, at mentions will be replaced with internal links that can
// be handled by a special click handler (Utils.handleFormattedTextClick)
+// - channelNamesMap - An object mapping channel display names to channels. If provided, !channel mentions will be replaced with
+// links to the relevant channel.
export function formatText(text, inputOptions) {
let output = text;
@@ -61,6 +63,10 @@ export function doFormatText(text, options) {
output = autolinkAtMentions(output, tokens, options.usernameMap);
}
+ if (options.channelNamesMap) {
+ output = autolinkChannelMentions(output, tokens, options.channelNamesMap);
+ }
+
output = autolinkEmails(output, tokens);
output = autolinkHashtags(output, tokens);
@@ -198,6 +204,57 @@ function autolinkAtMentions(text, tokens, usernameMap) {
return output;
}
+function autolinkChannelMentions(text, tokens, channelNamesMap) {
+ function channelMentionExists(c) {
+ return !!channelNamesMap[c];
+ }
+ function addToken(channelName, mention, displayName) {
+ const index = tokens.size;
+ const alias = `MM_CHANNELMENTION${index}`;
+
+ tokens.set(alias, {
+ value: `<a class='mention-link' href='#' data-channel-mention="${channelName}">${displayName}</a>`,
+ originalText: mention
+ });
+ return alias;
+ }
+
+ function replaceChannelMentionWithToken(fullMatch, spacer, mention, channelName) {
+ let channelNameLower = channelName.toLowerCase();
+
+ if (channelMentionExists(channelNameLower)) {
+ // Exact match
+ const alias = addToken(channelNameLower, mention, '!' + channelNamesMap[channelNameLower].display_name);
+ return spacer + alias;
+ }
+
+ // Not an exact match, attempt to truncate any punctuation to see if we can find a channel
+ const originalChannelName = channelNameLower;
+
+ for (let c = channelNameLower.length; c > 0; c--) {
+ if (punctuation.test(channelNameLower[c - 1])) {
+ channelNameLower = channelNameLower.substring(0, c - 1);
+
+ if (channelMentionExists(channelNameLower)) {
+ const suffix = originalChannelName.substr(c - 1);
+ const alias = addToken(channelNameLower, '!' + channelNameLower, '!' + channelNamesMap[channelNameLower].display_name);
+ return spacer + alias + suffix;
+ }
+ } else {
+ // If the last character is not punctuation, no point in going any further
+ break;
+ }
+ }
+
+ return fullMatch;
+ }
+
+ let output = text;
+ output = output.replace(/(^|\s)(!([a-z0-9.\-_]*))/gi, replaceChannelMentionWithToken);
+
+ return output;
+}
+
export function escapeRegex(text) {
return text.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
}