diff --git a/app/js/controllers.js b/app/js/controllers.js index 698302d5..6e9b7ad7 100644 --- a/app/js/controllers.js +++ b/app/js/controllers.js @@ -2099,6 +2099,7 @@ angular.module('myApp.controllers', ['myApp.i18n']) }; $scope.mentions = {}; $scope.commands = {}; + $scope.inlineResults = {}; $scope.$watch('draftMessage.text', onMessageChange); $scope.$watch('draftMessage.files', onFilesSelected); $scope.$watch('draftMessage.sticker', onStickerSelected); @@ -2389,7 +2390,7 @@ angular.module('myApp.controllers', ['myApp.i18n']) return cancelEvent($event); } - var inlineUsernameRegex = /^@([a-zA-Z\d_]{1,32}) /; + var inlineUsernameRegex = /^@([a-zA-Z\d_]{1,32}) ([\s\S]*)$/; var lastInlineBot = false; function onMessageChange(newVal) { // console.log('ctrl text changed', newVal); @@ -2405,16 +2406,27 @@ angular.module('myApp.controllers', ['myApp.i18n']) Storage.set(backupDraftObj); // console.log(dT(), 'draft save', backupDraftObj); - var matches; - if (matches = newVal.match(inlineUsernameRegex)) { - AppPeersManager.resolveInlineMention(matches[1]).then(function (placeholder) { - $scope.draftMessage.inlinePlaceholder = placeholder; + var matches = newVal.match(inlineUsernameRegex); + if (matches) { + $scope.draftMessage.inlineProgress = true; + AppPeersManager.resolveInlineMention(matches[1]).then(function (inlineBot) { + $scope.draftMessage.inlinePlaceholder = inlineBot.placeholder; + AppMessagesManager.getInlineResults(inlineBot.id, matches[2], '').then(function (botResults) { + $scope.inlineResults = botResults; + console.log('results', botResults); + delete $scope.draftMessage.inlineProgress; + }, function () { + delete $scope.draftMessage.inlineProgress; + }); }, function () { - - }) + delete $scope.draftMessage.inlinePlaceholder; + delete $scope.draftMessage.inlineProgress; + }); } } else { Storage.remove('draft' + $scope.curDialog.peerID); + delete $scope.draftMessage.inlinePlaceholder; + delete $scope.draftMessage.inlineProgress; // console.log(dT(), 'draft delete', 'draft' + $scope.curDialog.peerID); } } diff --git a/app/js/directives.js b/app/js/directives.js index f3eb85d9..da49bd58 100755 --- a/app/js/directives.js +++ b/app/js/directives.js @@ -1517,16 +1517,11 @@ angular.module('myApp.directives', ['myApp.filters']) getSendOnEnter: function () { return sendOnEnter; }, - getPeerImage: function (element, peerID, noReplace) { - if (cachedPeerPhotos[peerID] && !noReplace) { - element.replaceWith(cachedPeerPhotos[peerID]); - return; - } + dropdownDirective: function (element, callback) { var scope = $scope.$new(true); - scope.peerID = peerID; - peerPhotoCompiled(scope, function (clonedElement) { - cachedPeerPhotos[peerID] = clonedElement; + $compile('
')(scope, function (clonedElement) { element.replaceWith(clonedElement); + callback(scope, clonedElement); }); }, mentions: $scope.mentions, @@ -3289,7 +3284,7 @@ angular.module('myApp.directives', ['myApp.filters']) var width = attrs.width || element.width() || 40; var stroke = attrs.stroke || (width / 2 * 0.14); var center = width / 2; - var radius = center - stroke; + var radius = center - (stroke / 2); // Doesn't work without unique id for every gradient var curNum = ++num; @@ -3345,3 +3340,63 @@ angular.module('myApp.directives', ['myApp.filters']) }; }) + + .directive('myComposerDropdown', function () { + + return { + templateUrl: templateUrl('composer_dropdown') + } + }) + + .directive('myEmojiSuggestions', function () { + + return { + link: function($scope, element, attrs) { + $scope.$watchCollection('emojiCodes', function (codes) { + // var codes = $scope.$eval(attrs.myEmojiSuggestions); + var html = []; + var iconSize = Config.Mobile ? 26 : 20; + + var emoticonCode, emoticonData, spritesheet, pos, categoryIndex; + var count = Math.min(5, codes.length); + var i, x, y; + + for (i = 0; i < count; i++) { + emoticonCode = codes[i]; + if (emoticonCode.code) { + emoticonCode = emoticonCode.code; + } + if (emoticonData = Config.Emoji[emoticonCode]) { + spritesheet = EmojiHelper.spritesheetPositions[emoticonCode]; + categoryIndex = spritesheet[0]; + pos = spritesheet[1]; + x = iconSize * spritesheet[3]; + y = iconSize * spritesheet[2]; + html.push('
  • :' + encodeEntities(emoticonData[1][0]) + ':
  • '); + } + } + onContentLoaded(function () { + element.html(html); + }); + }); + } + }; + + }) + + .directive('myInlineResults', function () { + + return { + templateUrl: templateUrl('inline_results'), + scope: { + botResults: '=myInlineResults' + }, + + link: function ($scope, element, attrs) { + $scope.$watch('botResults.results.length', function (show) { + console.log($scope.botResults, show); + }); + } + } + }) + diff --git a/app/js/message_composer.js b/app/js/message_composer.js index 1ba65ce3..cf04c492 100644 --- a/app/js/message_composer.js +++ b/app/js/message_composer.js @@ -674,38 +674,19 @@ EmojiPanel.prototype.update = function () { function MessageComposer (textarea, options) { + var self = this; + this.textareaEl = $(textarea); this.setUpInput(); this.autoCompleteWrapEl = $('
    ').appendTo(document.body); - this.autoCompleteEl = $('').appendTo(this.autoCompleteWrapEl); + var autoCompleteEl = $('
    ').appendTo(this.autoCompleteWrapEl); - this.scroller = new Scroller(this.autoCompleteEl, {maxHeight: 180}); - - var self = this; - this.autoCompleteEl.on('mousedown', function (e) { - e = e.originalEvent || e; - var target = $(e.target), mention, code, command; - if (target[0].tagName != 'A') { - target = $(target[0].parentNode); - } - if (code = target.attr('data-code')) { - if (self.onEmojiSelected) { - self.onEmojiSelected(code, true); - } - EmojiHelper.pushPopularEmoji(code); - } - if (mention = target.attr('data-mention')) { - self.onMentionSelected(mention); - } - if (command = target.attr('data-command')) { - if (self.onCommandSelected) { - self.onCommandSelected(command); - } - self.hideSuggestions(); - } - return cancelEvent(e); + options.dropdownDirective(div, function (scope, autoCompleteEl) { + self.autoCompleteEl = autoCompleteEl; + self.autoCompleteScope = scope; + self.setUpAutoComplete(); }); this.isActive = false; @@ -714,10 +695,9 @@ function MessageComposer (textarea, options) { this.onMessageSubmit = options.onMessageSubmit; this.getSendOnEnter = options.getSendOnEnter; this.onFilePaste = options.onFilePaste; + this.onCommandSend = options.onCommandSend; this.mentions = options.mentions; this.commands = options.commands; - this.getPeerImage = options.getPeerImage; - this.onCommandSend = options.onCommandSend; } MessageComposer.autoCompleteRegEx = /(\s|^)(:|@|\/)([A-Za-z0-9\-\+\*@_]*)$/; @@ -738,6 +718,35 @@ MessageComposer.prototype.setUpInput = function () { } } +MessageComposer.prototype.setUpAutoComplete = function () { + this.scroller = new Scroller(this.autoCompleteEl, {maxHeight: 180}); + + var self = this; + this.autoCompleteEl.on('mousedown', function (e) { + e = e.originalEvent || e; + var target = $(e.target), mention, code, command; + if (target[0].tagName != 'A') { + target = $(target[0].parentNode); + } + if (code = target.attr('data-code')) { + if (self.onEmojiSelected) { + self.onEmojiSelected(code, true); + } + EmojiHelper.pushPopularEmoji(code); + } + if (mention = target.attr('data-mention')) { + self.onMentionSelected(mention); + } + if (command = target.attr('data-command')) { + if (self.onCommandSelected) { + self.onCommandSelected(command); + } + self.hideSuggestions(); + } + return cancelEvent(e); + }); +} + MessageComposer.prototype.setUpRich = function () { this.textareaEl.hide(); this.richTextareaEl = $('
    '); @@ -1353,8 +1362,7 @@ MessageComposer.prototype.blur = function () { } } -MessageComposer.prototype.renderSuggestions = function (html) { - this.autoCompleteEl.html(html.join('')); +MessageComposer.prototype.renderSuggestions = function () { this.autoCompleteWrapEl.show(); this.scroller.reinit(); this.updatePosition(); @@ -1362,72 +1370,35 @@ MessageComposer.prototype.renderSuggestions = function (html) { } MessageComposer.prototype.showEmojiSuggestions = function (codes) { - var html = []; - var iconSize = Config.Mobile ? 26 : 20; - - var emoticonCode, emoticonData, spritesheet, pos, categoryIndex; - var count = Math.min(5, codes.length); - var i, x, y; - - for (i = 0; i < count; i++) { - emoticonCode = codes[i]; - if (emoticonCode.code) { - emoticonCode = emoticonCode.code; - } - if (emoticonData = Config.Emoji[emoticonCode]) { - spritesheet = EmojiHelper.spritesheetPositions[emoticonCode]; - categoryIndex = spritesheet[0]; - pos = spritesheet[1]; - x = iconSize * spritesheet[3]; - y = iconSize * spritesheet[2]; - html.push('
  • :' + encodeEntities(emoticonData[1][0]) + ':
  • '); - } - } - - this.renderSuggestions(html); + var self = this; + this.autoCompleteScope.$apply(function () { + self.autoCompleteScope.type = 'emoji'; + self.autoCompleteScope.emojiCodes = codes; + }); + onContentLoaded(function () { + self.renderSuggestions(); + }); } MessageComposer.prototype.showMentionSuggestions = function (users) { - var html = []; - var user; - var count = users.length; - var i; - - for (i = 0; i < count; i++) { - user = users[i]; - html.push('
  • ' + user.rFullName + '@' + user.username + '
  • '); - } - - 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.autoCompleteScope.$apply(function () { + self.autoCompleteScope.type = 'mentions'; + self.autoCompleteScope.mentionUsers = users; + }); + onContentLoaded(function () { + self.renderSuggestions(); }); } MessageComposer.prototype.showCommandsSuggestions = function (commands) { - var html = []; - var command; - var count = Math.min(200, commands.length); - var i; - - for (i = 0; i < count; i++) { - command = commands[i]; - html.push('
  • ' + encodeEntities(command.value) + '' + command.rDescription + '
  • '); - } - - 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); + this.autoCompleteScope.$apply(function () { + self.autoCompleteScope.type = 'commands'; + self.autoCompleteScope.commands = commands; + }); + onContentLoaded(function () { + self.renderSuggestions(); }); } diff --git a/app/js/messages_manager.js b/app/js/messages_manager.js index 41fbb4e8..dd9d3007 100644 --- a/app/js/messages_manager.js +++ b/app/js/messages_manager.js @@ -3010,7 +3010,31 @@ angular.module('myApp.services') }; } }) - }) + }); + + var inlineResults = {}; + function getInlineResults (botID, query, offset) { + return MtpApiManager.invokeApi('messages.getInlineBotResults', { + bot: AppUsersManager.getUserInput(botID), + query: query, + offset: offset + }).then(function(botResults) { + var queryID = botResults.query_id; + delete botResults._; + delete botResults.flags; + delete botResults.query_id; + angular.forEach(botResults.results, function (result) { + var qID = queryID + '_' + result.id; + result.qID = qID; + + result.rTitle = RichTextProcessor.wrapRichText(result.title, {noLinebreaks: true, noLinks: true}); + result.rDescription = RichTextProcessor.wrapRichText(result.description, {noLinebreaks: true, noLinks: true}); + + inlineResults[qID] = result; + }); + return botResults; + }); + } return { getConversations: getConversations, @@ -3033,6 +3057,7 @@ angular.module('myApp.services') getMessagePeer: getMessagePeer, getMessageThumb: getMessageThumb, clearDialogCache: clearDialogCache, + getInlineResults: getInlineResults, wrapForDialog: wrapForDialog, wrapForHistory: wrapForHistory, wrapReplyMarkup: wrapReplyMarkup, diff --git a/app/js/services.js b/app/js/services.js index 42fcb126..3da6163d 100755 --- a/app/js/services.js +++ b/app/js/services.js @@ -949,7 +949,10 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) if (peerID > 0) { var bot = AppUsersManager.getUser(peerID); if (bot.pFlags.bot && bot.bot_inline_placeholder !== undefined) { - return bot.bot_inline_placeholder; + return qSync.when({ + id: peerID, + placeholder: bot.bot_inline_placeholder + }); } } return $q.reject(); diff --git a/app/less/app.less b/app/less/app.less index 681ecf98..cb853137 100644 --- a/app/less/app.less +++ b/app/less/app.less @@ -434,6 +434,10 @@ a { animation: infinite_rotation 0.8s linear infinite; } + .composer_progress_icon & { + stroke: rgba(0,0,0,0.3); + } + .progress-arc-percent & { stroke: #FFF; stroke: rgba(255,255,255,0.95); @@ -449,14 +453,23 @@ a { .stop0 { stop-opacity: 1.0; stop-color: #68a4d1; + .composer_progress_icon & { + stop-color: rgba(0,0,0,0.3); + } } .stop60 { stop-opacity: 1.0; stop-color: #68a4d1; + .composer_progress_icon & { + stop-color: rgba(0,0,0,0.3); + } } .stop100 { stop-opacity: 0.0; stop-color: #68a4d1; + .composer_progress_icon & { + stop-color: rgba(0,0,0,0.3); + } } /* Infinite rotation */ @@ -2485,7 +2498,27 @@ img.img_fullsize { } /* Message composer */ +.composer_progress_icon { + display: block; + opacity: 0; + position: absolute; + right: 3px; + top: 2px; + cursor: pointer; + padding: 0; + + width: 22px; + height: 22px; + margin-top: 1px; + transition: opacity cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.2s; + pointer-events: none; + + .composer_progress_enabled & { + opacity: 1; + } +} .composer_emoji_insert_btn { + opacity: 1; display: block; position: absolute; right: 3px; @@ -2496,7 +2529,13 @@ img.img_fullsize { width: 22px; height: 22px; margin-top: 1px; + transition: opacity cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.2s; + + .composer_progress_enabled & { + opacity: 0; + } } + .icon-emoji { display: inline-block; width: 22px; @@ -3093,6 +3132,46 @@ _:-ms-lang(x), .composer_rich_textarea:empty:focus:before { } } +.im_send_form_inline_results { + position: relative; + display: none; +} +.inline_results_wrap { + position: absolute; + bottom: 0; + background: #FFF; + border: 0; + + .box-shadow(0px 1px 1px 0px rgba(60,75,87,0.27)); + + // margin-top: -5px; + // margin-left: -1px; + // position: static; + display: block; + float: none; + top: auto; + left: auto; + border: 0; + border-radius: 0; + padding: 0; + margin: 0; + z-index: auto; +} + +.inline_result_wrap { + display: block; + font-size: 13px; + line-height: 15px; + padding: 4px 10px; + color: #52719a; + + &:hover, + &.composer_autocomplete_option_active { + color: #52719a; + background: #f2f6fa; + } +} + .error_modal_window { .modal-dialog { diff --git a/app/partials/desktop/composer_dropdown.html b/app/partials/desktop/composer_dropdown.html new file mode 100644 index 00000000..3cc9dab6 --- /dev/null +++ b/app/partials/desktop/composer_dropdown.html @@ -0,0 +1,27 @@ +
    + + + + + + + +
    + +
    \ No newline at end of file diff --git a/app/partials/desktop/im.html b/app/partials/desktop/im.html index 22a72e87..a46d2238 100644 --- a/app/partials/desktop/im.html +++ b/app/partials/desktop/im.html @@ -182,7 +182,9 @@ -
    + + +
    @@ -196,6 +198,7 @@
    +
    diff --git a/app/partials/desktop/inline_results.html b/app/partials/desktop/inline_results.html new file mode 100644 index 00000000..66fd4259 --- /dev/null +++ b/app/partials/desktop/inline_results.html @@ -0,0 +1,15 @@ +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    \ No newline at end of file