fix emoji being inserted into mentions identifier

if the cursor is before the first mention => insert it correctly
if the cursor is after the last mention => insert it correctly
if the cursor is between those two => insert it at the end of the
composition box
pull/1823/head
audric 4 years ago
parent b05910e219
commit 5d6c2d94ff

@ -8,9 +8,9 @@ import { SizeClassType } from '../util/emoji';
import { RenderTextCallbackType } from '../types/Util'; import { RenderTextCallbackType } from '../types/Util';
interface Props { type Props = {
text: string; text: string;
} };
const renderNewLines: RenderTextCallbackType = ({ text, key }) => ( const renderNewLines: RenderTextCallbackType = ({ text, key }) => (
<AddNewLines key={key} text={text} /> <AddNewLines key={key} text={text} />
@ -28,9 +28,8 @@ const renderEmoji = ({
renderNonEmoji: RenderTextCallbackType; renderNonEmoji: RenderTextCallbackType;
}) => <Emojify key={key} text={text} sizeClass={sizeClass} renderNonEmoji={renderNonEmoji} />; }) => <Emojify key={key} text={text} sizeClass={sizeClass} renderNonEmoji={renderNonEmoji} />;
export class MessageBodyHighlight extends React.Component<Props> { export const MessageBodyHighlight = (props: Props) => {
public render() { const { text } = props;
const { text } = this.props;
const results: Array<any> = []; const results: Array<any> = [];
const FIND_BEGIN_END = /<<left>>(.+?)<<right>>/g; const FIND_BEGIN_END = /<<left>>(.+?)<<right>>/g;
@ -86,5 +85,4 @@ export class MessageBodyHighlight extends React.Component<Props> {
} }
return results; return results;
} };
}

@ -43,6 +43,7 @@ export class Emojify extends React.Component<Props> {
while (match) { while (match) {
if (last < match.index) { if (last < match.index) {
const textWithNoEmoji = text.slice(last, match.index); const textWithNoEmoji = text.slice(last, match.index);
results.push( results.push(
renderNonEmoji({ renderNonEmoji({
text: textWithNoEmoji, text: textWithNoEmoji,

@ -860,8 +860,6 @@ class MessageInner extends React.PureComponent<Props, State> {
if (target.className === 'text-selectable' || window.contextMenuShown) { if (target.className === 'text-selectable' || window.contextMenuShown) {
return; return;
} }
event.preventDefault();
event.stopPropagation();
} }
} }

@ -964,6 +964,63 @@ class SessionCompositionBoxInner extends React.Component<Props, State> {
this.setState({ message }); 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 }) { private onEmojiClick({ colons }: { colons: string }) {
const messageBox = this.textarea.current; const messageBox = this.textarea.current;
if (!messageBox) { if (!messageBox) {
@ -973,10 +1030,12 @@ class SessionCompositionBoxInner extends React.Component<Props, State> {
const { message } = this.state; const { message } = this.state;
const currentSelectionStart = Number(messageBox.selectionStart); const currentSelectionStart = Number(messageBox.selectionStart);
const currentSelectionEnd = Number(messageBox.selectionEnd);
const before = message.slice(0, currentSelectionStart); const realSelectionStart = this.getSelectionBasedOnMentions(currentSelectionStart);
const end = message.slice(currentSelectionEnd);
const before = message.slice(0, realSelectionStart);
const end = message.slice(realSelectionStart);
const newMessage = `${before}${colons}${end}`; const newMessage = `${before}${colons}${end}`;
this.setState({ message: newMessage }, () => { this.setState({ message: newMessage }, () => {

@ -3,27 +3,13 @@ import classNames from 'classnames';
import { Picker } from 'emoji-mart'; import { Picker } from 'emoji-mart';
import { Constants } from '../../../session'; import { Constants } from '../../../session';
interface Props { type Props = {
onEmojiClicked: (emoji: any) => void; onEmojiClicked: (emoji: any) => void;
show: boolean; show: boolean;
} };
interface State { export const SessionEmojiPanel = (props: Props) => {
// FIXME Use Emoji-Mart categories const { onEmojiClicked, show } = props;
category: null;
}
export class SessionEmojiPanel extends React.Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = {
category: null,
};
}
public render() {
const { onEmojiClicked, show } = this.props;
return ( return (
<div className={classNames('session-emoji-panel', show && 'show')}> <div className={classNames('session-emoji-panel', show && 'show')}>
@ -40,5 +26,4 @@ export class SessionEmojiPanel extends React.Component<Props, State> {
/> />
</div> </div>
); );
} };
}

Loading…
Cancel
Save