diff --git a/ts/components/MessageBodyHighlight.tsx b/ts/components/MessageBodyHighlight.tsx
index 2bf6e39ec..f42cfe1f8 100644
--- a/ts/components/MessageBodyHighlight.tsx
+++ b/ts/components/MessageBodyHighlight.tsx
@@ -8,9 +8,9 @@ import { SizeClassType } from '../util/emoji';
import { RenderTextCallbackType } from '../types/Util';
-interface Props {
+type Props = {
text: string;
-}
+};
const renderNewLines: RenderTextCallbackType = ({ text, key }) => (
@@ -28,56 +28,27 @@ const renderEmoji = ({
renderNonEmoji: RenderTextCallbackType;
}) => ;
-export class MessageBodyHighlight extends React.Component {
- public render() {
- const { text } = this.props;
- const results: Array = [];
- const FIND_BEGIN_END = /<>(.+?)<>/g;
+export const MessageBodyHighlight = (props: Props) => {
+ const { text } = props;
+ const results: Array = [];
+ const FIND_BEGIN_END = /<>(.+?)<>/g;
- let match = FIND_BEGIN_END.exec(text);
- let last = 0;
- let count = 1;
+ let match = FIND_BEGIN_END.exec(text);
+ let last = 0;
+ let count = 1;
- if (!match) {
- return ;
- }
-
- const sizeClass = '';
-
- while (match) {
- if (last < match.index) {
- const beforeText = text.slice(last, match.index);
- results.push(
- renderEmoji({
- text: beforeText,
- sizeClass,
- key: count++,
- renderNonEmoji: renderNewLines,
- })
- );
- }
-
- const [, toHighlight] = match;
- results.push(
-
- {renderEmoji({
- text: toHighlight,
- sizeClass,
- key: count++,
- renderNonEmoji: renderNewLines,
- })}
-
- );
+ if (!match) {
+ return ;
+ }
- // @ts-ignore
- last = FIND_BEGIN_END.lastIndex;
- match = FIND_BEGIN_END.exec(text);
- }
+ const sizeClass = '';
- if (last < text.length) {
+ while (match) {
+ if (last < match.index) {
+ const beforeText = text.slice(last, match.index);
results.push(
renderEmoji({
- text: text.slice(last),
+ text: beforeText,
sizeClass,
key: count++,
renderNonEmoji: renderNewLines,
@@ -85,6 +56,33 @@ export class MessageBodyHighlight extends React.Component {
);
}
- return results;
+ const [, toHighlight] = match;
+ results.push(
+
+ {renderEmoji({
+ text: toHighlight,
+ sizeClass,
+ key: count++,
+ renderNonEmoji: renderNewLines,
+ })}
+
+ );
+
+ // @ts-ignore
+ last = FIND_BEGIN_END.lastIndex;
+ match = FIND_BEGIN_END.exec(text);
}
-}
+
+ if (last < text.length) {
+ results.push(
+ renderEmoji({
+ text: text.slice(last),
+ sizeClass,
+ key: count++,
+ renderNonEmoji: renderNewLines,
+ })
+ );
+ }
+
+ return results;
+};
diff --git a/ts/components/conversation/Emojify.tsx b/ts/components/conversation/Emojify.tsx
index 9fa41a70d..5a83619c9 100644
--- a/ts/components/conversation/Emojify.tsx
+++ b/ts/components/conversation/Emojify.tsx
@@ -43,6 +43,7 @@ export class Emojify extends React.Component {
while (match) {
if (last < match.index) {
const textWithNoEmoji = text.slice(last, match.index);
+
results.push(
renderNonEmoji({
text: textWithNoEmoji,
diff --git a/ts/components/conversation/Message.tsx b/ts/components/conversation/Message.tsx
index a60dfc8df..221a0e7b7 100644
--- a/ts/components/conversation/Message.tsx
+++ b/ts/components/conversation/Message.tsx
@@ -860,8 +860,6 @@ class MessageInner extends React.PureComponent {
if (target.className === 'text-selectable' || window.contextMenuShown) {
return;
}
- event.preventDefault();
- event.stopPropagation();
}
}
diff --git a/ts/components/session/conversation/SessionCompositionBox.tsx b/ts/components/session/conversation/SessionCompositionBox.tsx
index d831f2c5b..4465b2208 100644
--- a/ts/components/session/conversation/SessionCompositionBox.tsx
+++ b/ts/components/session/conversation/SessionCompositionBox.tsx
@@ -964,6 +964,63 @@ class SessionCompositionBoxInner extends React.Component {
this.setState({ message });
}
+ private getSelectionBasedOnMentions(index: number) {
+ // we have to get the real selectionStart/end of an index in the mentions box.
+ // this is kind of a pain as the mentions box has two inputs, one with the real text, and one with the extracted mentions
+
+ // the index shown to the user is actually just the visible part of the mentions (so the part between ᅲ...ᅭ
+ const matches = this.state.message.match(this.mentionsRegex);
+
+ let lastMatchStartIndex = 0;
+ let lastMatchEndIndex = 0;
+ let lastRealMatchEndIndex = 0;
+
+ if (!matches) {
+ return index;
+ }
+ const mapStartToLengthOfMatches = matches.map(match => {
+ const displayNameStart = match.indexOf('\uFFD7') + 1;
+ const displayNameEnd = match.lastIndexOf('\uFFD2');
+ const displayName = match.substring(displayNameStart, displayNameEnd);
+
+ const currentMatchStartIndex = this.state.message.indexOf(match) + lastMatchStartIndex;
+ lastMatchStartIndex = currentMatchStartIndex;
+ lastMatchEndIndex = currentMatchStartIndex + match.length;
+
+ const realLength = displayName.length + 1;
+ lastRealMatchEndIndex = lastRealMatchEndIndex + realLength;
+
+ // the +1 is for the @
+ return {
+ length: displayName.length + 1,
+ lastRealMatchEndIndex,
+ start: lastMatchStartIndex,
+ end: lastMatchEndIndex,
+ };
+ });
+
+ const beforeFirstMatch = index < mapStartToLengthOfMatches[0].start;
+ if (beforeFirstMatch) {
+ // those first char are always just char, so the mentions logic does not come into account
+ return index;
+ }
+ const lastMatchMap = _.last(mapStartToLengthOfMatches);
+
+ if (!lastMatchMap) {
+ return Number.MAX_SAFE_INTEGER;
+ }
+
+ const indexIsAfterEndOfLastMatch = lastMatchMap.lastRealMatchEndIndex <= index;
+ if (indexIsAfterEndOfLastMatch) {
+ const lastEnd = lastMatchMap.end;
+ const diffBetweenEndAndLastRealEnd = index - lastMatchMap.lastRealMatchEndIndex;
+ return lastEnd + diffBetweenEndAndLastRealEnd - 1;
+ }
+ // now this is the hard part, the cursor is currently between the end of the first match and the start of the last match
+ // for now, just append it to the end
+ return Number.MAX_SAFE_INTEGER;
+ }
+
private onEmojiClick({ colons }: { colons: string }) {
const messageBox = this.textarea.current;
if (!messageBox) {
@@ -973,10 +1030,12 @@ class SessionCompositionBoxInner extends React.Component {
const { message } = this.state;
const currentSelectionStart = Number(messageBox.selectionStart);
- const currentSelectionEnd = Number(messageBox.selectionEnd);
- const before = message.slice(0, currentSelectionStart);
- const end = message.slice(currentSelectionEnd);
+ const realSelectionStart = this.getSelectionBasedOnMentions(currentSelectionStart);
+
+ const before = message.slice(0, realSelectionStart);
+ const end = message.slice(realSelectionStart);
+
const newMessage = `${before}${colons}${end}`;
this.setState({ message: newMessage }, () => {
diff --git a/ts/components/session/conversation/SessionEmojiPanel.tsx b/ts/components/session/conversation/SessionEmojiPanel.tsx
index 9657c15ca..b9b174712 100644
--- a/ts/components/session/conversation/SessionEmojiPanel.tsx
+++ b/ts/components/session/conversation/SessionEmojiPanel.tsx
@@ -3,42 +3,27 @@ import classNames from 'classnames';
import { Picker } from 'emoji-mart';
import { Constants } from '../../../session';
-interface Props {
+type Props = {
onEmojiClicked: (emoji: any) => void;
show: boolean;
-}
+};
-interface State {
- // FIXME Use Emoji-Mart categories
- category: null;
-}
+export const SessionEmojiPanel = (props: Props) => {
+ const { onEmojiClicked, show } = props;
-export class SessionEmojiPanel extends React.Component {
- constructor(props: Props) {
- super(props);
-
- this.state = {
- category: null,
- };
- }
-
- public render() {
- const { onEmojiClicked, show } = this.props;
-
- return (
-
-
'./images/emoji/emoji-sheet-twitter-32.png'}
- set={'twitter'}
- sheetSize={32}
- darkMode={true}
- color={Constants.UI.COLORS.GREEN}
- showPreview={true}
- title={''}
- onSelect={onEmojiClicked}
- autoFocus={true}
- />
-
- );
- }
-}
+ return (
+
+
'./images/emoji/emoji-sheet-twitter-32.png'}
+ set={'twitter'}
+ sheetSize={32}
+ darkMode={true}
+ color={Constants.UI.COLORS.GREEN}
+ showPreview={true}
+ title={''}
+ onSelect={onEmojiClicked}
+ autoFocus={true}
+ />
+
+ );
+};