From fa3e97c6a426858bed4747cecc67206305ed1df4 Mon Sep 17 00:00:00 2001 From: Igor Zhukov Date: Thu, 2 Jul 2015 18:48:52 +0300 Subject: [PATCH] Commands autocomplete draft --- app/js/controllers.js | 47 ++++++++++++++++- app/js/directives.js | 8 +-- app/js/message_composer.js | 99 +++++++++++++++++++++++++++++++----- app/js/services.js | 56 ++++++++++---------- app/less/app.less | 50 +++++++++++++++++- app/less/desktop.less | 10 +++- app/partials/desktop/im.html | 2 +- 7 files changed, 220 insertions(+), 52 deletions(-) diff --git a/app/js/controllers.js b/app/js/controllers.js index c3bba611..5838bb36 100644 --- a/app/js/controllers.js +++ b/app/js/controllers.js @@ -1746,7 +1746,7 @@ angular.module('myApp.controllers', ['myApp.i18n']) $scope.$on('user_update', angular.noop); }) - .controller('AppImSendController', function ($scope, $timeout, MtpApiManager, Storage, AppChatsManager, AppUsersManager, AppPeersManager, AppDocsManager, AppMessagesManager, MtpApiFileManager) { + .controller('AppImSendController', function ($scope, $timeout, MtpApiManager, Storage, AppProfileManager, AppChatsManager, AppUsersManager, AppPeersManager, AppDocsManager, AppMessagesManager, MtpApiFileManager) { $scope.$watch('curDialog.peer', resetDraft); $scope.$on('user_update', angular.noop); @@ -1757,6 +1757,7 @@ angular.module('myApp.controllers', ['myApp.i18n']) $scope.draftMessage = {text: '', send: sendMessage, replyClear: replyClear}; $scope.mentions = {}; + $scope.commands = {}; $scope.$watch('draftMessage.text', onMessageChange); $scope.$watch('draftMessage.files', onFilesSelected); $scope.$watch('draftMessage.sticker', onStickerSelected); @@ -1831,8 +1832,52 @@ angular.module('myApp.controllers', ['myApp.i18n']) }); } + function updateCommands () { + var peerID = $scope.curDialog.peerID; + + AppProfileManager.getPeerBots(peerID).then(function (peerBots) { + if (!peerBots.length) { + safeReplaceObject($scope.commands, {}); + $scope.$broadcast('mentions_update'); + return; + } + + var needMentions = peerBots.length > 1; + var commandsList = []; + var commandsIndex = SearchIndexManager.createIndex(); + + angular.forEach(peerBots, function (peerBot) { + var mention = ''; + if (needMentions) { + var bot = AppUsersManager.getUser(peerBot.id); + if (bot && bot.username) { + mention += '@' + bot.username; + } + } + var botSearchText = AppUsersManager.getUserSearchText(peerBot.id); + angular.forEach(peerBot.commands, function (description, command) { + var value = '/' + command + mention; + commandsList.push({ + botID: peerBot.id, + value: value, + description: description + }); + SearchIndexManager.indexObject(value, botSearchText + ' ' + command + ' ' + description, commandsIndex); + }) + }); + + console.log(commandsList, commandsIndex); + safeReplaceObject($scope.commands, { + list: commandsList, + index: commandsIndex + }); + $scope.$broadcast('mentions_update'); + }); + } + function resetDraft (newPeer) { updateMentions(); + updateCommands(); replyClear(); if (newPeer) { diff --git a/app/js/directives.js b/app/js/directives.js index d16e3caa..6677f9ab 100755 --- a/app/js/directives.js +++ b/app/js/directives.js @@ -1295,7 +1295,8 @@ angular.module('myApp.directives', ['myApp.filters']) link: link, scope: { draftMessage: '=', - mentions: '=' + mentions: '=', + commands: '=' } }; @@ -1363,8 +1364,8 @@ angular.module('myApp.directives', ['myApp.filters']) getSendOnEnter: function () { return sendOnEnter; }, - getPeerImage: function (element, peerID) { - if (cachedPeerPhotos[peerID]) { + getPeerImage: function (element, peerID, noReplace) { + if (cachedPeerPhotos[peerID] && !noReplace) { element.replaceWith(cachedPeerPhotos[peerID]); return; } @@ -1376,6 +1377,7 @@ angular.module('myApp.directives', ['myApp.filters']) }); }, mentions: $scope.mentions, + commands: $scope.commands, onMessageSubmit: onMessageSubmit, onFilePaste: onFilePaste }); diff --git a/app/js/message_composer.js b/app/js/message_composer.js index e1e9d2a1..d9242a00 100644 --- a/app/js/message_composer.js +++ b/app/js/message_composer.js @@ -487,7 +487,13 @@ function MessageComposer (textarea, options) { this.setUpInput(); - this.autoCompleteEl = $('').appendTo(document.body); + this.autoCompleteWrapEl = $('
').appendTo(document.body); + this.autoCompleteScrollerEl = $('
').appendTo(this.autoCompleteWrapEl); + this.autoCompleteEl = $('').appendTo(this.autoCompleteScrollerEl); + + if (!Config.Mobile) { + this.autoCompleteScrollerEl.nanoScroller({preventPageScrolling: true, tabIndex: -1}); + } var self = this; this.autoCompleteEl.on('mousedown', function (e) { @@ -517,6 +523,7 @@ function MessageComposer (textarea, options) { this.getSendOnEnter = options.getSendOnEnter; this.onFilePaste = options.onFilePaste; this.mentions = options.mentions; + this.commands = options.commands; this.getPeerImage = options.getPeerImage; } @@ -526,7 +533,7 @@ MessageComposer.prototype.setUpInput = function () { } else { this.setUpPlaintext(); } - this.autoCompleteRegEx = /(?:\s|^)(:|@)([A-Za-z0-9\-\+\*_]*)$/; + this.autoCompleteRegEx = /(?:\s|^)(:|@|\/)([A-Za-z0-9\-\+\*@_]*)$/; } MessageComposer.prototype.setUpRich = function () { @@ -602,12 +609,18 @@ MessageComposer.prototype.onKeyEvent = function (e) { currentSelected.removeClass('composer_autocomplete_option_active'); if (nextWrap) { $(nextWrap).find('a').addClass('composer_autocomplete_option_active'); + if (!Config.Mobile) { + this.autoCompleteScrollerEl.nanoScroller({scrollTop: nextWrap.offsetTop}) + } return cancelEvent(e); } } var childNodes = this.autoCompleteEl[0].childNodes; var nextWrap = childNodes[next ? 0 : childNodes.length - 1]; + if (!Config.Mobile) { + this.autoCompleteScrollerEl.nanoScroller({scrollTop: nextWrap.offsetTop}) + } $(nextWrap).find('a').addClass('composer_autocomplete_option_active'); return cancelEvent(e); @@ -742,6 +755,30 @@ MessageComposer.prototype.checkAutocomplete = function () { this.hideSuggestions(); } } + else if (matches[1] == '/') { // commands + if (this.commands && this.commands.index) { + if (query.length) { + var foundObject = SearchIndexManager.search(query, this.commands.index); + var foundCommands = []; + var command; + for (var i = 0, length = this.commands.list.length; i < length; i++) { + command = this.commands.list[i]; + if (foundObject[command.value]) { + foundCommands.push(command); + } + } + } else { + var foundCommands = this.commands.list; + } + if (foundCommands.length) { + this.showCommandsSuggestions(foundCommands); + } else { + this.hideSuggestions(); + } + } else { + this.hideSuggestions(); + } + } else { // emoji EmojiHelper.getPopularEmoji((function (popular) { if (query.length) { @@ -1028,6 +1065,21 @@ MessageComposer.prototype.focus = function () { } } +MessageComposer.prototype.renderSuggestions = function (html) { + this.autoCompleteEl.html(html.join('')); + this.autoCompleteWrapEl.show(); + + var self = this; + if (!Config.Mobile) { + self.autoCompleteScrollerEl.nanoScroller({scroll: 'top'}); + setTimeout(function () { + self.autoCompleteScrollerEl.nanoScroller(); + }, 100); + } + + this.updatePosition(); + this.autocompleteShown = true; +} MessageComposer.prototype.showEmojiSuggestions = function (codes) { var html = []; @@ -1052,10 +1104,7 @@ MessageComposer.prototype.showEmojiSuggestions = function (codes) { } } - this.autoCompleteEl.html(html.join('')); - this.autoCompleteEl.show(); - this.updatePosition(); - this.autocompleteShown = true; + this.renderSuggestions(html); } MessageComposer.prototype.showMentionSuggestions = function (users) { @@ -1069,27 +1118,49 @@ MessageComposer.prototype.showMentionSuggestions = function (users) { html.push('
  • ' + user.rFullName + '@' + user.username + '
  • '); } - this.autoCompleteEl.html(html.join('')); - + this.renderSuggestions(html); var self = this; this.autoCompleteEl.find('.composer_user_photo').each(function (k, element) { self.getPeerImage($(element), element.getAttribute('data-user-id')); }); +} - this.autoCompleteEl.show(); - this.updatePosition(); - this.autocompleteShown = true; +MessageComposer.prototype.showCommandsSuggestions = function (commands) { + var html = []; + var command; + var count = Math.min(5, commands.length); + var i; + + for (i = 0; i < count; i++) { + command = commands[i]; + html.push('
  • ' + command.value + '' + command.description + '
  • '); + } + + this.renderSuggestions(html); + + var self = this; + var usedImages = {}; + this.autoCompleteEl.find('.composer_user_photo').each(function (k, element) { + var noReplace = true; + var botID = element.getAttribute('data-user-id'); + if (!usedImages[botID]) { + usedImages[botID] = true; + noReplace = false; + } + self.getPeerImage($(element), botID, noReplace); + }); } MessageComposer.prototype.updatePosition = function () { var offset = (this.richTextareaEl || this.textareaEl).offset(); - var height = this.autoCompleteEl.outerHeight(); + var height = this.autoCompleteWrapEl.outerHeight(); var width = (this.richTextareaEl || this.textareaEl).outerWidth(); - this.autoCompleteEl.css({top: offset.top - height, left: offset.left, width: width - 2}); + this.autoCompleteWrapEl.css({top: offset.top - height, left: offset.left, width: width - 2}); } MessageComposer.prototype.hideSuggestions = function () { - this.autoCompleteEl.hide(); + return; + this.autoCompleteWrapEl.hide(); delete this.autocompleteShown; } diff --git a/app/js/services.js b/app/js/services.js index 360a2748..3ae51bf9 100755 --- a/app/js/services.js +++ b/app/js/services.js @@ -837,7 +837,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) } }) -.service('AppProfileManager', function (AppBotsManager, AppUsersManager, AppPhotosManager, NotificationsManager, MtpApiManager, RichTextProcessor) { +.service('AppProfileManager', function ($q, AppUsersManager, AppChatsManager, AppPhotosManager, NotificationsManager, MtpApiManager, RichTextProcessor) { var botInfos = {}; @@ -851,6 +851,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) commands[botCommand.command] = botCommand.description; }) return botInfos[botID] = { + id: botID, version: botInfo.version, shareText: botInfo.share_text, description: botInfo.description, @@ -884,40 +885,35 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) }); } - return { - getProfile: getProfile - } - -}) - -.service('AppBotsManager', function (AppUsersManager, AppChatsManager) { - - return { - getPeerBots: function (peerID) { - var peerBots = []; - if (peerID > 0) { - var user = AppUsersManager.getUser(peerID); - if (!user.pFlags.bot) { - return $q.when(peerBots); - } - return AppUsersManager.getUserFull(peerID).then(function (userFull) { - var botInfo = userFull.bot_info; - if (botInfo && botInfo._ != 'botInfoEmpty') { - peerBots.push(botInfo); - } - return peerBots; - }); + function getPeerBots (peerID) { + var peerBots = []; + if (peerID >= 0) { + if (!AppUsersManager.isBot(peerID)) { + return $q.when(peerBots); } - - return AppChatsManager.getFullChat(-peerID).then(function (chatFull) { - angular.forEach(chatFull.bot_info, function (botInfo) { + return getProfile(peerID).then(function (userFull) { + var botInfo = userFull.bot_info; + if (botInfo && botInfo._ != 'botInfoEmpty') { peerBots.push(botInfo); - }); + } return peerBots; }); - } - }; + + return AppChatsManager.getChatFull(-peerID).then(function (chatFull) { + angular.forEach(chatFull.bot_info, function (botInfo) { + peerBots.push(saveBotInfo(botInfo)); + }); + return peerBots; + }); + + } + + return { + getProfile: getProfile, + getPeerBots: getPeerBots + } + }) .service('AppMessagesManager', function ($q, $rootScope, $location, $filter, $timeout, $sce, ApiUpdatesManager, AppUsersManager, AppChatsManager, AppPeersManager, AppPhotosManager, AppVideoManager, AppDocsManager, AppAudioManager, AppWebPagesManager, MtpApiManager, MtpApiFileManager, RichTextProcessor, NotificationsManager, PeersSelectService, Storage, AppProfileManager, FileManager, TelegramMeWebService, ErrorService, StatusManager, _) { diff --git a/app/less/app.less b/app/less/app.less index 4e491d21..3b39feec 100644 --- a/app/less/app.less +++ b/app/less/app.less @@ -2413,15 +2413,33 @@ a.composer_emoji_btn { &.emoji-spritesheet-4 { background-size: 884px 182px; } } -.composer_dropdown { + +.composer_dropdown_wrap { + background: #FFF; display: none; - padding: 6px 0; + position: absolute; border: 0; .box-shadow(0px 1px 1px 0px rgba(60,75,87,0.27)); border-radius: 0; margin-top: -5px; + height: 180px; +} +.composer_dropdown_scroller { +} + +.composer_dropdown { + position: static; + display: block; + float: none; + top: auto; + left: auto; + border: 0; + border-radius: 0; + padding: 0; + margin: 0; + z-index: auto; & > li > a { display: block; @@ -2473,6 +2491,7 @@ a.composer_emoji_btn { img& { width: 32px; height: 32px; + vertical-align: top; } span& { @@ -2490,6 +2509,33 @@ a.composer_emoji_btn { vertical-align: top; } +.composer_dropdown a.composer_command_option { + color: #808080; + line-height: 32px; + padding-right: 5px; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; +} +.composer_dropdown .composer_command_value { + color: #52719a; + display: inline; +} +.composer_dropdown .composer_command_desc { + display: inline; + color: #808080; + padding-left: 7px; + font-weight: normal; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; +} +a.composer_command_option:hover .composer_command_desc, +a.composer_command_option.composer_autocomplete_option_active .composer_command_desc { + color: #698192; +} + + .composer_stickerset_title { display: block; // clear: both; diff --git a/app/less/desktop.less b/app/less/desktop.less index d00863ac..55435f88 100644 --- a/app/less/desktop.less +++ b/app/less/desktop.less @@ -482,7 +482,8 @@ } } -.composer_emoji_tooltip { +.composer_emoji_tooltip, +.composer_dropdown_wrap { z-index: 1001; .nano > .nano-pane { @@ -491,11 +492,18 @@ & > .nano-slider { background: #d1d1d1; + background : rgba(0,0,0,0.17); margin: 0 3px 0 4px; } } } +.composer_dropdown_wrap .nano > .nano-pane { + top: 3px; + bottom: 3px; + right: -1px; +} + .countries_modal_col { .nano { & > .nano-pane { diff --git a/app/partials/desktop/im.html b/app/partials/desktop/im.html index bac088d7..96a587f4 100644 --- a/app/partials/desktop/im.html +++ b/app/partials/desktop/im.html @@ -162,7 +162,7 @@ -
    +