Maintain last-known cursor position for inserting emojis

This commit is contained in:
Ken Powers 2019-05-30 19:35:15 -04:00 committed by Scott Nonnenberg
parent 93335f8759
commit a934759e66
5 changed files with 81 additions and 19 deletions

View file

@ -1,11 +1,15 @@
const { take } = require('lodash');
const { getRecentEmojis } = require('./data');
const { replaceColons } = require('../../ts/components/emoji/lib');
const {
replaceColons,
hasVariation,
} = require('../../ts/components/emoji/lib');
module.exports = {
getInitialState,
load,
replaceColons,
hasVariation,
};
let initialState = null;

View file

@ -278,6 +278,12 @@
this.setupEmojiPickerButton();
this.setupStickerPickerButton();
this.lastSelectionStart = 0;
document.addEventListener(
'selectionchange',
this.updateLastSelectionStart.bind(this, undefined)
);
},
events: {
@ -313,7 +319,17 @@
setupEmojiPickerButton() {
const props = {
onPickEmoji: e => this.insertEmoji(e),
onClose: () => this.focusMessageField(),
onClose: () => {
const textarea = this.$messageField[0];
textarea.focus();
const newPos = textarea.value.length;
textarea.selectionStart = newPos;
textarea.selectionEnd = newPos;
this.forceUpdateLastSelectionStart(newPos);
},
};
this.emojiButtonView = new Whisper.ReactWrapperView({
@ -447,6 +463,10 @@
this.window.removeEventListener('resize', this.onResize);
this.window.removeEventListener('focus', this.onFocus);
document.removeEventListener(
'selectionchange',
this.updateLastSelectionStart
);
window.autosize.destroy(this.$messageField);
@ -1052,6 +1072,7 @@
focusMessageFieldAndClearDisabled() {
this.$messageField.removeAttr('disabled');
this.$messageField.focus();
this.updateLastSelectionStart();
},
async loadMoreMessages() {
@ -1697,24 +1718,31 @@
},
insertEmoji({ shortName, skinTone }) {
const colons = `:${shortName}:${
skinTone ? `:skin-tone-${skinTone}:` : ''
}`;
const skinReplacement = window.Signal.Emojis.hasVariation(
shortName,
skinTone
)
? `:skin-tone-${skinTone}:`
: '';
const colons = `:${shortName}:${skinReplacement}`;
const textarea = this.$messageField[0];
if (textarea.selectionStart || textarea.selectionStart === 0) {
const startPos = textarea.selectionStart;
const endPos = textarea.selectionEnd;
const hasFocus = document.activeElement === textarea;
const startPos = hasFocus
? textarea.selectionStart
: this.lastSelectionStart;
const endPos = hasFocus ? textarea.selectionEnd : this.lastSelectionStart;
textarea.value =
textarea.value.substring(0, startPos) +
colons +
textarea.value.substring(endPos, textarea.value.length);
textarea.selectionStart = startPos + colons.length;
textarea.selectionEnd = startPos + colons.length;
} else {
textarea.value += colons;
}
textarea.value =
textarea.value.substring(0, startPos) +
colons +
textarea.value.substring(endPos, textarea.value.length);
const newPos = startPos + colons.length;
textarea.selectionStart = newPos;
textarea.selectionEnd = newPos;
this.forceUpdateLastSelectionStart(newPos);
this.forceUpdateMessageFieldSize({});
},
async setQuoteMessage(messageId) {
@ -1853,6 +1881,18 @@
this.debouncedMaybeGrabLinkPreview();
},
updateLastSelectionStart(newPos) {
if (document.activeElement === this.$messageField[0]) {
this.forceUpdateLastSelectionStart(newPos);
}
},
forceUpdateLastSelectionStart(
newPos = this.$messageField[0].selectionStart
) {
this.lastSelectionStart = newPos;
},
maybeGrabLinkPreview() {
// Don't generate link previews if user has turned them off
if (!storage.get('linkPreviews', false)) {

View file

@ -292,7 +292,6 @@
color: $color-light-90;
border: 1px solid rgba(0, 0, 0, 0.2);
outline: 0;
z-index: 5;
resize: none;
font-size: 1em;
font-family: inherit;

View file

@ -158,6 +158,25 @@ export function unifiedToEmoji(unified: string) {
.join('');
}
export function hasVariation(shortName: string, skinTone: number = 0) {
if (skinTone === 0) {
return false;
}
const base = dataByShortName[shortName];
if (!base) {
return false;
}
if (skinTone > 0 && base.skin_variations) {
const toneKey = skinTones[skinTone - 1];
return Boolean(base.skin_variations[toneKey]);
}
return false;
}
export function convertShortName(shortName: string, skinTone: number = 0) {
const base = dataByShortName[shortName];

View file

@ -231,7 +231,7 @@
"rule": "jQuery-load(",
"path": "js/modules/emojis.js",
"line": "async function load() {",
"lineNumber": 13,
"lineNumber": 17,
"reasonCategory": "falseMatch",
"updated": "2019-05-23T22:27:53.554Z"
},