From 092685db5a9a5c12d0941d988a69dc954a6022da Mon Sep 17 00:00:00 2001 From: Igor Zhukov Date: Wed, 10 May 2017 01:28:13 +0300 Subject: [PATCH] Supported sticker suggestions by emoji Also fixed blinking suggestions list Closes #1409 Closes #1332 Closes #814 --- app/js/controllers.js | 57 +++++++++++++++++++++++++++- app/js/lib/ng_utils.js | 11 ++---- app/js/message_composer.js | 37 +++++++++++++++---- app/js/services.js | 76 ++++++++++++++++++++++++++++++++++++-- 4 files changed, 160 insertions(+), 21 deletions(-) diff --git a/app/js/controllers.js b/app/js/controllers.js index b67e21c5..dbc54916 100644 --- a/app/js/controllers.js +++ b/app/js/controllers.js @@ -2288,7 +2288,7 @@ angular.module('myApp.controllers', ['myApp.i18n']) $scope.$on('user_update', angular.noop) }) - .controller('AppImSendController', function ($rootScope, $q, $scope, $timeout, MtpApiManager, Storage, AppProfileManager, AppChatsManager, AppUsersManager, AppPeersManager, AppDocsManager, AppMessagesManager, AppInlineBotsManager, MtpApiFileManager, DraftsManager, RichTextProcessor) { + .controller('AppImSendController', function ($rootScope, $q, $scope, $timeout, MtpApiManager, Storage, AppProfileManager, AppChatsManager, AppUsersManager, AppPeersManager, AppDocsManager, AppStickersManager, AppMessagesManager, AppInlineBotsManager, MtpApiFileManager, DraftsManager, RichTextProcessor) { $scope.$watch('curDialog.peer', resetDraft) $scope.$on('user_update', angular.noop) $scope.$on('peer_draft_attachment', applyDraftAttachment) @@ -2793,6 +2793,7 @@ angular.module('myApp.controllers', ['myApp.i18n']) } var inlineUsernameRegex = /^@([a-zA-Z\d_]{1,32})( | )([\s\S]*)$/ + var inlineStickersEmojiRegex = /^\s*:(\S+):\s*$/ var getInlineResultsTO = false var lastInlineBot = false var jump = 0 @@ -2809,6 +2810,39 @@ angular.module('myApp.controllers', ['myApp.i18n']) } var matches = message.match(inlineUsernameRegex) if (!matches) { + matches = message.match(inlineStickersEmojiRegex) + if (matches) { + var emojiCode = EmojiHelper.shortcuts[matches[1]] + if (emojiCode) { + $scope.draftMessage.inlineProgress = true + AppStickersManager.searchStickers(emojiCode).then(function (docs) { + var inlineResults = [] + angular.forEach(docs, function (doc) { + inlineResults.push({ + _: 'botInlineMediaResult', + qID: '_sticker_' + doc.id, + pFlags: {sticker: true}, + id: doc.id, + type: 'sticker', + document: doc, + send_message: {_: 'botInlineMessageMediaAuto'} + }) + }) + var botResults = { + pFlags: {gallery: true}, + query_id: 0, + results: inlineResults + } + botResults.text = message + $scope.$broadcast('inline_results', botResults) + delete $scope.draftMessage.inlineProgress + }) + } else { + delete $scope.draftMessage.inlineProgress + $scope.$broadcast('inline_results', false) + return + } + } delete $scope.draftMessage.inlineProgress $scope.$broadcast('inline_results', false) return @@ -2954,7 +2988,26 @@ angular.module('myApp.controllers', ['myApp.i18n']) replyToMsgID: $scope.draftMessage.replyToMsgID, clearDraft: true } - AppInlineBotsManager.sendInlineResult($scope.curDialog.peerID, qID, options) + + if (qID.substr(0, 9) == '_sticker_') { + var docID = qID.substr(9) + var doc = AppDocsManager.getDoc(docID) + if (doc.id && doc.access_hash) { + var inputMedia = { + _: 'inputMediaDocument', + id: { + _: 'inputDocument', + id: doc.id, + access_hash: doc.access_hash + } + } + AppMessagesManager.sendOther($scope.curDialog.peerID, inputMedia, options) + } + } + else { + AppInlineBotsManager.sendInlineResult($scope.curDialog.peerID, qID, options) + } + if (forceDraft == $scope.curDialog.peer) { forceDraft = false diff --git a/app/js/lib/ng_utils.js b/app/js/lib/ng_utils.js index 8721b69a..b03de9d8 100755 --- a/app/js/lib/ng_utils.js +++ b/app/js/lib/ng_utils.js @@ -1160,7 +1160,6 @@ angular.module('izhukov.utils', []) }) .service('RichTextProcessor', function ($sce, $sanitize) { - var emojiMap = {} var emojiData = Config.Emoji var emojiIconSize = 18 var emojiSupported = navigator.userAgent.search(/OS X|iPhone|iPad|iOS|Android/i) != -1, @@ -1168,10 +1167,6 @@ angular.module('izhukov.utils', []) var emojiRegExp = '\\u0023\\u20E3|\\u00a9|\\u00ae|\\u203c|\\u2049|\\u2139|[\\u2194-\\u2199]|\\u21a9|\\u21aa|\\u231a|\\u231b|\\u23e9|[\\u23ea-\\u23ec]|\\u23f0|\\u24c2|\\u25aa|\\u25ab|\\u25b6|\\u2611|\\u2614|\\u26fd|\\u2705|\\u2709|[\\u2795-\\u2797]|\\u27a1|\\u27b0|\\u27bf|\\u2934|\\u2935|[\\u2b05-\\u2b07]|\\u2b1b|\\u2b1c|\\u2b50|\\u2b55|\\u3030|\\u303d|\\u3297|\\u3299|[\\uE000-\\uF8FF\\u270A-\\u2764\\u2122\\u25C0\\u25FB-\\u25FE\\u2615\\u263a\\u2648-\\u2653\\u2660-\\u2668\\u267B\\u267F\\u2693\\u261d\\u26A0-\\u26FA\\u2708\\u2702\\u2601\\u260E]|[\\u2600\\u26C4\\u26BE\\u23F3\\u2764]|\\uD83D[\\uDC00-\\uDFFF]|\\uD83C[\\uDDE8-\\uDDFA\uDDEC]\\uD83C[\\uDDEA-\\uDDFA\uDDE7]|[0-9]\\u20e3|\\uD83C[\\uDC00-\\uDFFF]' - for (emojiCode in emojiData) { - emojiMap[emojiData[emojiCode][0]] = emojiCode - } - var alphaCharsRegExp = 'a-z' + '\\u00c0-\\u00d6\\u00d8-\\u00f6\\u00f8-\\u00ff' + // Latin-1 '\\u0100-\\u024f' + // Latin Extended A and B @@ -1363,8 +1358,8 @@ angular.module('izhukov.utils', []) }) } else if (match[8]) { // Emoji - if ((emojiCode = emojiMap[match[8]]) && - (emojiCoords = getEmojiSpritesheetCoords(emojiCode))) { + if ((emojiCode = EmojiHelper.emojiMap[match[8]]) && + (emojiCoords = getEmojiSpritesheetCoords(emojiCode))) { entities.push({ _: 'messageEntityEmoji', offset: matchIndex, @@ -1906,7 +1901,7 @@ angular.module('izhukov.utils', []) text.push(raw.substr(0, match.index)) if (match[8]) { - if ((emojiCode = emojiMap[match[8]]) && + if ((emojiCode = EmojiHelper.emojiMap[match[8]]) && (emojiTitle = emojiData[emojiCode][1][0])) { text.push(':' + emojiTitle + ':') } else { diff --git a/app/js/message_composer.js b/app/js/message_composer.js index b67c35f7..00e4dd27 100644 --- a/app/js/message_composer.js +++ b/app/js/message_composer.js @@ -9,9 +9,10 @@ /* EmojiHelper */ -;(function (global, emojis, categories, spritesheets) { +;(function (global, emojiData, categories, spritesheets) { var emojis = {} var shortcuts = {} + var emojiMap = {} var spritesheetPositions = {} var index = false @@ -32,7 +33,7 @@ totalColumns = spritesheets[i][1] for (j = 0, len2 = categories[i].length; j < len2; j++) { code = categories[i][j] - emoji = Config.Emoji[code] + emoji = emojiData[code] shortcut = emoji[1][0] emojis[code] = [emoji[0], shortcut] shortcuts[shortcut] = code @@ -40,6 +41,10 @@ } } + angular.forEach(emojiData, function (emoji, emojiCode) { + emojiMap[emoji[0]] = emojiCode + }) + function getPopularEmoji (callback) { ConfigStorage.get('emojis_popular', function (popEmojis) { var result = [] @@ -126,6 +131,7 @@ global.EmojiHelper = { emojis: emojis, + emojiMap: emojiMap, shortcuts: shortcuts, spritesheetPositions: spritesheetPositions, getPopularEmoji: getPopularEmoji, @@ -715,6 +721,7 @@ function MessageComposer (textarea, options) { this.onInlineResultSend = options.onInlineResultSend this.mentions = options.mentions this.commands = options.commands + this.renderToggleCnt = 0 } MessageComposer.autoCompleteRegEx = /(\s|^)(:|@|\/)([\S]*)$/ @@ -1096,6 +1103,9 @@ MessageComposer.prototype.checkAutocomplete = function (forceFull) { } } else if (matches[2] == ':') { // emoji + if (value.match(/^\s*:(.+):\s*$/)) { + return + } EmojiHelper.getPopularEmoji((function (popular) { if (query.length) { var found = EmojiHelper.searchEmojis(query) @@ -1497,6 +1507,7 @@ MessageComposer.prototype.renderSuggestions = function () { } MessageComposer.prototype.showEmojiSuggestions = function (codes) { + var renderCnt = ++this.renderToggleCnt var self = this setZeroTimeout(function () { self.autoCompleteScope.$apply(function () { @@ -1504,12 +1515,15 @@ MessageComposer.prototype.showEmojiSuggestions = function (codes) { self.autoCompleteScope.emojiCodes = codes }) onContentLoaded(function () { - self.renderSuggestions() + if (renderCnt == self.renderToggleCnt) { + self.renderSuggestions() + } }) }) } MessageComposer.prototype.showMentionSuggestions = function (users) { + var renderCnt = ++this.renderToggleCnt var self = this setZeroTimeout(function () { self.autoCompleteScope.$apply(function () { @@ -1517,12 +1531,15 @@ MessageComposer.prototype.showMentionSuggestions = function (users) { self.autoCompleteScope.mentionUsers = users }) onContentLoaded(function () { - self.renderSuggestions() + if (renderCnt == self.renderToggleCnt) { + self.renderSuggestions() + } }) }) } MessageComposer.prototype.showCommandsSuggestions = function (commands) { + var renderCnt = ++this.renderToggleCnt var self = this setZeroTimeout(function () { self.autoCompleteScope.$apply(function () { @@ -1530,7 +1547,9 @@ MessageComposer.prototype.showCommandsSuggestions = function (commands) { self.autoCompleteScope.commands = commands }) onContentLoaded(function () { - self.renderSuggestions() + if (renderCnt == self.renderToggleCnt) { + self.renderSuggestions() + } }) }) } @@ -1540,6 +1559,7 @@ MessageComposer.prototype.showInlineSuggestions = function (botResults) { this.hideSuggestions() return } + var renderCnt = ++this.renderToggleCnt var self = this if (self.autoCompleteScope.type == 'inline' && self.autoCompleteScope.botResults == botResults && @@ -1552,7 +1572,9 @@ MessageComposer.prototype.showInlineSuggestions = function (botResults) { self.autoCompleteScope.botResults = botResults }) onContentLoaded(function () { - self.renderSuggestions() + if (renderCnt == self.renderToggleCnt) { + self.renderSuggestions() + } }) }) } @@ -1575,7 +1597,8 @@ MessageComposer.prototype.updatePosition = function () { } MessageComposer.prototype.hideSuggestions = function () { - // console.trace() + var renderCnt = ++this.renderToggleCnt + // console.trace(dT()) // return this.autoCompleteWrapEl.hide() delete this.autocompleteShown diff --git a/app/js/services.js b/app/js/services.js index e2325763..c1b1af48 100755 --- a/app/js/services.js +++ b/app/js/services.js @@ -2209,6 +2209,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) var started = false var applied = false var currentStickerSets = [] + var emojiIndex = {} $rootScope.$on('apiUpdate', function (e, update) { if (update._ != 'updateStickerSets' && @@ -2220,7 +2221,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) return Storage.get('all_stickers').then(function (stickers) { if (!stickers || - stickers.layer != Config.Schema.API.layer) { + stickers.layer != Config.Schema.API.layer) { $rootScope.$broadcast('stickers_changed') } switch (update._) { @@ -2243,6 +2244,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) set.pFlags.installed = true stickers.sets.unshift(set) stickers.fullSets[set.id] = fullSet + indexStickerSetEmoticons(fullSet) break case 'updateDelStickerSet': @@ -2283,6 +2285,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) openStickerset: openStickerset, installStickerset: installStickerset, pushPopularSticker: pushPopularSticker, + searchStickers: searchStickers, getStickerset: getStickerset } @@ -2296,10 +2299,12 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) function getStickers (force) { return Storage.get('all_stickers').then(function (stickers) { var layer = Config.Schema.API.layer - if (stickers.layer != layer) { + if (stickers.layer != layer || + stickers.emojiIndex === undefined) { stickers = false } if (stickers && stickers.date > tsNow(true) && !force) { + emojiIndex = stickers.emojiIndex return processRawStickers(stickers) } return MtpApiManager.invokeApi('messages.getAllStickers', { @@ -2315,6 +2320,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) if (notModified) { Storage.set({all_stickers: newStickers}) + emojiIndex = newStickers.emojiIndex return processRawStickers(newStickers) } @@ -2376,14 +2382,74 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) }) } + function indexStickerSetEmoticons(fullSet) { + angular.forEach(fullSet.packs, function (pack) { + var emoji = pack.emoticon + var emojiCode = false + while (emoji.length) { + emojiCode = EmojiHelper.emojiMap[emoji] + if (emojiCode !== undefined) { + break + } + emoji = emoji.substr(0, -1) + } + // console.warn('index', fullSet, pack, emojiCode) + if (emojiCode === undefined) { + return + } + var stickersList = emojiIndex[emojiCode] + if (stickersList === undefined) { + emojiIndex[emojiCode] = stickersList = [] + } + angular.forEach(pack.documents, function (docID) { + if (stickersList.indexOf(docID) === -1) { + stickersList.push(docID) + } + }) + }) + } + + function searchStickers(emojiCode) { + return getPopularStickers().then(function () { + // console.warn('search', emojiCode, emojiIndex, emojiIndex[emojiCode]) + var stickersList = emojiIndex[emojiCode] + var result = [] + if (stickersList === undefined) { + return result + } + var setIDs = [] + angular.forEach(currentStickerSets, function (set) { + setIDs.push(set.id) + }) + angular.forEach(stickersList, function (docID) { + var doc = AppDocsManager.getDoc(docID) + if (!doc || !doc.stickerSetInput) { + return + } + var setID = doc.stickerSetInput.id + if (setIDs.indexOf(setID) == -1) { + return + } + result.push(doc) + }) + result.sort(function (doc1, doc2) { + return setIDs.indexOf(doc1.stickerSetInput.id) - setIDs.indexOf(doc2.stickerSetInput.id) + }) + return result + }) + + } + function getStickerSets (allStickers, prevCachedSets) { var promises = [] var cachedSets = prevCachedSets || allStickers.fullSets || {} allStickers.fullSets = {} + emojiIndex = allStickers.emojiIndex = {} angular.forEach(allStickers.sets, function (shortSet) { var fullSet = cachedSets[shortSet.id] if (fullSet && fullSet.set.hash == shortSet.hash) { allStickers.fullSets[shortSet.id] = fullSet + indexStickerSetEmoticons(fullSet) } else { var promise = MtpApiManager.invokeApi('messages.getStickerSet', { stickerset: { @@ -2393,6 +2459,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) } }).then(function (fullSet) { allStickers.fullSets[shortSet.id] = fullSet + indexStickerSetEmoticons(fullSet) }) promises.push(promise) } @@ -2653,8 +2720,8 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) function regroupWrappedResults (results, rowW, rowH) { if (!results || - !results[0] || - results[0].type != 'photo' && results[0].type != 'gif' && results[0].type != 'sticker') { + !results[0] || + ['photo', 'gif', 'sticker'].indexOf(results[0].type) == -1) { return } var ratios = [] @@ -4768,6 +4835,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) // console.warn(dT(), 'server', draft) } else { // console.warn(dT(), 'local', draft) + console.warn(dT(), 'local', draft) } var replyToMsgID = draft && draft.replyToMsgID if (replyToMsgID) {