Browse Source

Added basic inline bots support

master
Igor Zhukov 8 years ago
parent
commit
040da24417
  1. 55
      app/js/controllers.js
  2. 35
      app/js/directives.js
  3. 5
      app/js/lib/ng_utils.js
  4. 12
      app/js/lib/utils.js
  5. 102
      app/js/message_composer.js
  6. 104
      app/js/messages_manager.js
  7. 2
      app/js/services.js
  8. 88
      app/less/app.less
  9. 2
      app/partials/desktop/composer_dropdown.html
  10. 2
      app/partials/desktop/im.html
  11. 16
      app/partials/desktop/inline_results.html
  12. 2
      app/partials/desktop/message.html

55
app/js/controllers.js

@ -1056,6 +1056,7 @@ angular.module('myApp.controllers', ['myApp.i18n'])
$scope.selectedReply = selectedReply; $scope.selectedReply = selectedReply;
$scope.selectedCancel = selectedCancel; $scope.selectedCancel = selectedCancel;
$scope.selectedFlush = selectedFlush; $scope.selectedFlush = selectedFlush;
$scope.selectInlineBot = selectInlineBot;
$scope.startBot = startBot; $scope.startBot = startBot;
$scope.cancelBot = cancelBot; $scope.cancelBot = cancelBot;
@ -1572,13 +1573,7 @@ angular.module('myApp.controllers', ['myApp.i18n'])
target.className.indexOf('im_message_body') != -1) { target.className.indexOf('im_message_body') != -1) {
break; break;
} }
if (target.tagName == 'A' || if (target.tagName == 'A' || hasOnlick(target)) {
target.onclick ||
target.getAttribute('ng-click')) {
return false;
}
var events = $._data(target, 'events');
if (events && (events.click || events.mousedown)) {
return false; return false;
} }
target = target.parentNode; target = target.parentNode;
@ -1663,6 +1658,11 @@ angular.module('myApp.controllers', ['myApp.i18n'])
$scope.$broadcast('messages_select'); $scope.$broadcast('messages_select');
} }
function selectInlineBot (botID, $event) {
$scope.$broadcast('inline_bot_select', botID);
return cancelEvent($event);
}
function selectedCancel (noBroadcast) { function selectedCancel (noBroadcast) {
$scope.selectedMsgs = {}; $scope.selectedMsgs = {};
$scope.selectedCount = 0; $scope.selectedCount = 0;
@ -2099,11 +2099,11 @@ angular.module('myApp.controllers', ['myApp.i18n'])
}; };
$scope.mentions = {}; $scope.mentions = {};
$scope.commands = {}; $scope.commands = {};
$scope.inlineResults = {};
$scope.$watch('draftMessage.text', onMessageChange); $scope.$watch('draftMessage.text', onMessageChange);
$scope.$watch('draftMessage.files', onFilesSelected); $scope.$watch('draftMessage.files', onFilesSelected);
$scope.$watch('draftMessage.sticker', onStickerSelected); $scope.$watch('draftMessage.sticker', onStickerSelected);
$scope.$watch('draftMessage.command', onCommandSelected); $scope.$watch('draftMessage.command', onCommandSelected);
$scope.$watch('draftMessage.inlineResultID', onInlineResultSelected);
$scope.$on('history_reply_markup', function (e, peerData) { $scope.$on('history_reply_markup', function (e, peerData) {
if (peerData.peerID == $scope.curDialog.peerID) { if (peerData.peerID == $scope.curDialog.peerID) {
@ -2111,6 +2111,12 @@ angular.module('myApp.controllers', ['myApp.i18n'])
} }
}); });
$scope.$on('inline_bot_select', function (e, botID) {
var bot = AppUsersManager.getUser(botID);
$scope.draftMessage.text = '@' + bot.username + ' ';;
$scope.$broadcast('ui_peer_draft', {focus: true});
});
$scope.replyKeyboardToggle = replyKeyboardToggle; $scope.replyKeyboardToggle = replyKeyboardToggle;
$scope.toggleSlash = toggleSlash; $scope.toggleSlash = toggleSlash;
@ -2390,7 +2396,7 @@ angular.module('myApp.controllers', ['myApp.i18n'])
return cancelEvent($event); return cancelEvent($event);
} }
var inlineUsernameRegex = /^@([a-zA-Z\d_]{1,32}) ([\s\S]*)$/; var inlineUsernameRegex = /^@([a-zA-Z\d_]{1,32})( | )([\s\S]*)$/;
var lastInlineBot = false; var lastInlineBot = false;
function onMessageChange(newVal) { function onMessageChange(newVal) {
// console.log('ctrl text changed', newVal); // console.log('ctrl text changed', newVal);
@ -2409,11 +2415,14 @@ angular.module('myApp.controllers', ['myApp.i18n'])
var matches = newVal.match(inlineUsernameRegex); var matches = newVal.match(inlineUsernameRegex);
if (matches) { if (matches) {
$scope.draftMessage.inlineProgress = true; $scope.draftMessage.inlineProgress = true;
AppPeersManager.resolveInlineMention(matches[1]).then(function (inlineBot) { var username = matches[1];
$scope.draftMessage.inlinePlaceholder = inlineBot.placeholder; AppPeersManager.resolveInlineMention(username).then(function (inlineBot) {
AppMessagesManager.getInlineResults(inlineBot.id, matches[2], '').then(function (botResults) { $scope.$broadcast('inline_placeholder', {
$scope.inlineResults = botResults; prefix: '@' + username + matches[2],
console.log('results', botResults); placeholder: inlineBot.placeholder
});
AppMessagesManager.getInlineResults(inlineBot.id, matches[3], '').then(function (botResults) {
$scope.$broadcast('inline_results', botResults);
delete $scope.draftMessage.inlineProgress; delete $scope.draftMessage.inlineProgress;
}, function () { }, function () {
delete $scope.draftMessage.inlineProgress; delete $scope.draftMessage.inlineProgress;
@ -2502,6 +2511,24 @@ angular.module('myApp.controllers', ['myApp.i18n'])
delete $scope.draftMessage.sticker; delete $scope.draftMessage.sticker;
delete $scope.draftMessage.text; delete $scope.draftMessage.text;
delete $scope.draftMessage.command; delete $scope.draftMessage.command;
delete $scope.draftMessage.inlineResultID;
$scope.$broadcast('ui_message_send');
$scope.$broadcast('ui_peer_draft');
}
function onInlineResultSelected (qID) {
if (!qID) {
return;
}
var options = {
replyToMsgID: $scope.draftMessage.replyToMessage && $scope.draftMessage.replyToMessage.mid
};
AppMessagesManager.sendInlineResult($scope.curDialog.peerID, qID, options);
resetDraft();
delete $scope.draftMessage.sticker;
delete $scope.draftMessage.text;
delete $scope.draftMessage.command;
delete $scope.draftMessage.inlineResultID;
$scope.$broadcast('ui_message_send'); $scope.$broadcast('ui_message_send');
$scope.$broadcast('ui_peer_draft'); $scope.$broadcast('ui_peer_draft');
} }

35
app/js/directives.js

@ -1516,7 +1516,7 @@ angular.module('myApp.directives', ['myApp.filters'])
}, },
dropdownDirective: function (element, callback) { dropdownDirective: function (element, callback) {
var scope = $scope.$new(true); var scope = $scope.$new(true);
$compile('<div my-composer-dropdown></div>')(scope, function (clonedElement) { var clonedElement = $compile('<div><div my-composer-dropdown></div></div>')(scope, function (clonedElement, scope) {
element.replaceWith(clonedElement); element.replaceWith(clonedElement);
callback(scope, clonedElement); callback(scope, clonedElement);
}); });
@ -1524,6 +1524,7 @@ angular.module('myApp.directives', ['myApp.filters'])
mentions: $scope.mentions, mentions: $scope.mentions,
commands: $scope.commands, commands: $scope.commands,
onMessageSubmit: onMessageSubmit, onMessageSubmit: onMessageSubmit,
onInlineResultSend: onInlineResultSend,
onFilePaste: onFilePaste, onFilePaste: onFilePaste,
onCommandSend: function (command) { onCommandSend: function (command) {
$scope.$apply(function () { $scope.$apply(function () {
@ -1532,11 +1533,21 @@ angular.module('myApp.directives', ['myApp.filters'])
} }
}); });
$scope.$on('inline_results', function (e, inlineResults) {
setZeroTimeout(function () {
composer.showInlineSuggestions(inlineResults);
});
});
var richTextarea = composer.richTextareaEl[0]; var richTextarea = composer.richTextareaEl[0];
if (richTextarea) { if (richTextarea) {
$(richTextarea).on('keydown keyup', updateHeight); $(richTextarea).on('keydown keyup', updateHeight);
} }
$scope.$on('inline_placeholder', function(e, data) {
composer.setInlinePlaceholder(data.prefix, data.placeholder);
});
fileSelects.on('change', function () { fileSelects.on('change', function () {
var self = this; var self = this;
$scope.$apply(function () { $scope.$apply(function () {
@ -1573,6 +1584,12 @@ angular.module('myApp.directives', ['myApp.filters'])
return cancelEvent(e); return cancelEvent(e);
} }
function onInlineResultSend (qID) {
$scope.$apply(function () {
$scope.draftMessage.inlineResultID = qID;
});
}
function updateValue () { function updateValue () {
if (richTextarea) { if (richTextarea) {
composer.onChange(); composer.onChange();
@ -2265,7 +2282,7 @@ angular.module('myApp.directives', ['myApp.filters'])
var src = 'https://maps.googleapis.com/maps/api/staticmap?sensor=false&center=' + $scope.point['lat'] + ',' + $scope.point['long'] + '&zoom=15&size='+width+'x'+height+'&scale=2&key=' + apiKey; var src = 'https://maps.googleapis.com/maps/api/staticmap?sensor=false&center=' + $scope.point['lat'] + ',' + $scope.point['long'] + '&zoom=15&size='+width+'x'+height+'&scale=2&key=' + apiKey;
ExternalResourcesManager.downloadImage(src).then(function (url) { ExternalResourcesManager.downloadImage(src).then(function (url) {
element.attr('src', url); element.attr('src', url.valueOf());
}); });
} }
@ -2765,7 +2782,7 @@ angular.module('myApp.directives', ['myApp.filters'])
} }
}; };
if (element[0].tagName == 'A') { if (element[0].tagName == 'A' && !hasOnlick(element[0])) {
element.on('click', function () { element.on('click', function () {
if (peerID > 0) { if (peerID > 0) {
AppUsersManager.openUser(peerID, override); AppUsersManager.openUser(peerID, override);
@ -3382,7 +3399,7 @@ angular.module('myApp.directives', ['myApp.filters'])
}) })
.directive('myInlineResults', function () { .directive('myInlineResults', function (ExternalResourcesManager) {
return { return {
templateUrl: templateUrl('inline_results'), templateUrl: templateUrl('inline_results'),
@ -3391,8 +3408,14 @@ angular.module('myApp.directives', ['myApp.filters'])
}, },
link: function ($scope, element, attrs) { link: function ($scope, element, attrs) {
$scope.$watch('botResults.results.length', function (show) { $scope.$watch('botResults.results', function (results) {
// console.log($scope.botResults, show); angular.forEach(results, function (result) {
if (result.thumb_url && !result.thumbUrl) {
ExternalResourcesManager.downloadImage(result.thumb_url).then(function (url) {
result.thumbUrl = url;
});
}
})
}); });
} }
} }

5
app/js/lib/ng_utils.js

@ -963,7 +963,7 @@ angular.module('izhukov.utils', [])
}; };
}) })
.service('ExternalResourcesManager', function ($q, $http) { .service('ExternalResourcesManager', function ($q, $http, $sce) {
var urlPromises = {}; var urlPromises = {};
function downloadImage (url) { function downloadImage (url) {
@ -974,7 +974,8 @@ angular.module('izhukov.utils', [])
return urlPromises[url] = $http.get(url, {responseType: 'blob', transformRequest: null}) return urlPromises[url] = $http.get(url, {responseType: 'blob', transformRequest: null})
.then(function (response) { .then(function (response) {
window.URL = window.URL || window.webkitURL; window.URL = window.URL || window.webkitURL;
return window.URL.createObjectURL(response.data); var url = window.URL.createObjectURL(response.data);
return $sce.trustAsResourceUrl(url);
}); });
} }

12
app/js/lib/utils.js

@ -60,6 +60,18 @@ function cancelEvent (event) {
return false; return false;
} }
function hasOnlick (element) {
if (element.onclick ||
element.getAttribute('ng-click')) {
return true;
}
var events = $._data(element, 'events');
if (events && (events.click || events.mousedown)) {
return true;
}
return false;
}
function getScrollWidth() { function getScrollWidth() {
var outer = $('<div>').css({ var outer = $('<div>').css({
position: 'absolute', position: 'absolute',

102
app/js/message_composer.js

@ -683,8 +683,8 @@ function MessageComposer (textarea, options) {
this.autoCompleteWrapEl = $('<div class="composer_dropdown_wrap"></div>').appendTo(document.body); this.autoCompleteWrapEl = $('<div class="composer_dropdown_wrap"></div>').appendTo(document.body);
var autoCompleteEl = $('<div></div>').appendTo(this.autoCompleteWrapEl); var autoCompleteEl = $('<div></div>').appendTo(this.autoCompleteWrapEl);
options.dropdownDirective(autoCompleteEl, function (scope, autoCompleteEl) { options.dropdownDirective(autoCompleteEl, function (scope, newAutoCompleteEl) {
self.autoCompleteEl = autoCompleteEl; self.autoCompleteEl = newAutoCompleteEl;
self.autoCompleteScope = scope; self.autoCompleteScope = scope;
self.setUpAutoComplete(); self.setUpAutoComplete();
}); });
@ -696,6 +696,7 @@ function MessageComposer (textarea, options) {
this.getSendOnEnter = options.getSendOnEnter; this.getSendOnEnter = options.getSendOnEnter;
this.onFilePaste = options.onFilePaste; this.onFilePaste = options.onFilePaste;
this.onCommandSend = options.onCommandSend; this.onCommandSend = options.onCommandSend;
this.onInlineResultSend = options.onInlineResultSend;
this.mentions = options.mentions; this.mentions = options.mentions;
this.commands = options.commands; this.commands = options.commands;
} }
@ -704,6 +705,10 @@ MessageComposer.autoCompleteRegEx = /(\s|^)(:|@|\/)([A-Za-z0-9\-\+\*@_]*)$/;
MessageComposer.prototype.setUpInput = function () { MessageComposer.prototype.setUpInput = function () {
this.inlinePlaceholderWrap = $('<div class="im_inline_placeholder_wrap"></div>').prependTo(this.textareaEl[0].parentNode);
this.inlinePlaceholderPrefixEl = $('<span class="im_inline_placeholder_prefix"></span>').appendTo(this.inlinePlaceholderWrap);
this.inlinePlaceholderEl = $('<span class="im_inline_placeholder"></span>').appendTo(this.inlinePlaceholderWrap);
if ('contentEditable' in document.body) { if ('contentEditable' in document.body) {
this.setUpRich(); this.setUpRich();
} else { } else {
@ -718,13 +723,27 @@ MessageComposer.prototype.setUpInput = function () {
} }
} }
MessageComposer.prototype.setInlinePlaceholder = function (prefix, placeholder) {
this.inlinePlaceholderPrefix = prefix
this.inlinePlaceholderPrefixEl.html(encodeEntities(prefix));
this.inlinePlaceholderEl.html(encodeEntities(placeholder));
}
MessageComposer.prototype.updateInlinePlaceholder = function () {
var prefix = this.inlinePlaceholderPrefix;
if (prefix) {
var value = this.textareaEl.val();
this.inlinePlaceholderWrap.toggle(value == prefix);
}
}
MessageComposer.prototype.setUpAutoComplete = function () { MessageComposer.prototype.setUpAutoComplete = function () {
this.scroller = new Scroller(this.autoCompleteEl, {maxHeight: 180}); this.scroller = new Scroller(this.autoCompleteEl, {maxHeight: 180});
var self = this; var self = this;
this.autoCompleteEl.on('mousedown', function (e) { this.autoCompleteEl.on('mousedown', function (e) {
e = e.originalEvent || e; e = e.originalEvent || e;
var target = $(e.target), mention, code, command; var target = $(e.target), mention, code, command, inlineID;
if (target[0].tagName != 'A') { if (target[0].tagName != 'A') {
target = $(target[0].parentNode); target = $(target[0].parentNode);
} }
@ -743,6 +762,12 @@ MessageComposer.prototype.setUpAutoComplete = function () {
} }
self.hideSuggestions(); self.hideSuggestions();
} }
if (inlineID = target.attr('data-inlineid')) {
if (self.onInlineResultSend) {
self.onInlineResultSend(inlineID);
}
self.hideSuggestions();
}
return cancelEvent(e); return cancelEvent(e);
}); });
} }
@ -778,7 +803,7 @@ MessageComposer.prototype.onKeyEvent = function (e) {
if (this.keyupStarted === undefined) { if (this.keyupStarted === undefined) {
this.keyupStarted = now; this.keyupStarted = now;
} }
if (now - this.keyupStarted > 10000) { if (now - this.keyupStarted > 3000 || true) {
this.onChange(); this.onChange();
} }
else { else {
@ -786,6 +811,8 @@ MessageComposer.prototype.onKeyEvent = function (e) {
if (this.wasEmpty != !length) { if (this.wasEmpty != !length) {
this.wasEmpty = !this.wasEmpty; this.wasEmpty = !this.wasEmpty;
this.onChange(); this.onChange();
} else if (this.inlinePlaceholderPrefix) {
this.onChange();
} else { } else {
this.updateValueTO = setTimeout(this.onChange.bind(this), 1000); this.updateValueTO = setTimeout(this.onChange.bind(this), 1000);
} }
@ -812,48 +839,57 @@ MessageComposer.prototype.onKeyEvent = function (e) {
if (this.autocompleteShown) { if (this.autocompleteShown) {
if (e.keyCode == 38 || e.keyCode == 40) { // UP / DOWN if (e.keyCode == 38 || e.keyCode == 40) { // UP / DOWN
var next = e.keyCode == 40; var next = e.keyCode == 40;
var currentSelected = $(this.autoCompleteEl).find('.composer_autocomplete_option_active'); var currentSel = $(this.autoCompleteEl).find('li.composer_autocomplete_option_active');
var allLIs = Array.prototype.slice.call($(this.autoCompleteEl).find('li'));
if (currentSelected.length) { var nextSel;
var currentSelectedWrap = currentSelected[0].parentNode;
var nextWrap = currentSelectedWrap[next ? 'nextSibling' : 'previousSibling']; if (currentSel.length) {
currentSelected.removeClass('composer_autocomplete_option_active'); var pos = allLIs.indexOf(currentSel[0]);
if (nextWrap) { var nextPos = pos + (next ? 1 : -1);
$(nextWrap).find('a').addClass('composer_autocomplete_option_active'); nextSel = allLIs[nextPos];
this.scroller.scrollToNode(nextWrap); currentSel.removeClass('composer_autocomplete_option_active');
if (nextSel) {
$(nextSel).addClass('composer_autocomplete_option_active');
this.scroller.scrollToNode(nextSel);
return cancelEvent(e); return cancelEvent(e);
} }
} }
var childNodes = this.autoCompleteEl[0].childNodes; nextSel = allLIs[next ? 0 : allLIs.length - 1];
var nextWrap = childNodes[next ? 0 : childNodes.length - 1]; this.scroller.scrollToNode(nextSel);
this.scroller.scrollToNode(nextWrap); $(nextSel).addClass('composer_autocomplete_option_active');
$(nextWrap).find('a').addClass('composer_autocomplete_option_active');
return cancelEvent(e); return cancelEvent(e);
} }
if (e.keyCode == 13 || e.keyCode == 9) { // Enter or Tab if (e.keyCode == 13 || e.keyCode == 9) { // Enter or Tab
var currentSelected = $(this.autoCompleteEl).find('.composer_autocomplete_option_active'); var currentSel = $(this.autoCompleteEl).find('li.composer_autocomplete_option_active');
if (!currentSelected.length && e.keyCode == 9) { if (!currentSel.length && e.keyCode == 9) {
currentSelected = $(this.autoCompleteEl[0].childNodes[0]).find('a'); currentSel = $(this.autoCompleteEl).find('li:first');
} }
currentSel = currentSel.find('a:first');
var code, mention, command; var code, mention, command;
if (code = currentSelected.attr('data-code')) { if (code = currentSel.attr('data-code')) {
this.onEmojiSelected(code, true); this.onEmojiSelected(code, true);
EmojiHelper.pushPopularEmoji(code); EmojiHelper.pushPopularEmoji(code);
return cancelEvent(e); return cancelEvent(e);
} }
if (mention = currentSelected.attr('data-mention')) { if (mention = currentSel.attr('data-mention')) {
this.onMentionSelected(mention); this.onMentionSelected(mention);
return cancelEvent(e); return cancelEvent(e);
} }
if (command = currentSelected.attr('data-command')) { if (command = currentSel.attr('data-command')) {
if (this.onCommandSelected) { if (this.onCommandSelected) {
this.onCommandSelected(command, e.keyCode == 9); this.onCommandSelected(command, e.keyCode == 9);
} }
return cancelEvent(e); return cancelEvent(e);
} }
if (inlineID = target.attr('data-inlineid')) {
if (self.onInlineResultSend) {
self.onInlineResultSend(inlineID);
}
return cancelEvent(e);
}
checkSubmit = true; checkSubmit = true;
} }
} }
@ -1283,6 +1319,7 @@ MessageComposer.prototype.onChange = function (e) {
delete this.keyupStarted; delete this.keyupStarted;
this.textareaEl.val(getRichValue(this.richTextareaEl[0])).trigger('change'); this.textareaEl.val(getRichValue(this.richTextareaEl[0])).trigger('change');
} }
this.updateInlinePlaceholder();
} }
MessageComposer.prototype.getEmojiHtml = function (code, emoji) { MessageComposer.prototype.getEmojiHtml = function (code, emoji) {
@ -1402,10 +1439,26 @@ MessageComposer.prototype.showCommandsSuggestions = function (commands) {
}); });
} }
MessageComposer.prototype.showInlineSuggestions = function (botResults) {
if (!botResults || !botResults.results.length) {
if (this.autocompleteShown && this.autoCompleteScope.type == 'inline') {
this.hideSuggestions();
}
return;
}
var self = this;
this.autoCompleteScope.$apply(function () {
self.autoCompleteScope.type = 'inline';
self.autoCompleteScope.botResults = botResults;
});
onContentLoaded(function () {
self.renderSuggestions();
});
}
MessageComposer.prototype.updatePosition = function () { MessageComposer.prototype.updatePosition = function () {
var offset = (this.richTextareaEl || this.textareaEl).offset(); var offset = (this.richTextareaEl || this.textareaEl).offset();
var height = this.scroller.updateHeight(); var height = this.scroller.updateHeight();
console.log(dT(), 'pos', height);
var width = $((this.richTextareaEl || this.textareaEl)[0].parentNode).outerWidth(); var width = $((this.richTextareaEl || this.textareaEl)[0].parentNode).outerWidth();
this.autoCompleteWrapEl.css({ this.autoCompleteWrapEl.css({
top: offset.top - height, top: offset.top - height,
@ -1416,6 +1469,7 @@ MessageComposer.prototype.updatePosition = function () {
} }
MessageComposer.prototype.hideSuggestions = function () { MessageComposer.prototype.hideSuggestions = function () {
// return;
this.autoCompleteWrapEl.hide(); this.autoCompleteWrapEl.hide();
delete this.autocompleteShown; delete this.autocompleteShown;
} }

104
app/js/messages_manager.js

@ -1294,10 +1294,12 @@ angular.module('myApp.services')
isChannel = AppPeersManager.isChannel(peerID), isChannel = AppPeersManager.isChannel(peerID),
isMegagroup = isChannel && AppPeersManager.isMegagroup(peerID), isMegagroup = isChannel && AppPeersManager.isMegagroup(peerID),
asChannel = isChannel && !isMegagroup ? true : false, asChannel = isChannel && !isMegagroup ? true : false,
entities = [], entities = options.entities || [],
message; message;
text = RichTextProcessor.parseMarkdown(text, entities); if (!options.viaBotID) {
text = RichTextProcessor.parseMarkdown(text, entities);
}
if (historyStorage === undefined) { if (historyStorage === undefined) {
historyStorage = historiesStorage[peerID] = {count: null, history: [], pending: []}; historyStorage = historiesStorage[peerID] = {count: null, history: [], pending: []};
@ -1331,6 +1333,7 @@ angular.module('myApp.services')
message: text, message: text,
random_id: randomIDS, random_id: randomIDS,
reply_to_msg_id: replyToMsgID, reply_to_msg_id: replyToMsgID,
via_bot_id: options.viaBotID,
entities: entities, entities: entities,
views: asChannel && 1, views: asChannel && 1,
pending: true pending: true
@ -1362,21 +1365,35 @@ angular.module('myApp.services')
if (replyToMsgID) { if (replyToMsgID) {
flags |= 1; flags |= 1;
} }
if (entities.length) {
flags |= 8;
}
if (asChannel) { if (asChannel) {
flags |= 16; flags |= 16;
} }
var apiPromise;
if (options.viaBotID) {
console.warn(options);
apiPromise = MtpApiManager.invokeApi('messages.sendInlineBotResult', {
flags: flags,
peer: AppPeersManager.getInputPeerByID(peerID),
random_id: randomID,
reply_to_msg_id: getMessageLocalID(replyToMsgID),
query_id: options.queryID,
id: options.resultID
}, sentRequestOptions);
} else {
if (entities.length) {
flags |= 8;
}
apiPromise = MtpApiManager.invokeApi('messages.sendMessage', {
flags: flags,
peer: AppPeersManager.getInputPeerByID(peerID),
message: text,
random_id: randomID,
reply_to_msg_id: getMessageLocalID(replyToMsgID),
entities: entities
}, sentRequestOptions)
}
// console.log(flags, entities); // console.log(flags, entities);
MtpApiManager.invokeApi('messages.sendMessage', { apiPromise.then(function (updates) {
flags: flags,
peer: AppPeersManager.getInputPeerByID(peerID),
message: text,
random_id: randomID,
reply_to_msg_id: getMessageLocalID(replyToMsgID),
entities: entities
}, sentRequestOptions).then(function (updates) {
if (updates._ == 'updateShortSentMessage') { if (updates._ == 'updateShortSentMessage') {
message.flags = updates.flags; message.flags = updates.flags;
message.date = updates.date; message.date = updates.date;
@ -1424,6 +1441,31 @@ angular.module('myApp.services')
pendingByRandomID[randomIDS] = [peerID, messageID]; pendingByRandomID[randomIDS] = [peerID, messageID];
}; };
function sendInlineResult (peerID, qID, options) {
var inlineResult = inlineResults[qID];
if (inlineResult === undefined) {
return false;
}
var splitted = qID.split('_');
var queryID = splitted.shift();
var resultID = splitted.join('_');
options = options || {};
options.viaBotID = inlineResult.botID;
options.queryID = queryID;
options.resultID = resultID;
if (inlineResult.send_message._ == 'botInlineMessageText') {
options.entities = inlineResult.send_message.entities;
sendText(peerID, inlineResult.send_message.message, options);
} else {
sendOther(peerID, {
_: 'messageMediaPending',
size: 0,
progress: {percent: 33, total: 0}
}, options);
}
}
function sendFile(peerID, file, options) { function sendFile(peerID, file, options) {
options = options || {}; options = options || {};
var messageID = tempID--, var messageID = tempID--,
@ -1654,6 +1696,10 @@ angular.module('myApp.services')
'document': doc 'document': doc
}; };
break; break;
case 'messageMediaPending':
media = inputMedia;
break;
} }
var flags = 0; var flags = 0;
@ -1687,6 +1733,7 @@ angular.module('myApp.services')
media: media, media: media,
random_id: randomIDS, random_id: randomIDS,
reply_to_msg_id: replyToMsgID, reply_to_msg_id: replyToMsgID,
via_bot_id: options.viaBotID,
views: asChannel && 1, views: asChannel && 1,
pending: true pending: true
}; };
@ -1721,13 +1768,27 @@ angular.module('myApp.services')
sentRequestOptions.afterMessageID = pendingAfterMsgs[peerID].messageID; sentRequestOptions.afterMessageID = pendingAfterMsgs[peerID].messageID;
} }
MtpApiManager.invokeApi('messages.sendMedia', { var apiPromise;
flags: flags, if (options.viaBotID) {
peer: AppPeersManager.getInputPeerByID(peerID), console.warn(options);
media: inputMedia, apiPromise = MtpApiManager.invokeApi('messages.sendInlineBotResult', {
random_id: randomID, flags: flags,
reply_to_msg_id: getMessageLocalID(replyToMsgID) peer: AppPeersManager.getInputPeerByID(peerID),
}, sentRequestOptions).then(function (updates) { random_id: randomID,
reply_to_msg_id: getMessageLocalID(replyToMsgID),
query_id: options.queryID,
id: options.resultID
}, sentRequestOptions);
} else {
apiPromise = MtpApiManager.invokeApi('messages.sendMedia', {
flags: flags,
peer: AppPeersManager.getInputPeerByID(peerID),
media: inputMedia,
random_id: randomID,
reply_to_msg_id: getMessageLocalID(replyToMsgID)
}, sentRequestOptions);
}
apiPromise.then(function (updates) {
ApiUpdatesManager.processUpdateMessage(updates); ApiUpdatesManager.processUpdateMessage(updates);
}, function (error) { }, function (error) {
toggleError(true); toggleError(true);
@ -3026,9 +3087,11 @@ angular.module('myApp.services')
angular.forEach(botResults.results, function (result) { angular.forEach(botResults.results, function (result) {
var qID = queryID + '_' + result.id; var qID = queryID + '_' + result.id;
result.qID = qID; result.qID = qID;
result.botID = botID;
result.rTitle = RichTextProcessor.wrapRichText(result.title, {noLinebreaks: true, noLinks: true}); result.rTitle = RichTextProcessor.wrapRichText(result.title, {noLinebreaks: true, noLinks: true});
result.rDescription = RichTextProcessor.wrapRichText(result.description, {noLinebreaks: true, noLinks: true}); result.rDescription = RichTextProcessor.wrapRichText(result.description, {noLinebreaks: true, noLinks: true});
result.initials = (result.url || result.title || result.type || '').substr(0, 1)
inlineResults[qID] = result; inlineResults[qID] = result;
}); });
@ -3058,6 +3121,7 @@ angular.module('myApp.services')
getMessageThumb: getMessageThumb, getMessageThumb: getMessageThumb,
clearDialogCache: clearDialogCache, clearDialogCache: clearDialogCache,
getInlineResults: getInlineResults, getInlineResults: getInlineResults,
sendInlineResult: sendInlineResult,
wrapForDialog: wrapForDialog, wrapForDialog: wrapForDialog,
wrapForHistory: wrapForHistory, wrapForHistory: wrapForHistory,
wrapReplyMarkup: wrapReplyMarkup, wrapReplyMarkup: wrapReplyMarkup,

2
app/js/services.js

@ -833,7 +833,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
} }
}) })
.service('AppPeersManager', function (qSync, AppUsersManager, AppChatsManager, MtpApiManager) { .service('AppPeersManager', function ($q, qSync, AppUsersManager, AppChatsManager, MtpApiManager) {
function getInputPeer (peerString) { function getInputPeer (peerString) {
var firstChar = peerString.charAt(0), var firstChar = peerString.charAt(0),

88
app/less/app.less

@ -1440,12 +1440,14 @@ a.im_dialog_selected {
margin: 10px 0; margin: 10px 0;
} }
.im_message_author, .im_message_author,
.im_message_fwd_author { .im_message_fwd_author,
.im_message_via_author {
color: #3a6d99; color: #3a6d99;
font-weight: bold; font-weight: bold;
} }
.non_osx .im_message_author, .non_osx .im_message_author,
.non_osx .im_message_fwd_author { .non_osx .im_message_fwd_author,
.non_osx .im_message_via_author {
font-size: 12px; font-size: 12px;
} }
.im_message_author_via { .im_message_author_via {
@ -2368,6 +2370,18 @@ textarea.im_message_field {
height: 50px; height: 50px;
resize: none; resize: none;
} }
.im_inline_placeholder_wrap {
position: absolute;
left: 0;
margin-top: 2px;
white-space: nowrap
}
.im_inline_placeholder_prefix {
visibility: hidden;
}
.im_inline_placeholder {
color: #999;
}
.icon-online { .icon-online {
background: #6ec26d; background: #6ec26d;
@ -2858,6 +2872,7 @@ a.composer_emoji_btn {
border-radius: 0; border-radius: 0;
margin-top: -5px; margin-top: -5px;
margin-left: -1px; margin-left: -1px;
width: 180px;
} }
.composer_dropdown { .composer_dropdown {
@ -2881,9 +2896,10 @@ a.composer_emoji_btn {
} }
li a:hover, li a:hover,
li a.composer_autocomplete_option_active { li.composer_autocomplete_option_active a {
color: #52719a; color: #52719a;
background: #f2f6fa; background: #f2f6fa;
text-decoration: none;
} }
} }
@ -2903,7 +2919,7 @@ a.composer_emoji_btn {
.composer_dropdown { .composer_dropdown {
li a:hover .composer_user_mention, li a:hover .composer_user_mention,
li a.composer_autocomplete_option_active .composer_user_mention { li.composer_autocomplete_option_active a .composer_user_mention {
color: #698192; color: #698192;
} }
} }
@ -2962,7 +2978,7 @@ a.composer_emoji_btn {
text-overflow: ellipsis; text-overflow: ellipsis;
} }
a.composer_command_option:hover .composer_command_desc, a.composer_command_option:hover .composer_command_desc,
a.composer_command_option.composer_autocomplete_option_active .composer_command_desc { .composer_autocomplete_option_active a.composer_command_option .composer_command_desc {
color: #698192; color: #698192;
} }
.composer_command_desc .emoji { .composer_command_desc .emoji {
@ -3132,44 +3148,36 @@ _:-ms-lang(x), .composer_rich_textarea:empty:focus:before {
} }
} }
.im_send_form_inline_results { .inline_result_wrap {
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; display: block;
float: none;
top: auto;
left: auto;
border: 0;
border-radius: 0;
padding: 0;
margin: 0;
z-index: auto;
} }
.inline_result_article {
.inline_result_wrap {
display: block; display: block;
font-size: 13px; }
line-height: 15px; .inline_article_thumb_wrap {
padding: 4px 10px; width: 50px;
color: #52719a; height: 50px;
margin-right: 10px;
&:hover, pointer-events: none;
&.composer_autocomplete_option_active { }
color: #52719a; .inline_article_thumb {
background: #f2f6fa; max-width: 50px;
} max-height: 50px;
line-height: 0;
}
.inline_article_thumb_initials {
background: rgba(0,0,0, 0.05);
line-height: 50px;
text-align: center;
font-size: 25px;
text-transform: uppercase;
}
.inline_article_content_wrap {
overflow: hidden;
pointer-events: none;
}
.inline_article_title {
font-weight: bold;
} }

2
app/partials/desktop/composer_dropdown.html

@ -1,7 +1,7 @@
<div ng-switch="type"> <div ng-switch="type">
<ul ng-switch-when="mentions" class="composer_dropdown"> <ul ng-switch-when="mentions" class="composer_dropdown">
<li ng-repeat="user in users"> <li ng-repeat="user in mentionUsers">
<a class="composer_mention_option" data-mention="{{user.username}}"> <a class="composer_mention_option" data-mention="{{user.username}}">
<span class="composer_user_photo" my-peer-photolink="user.id" img-class="composer_user_photo"></span> <span class="composer_user_photo" my-peer-photolink="user.id" img-class="composer_user_photo"></span>
<span class="composer_user_name" ng-bind-html="user.rFullName"></span> <span class="composer_user_name" ng-bind-html="user.rFullName"></span>

2
app/partials/desktop/im.html

@ -203,7 +203,7 @@
<a class="composer_keyboard_btn" ng-show="historyState.replyKeyboard._ == 'replyKeyboardMarkup'" ng-mousedown="replyKeyboardToggle($event)" ng-class="!historyState.replyKeyboard.pFlags.hidden ? 'active' : ''"><i class="icon icon-keyboard"></i></a> <a class="composer_keyboard_btn" ng-show="historyState.replyKeyboard._ == 'replyKeyboardMarkup'" ng-mousedown="replyKeyboardToggle($event)" ng-class="!historyState.replyKeyboard.pFlags.hidden ? 'active' : ''"><i class="icon icon-keyboard"></i></a>
<div class="im_send_dropbox_wrap" my-i18n="im_photos_drop_text"></div> <div class="im_send_dropbox_wrap" my-i18n="im_photos_drop_text"></div>
<textarea ng-model="draftMessage.text" class="form-control im_message_field no_outline" dir="auto"></textarea> <textarea ng-model="draftMessage.text" class="form-control im_message_field no_outline" dir="auto" ng-trim="false"></textarea>
</div> </div>
<div class="im_send_buttons_wrap clearfix"> <div class="im_send_buttons_wrap clearfix">

16
app/partials/desktop/inline_results.html

@ -1,8 +1,8 @@
<div class="inline_results_wrap"> <ul class="inline_results_wrap composer_dropdown">
<div class="inline_result_wrap" ng-repeat="result in botResults.results track by result.qID" ng-switch="result.type"> <li class="inline_result_wrap" ng-repeat="result in botResults.results track by result.qID" ng-switch="result.type">
<div ng-switch-default class="inline_result_"> <a ng-switch-default class="inline_result_article clearfix" data-inlineid="{{result.qID}}">
<div class="inline_article_thumb_wrap pull-left" ng-switch="result.thumbUrl.length > 0"> <div class="inline_article_thumb_wrap pull-left" ng-switch="result.thumbUrl !== undefined">
<img ng-switch-when="true" ng-src="{{result.thumbUrl}}"/> <img ng-switch-when="true" class="inline_article_thumb" ng-src="{{result.thumbUrl}}"/>
<div ng-switch-default class="inline_article_thumb_initials" ng-bind="result.initials"></div> <div ng-switch-default class="inline_article_thumb_initials" ng-bind="result.initials"></div>
</div> </div>
<div class="inline_article_content_wrap"> <div class="inline_article_content_wrap">
@ -10,6 +10,6 @@
<div class="inline_article_description" ng-if="::result.description.length > 0" ng-bind-html="::result.rDescription"></div> <div class="inline_article_description" ng-if="::result.description.length > 0" ng-bind-html="::result.rDescription"></div>
<div class="inline_article_url" ng-if="::result.url.length > 0" ng-bind="::result.url"></div> <div class="inline_article_url" ng-if="::result.url.length > 0" ng-bind="::result.url"></div>
</div> </div>
</div> </a>
</div> </li>
</div> </ul>

2
app/partials/desktop/message.html

@ -44,7 +44,7 @@
<div class="im_message_body" ng-class="::{im_message_body_media: historyMessage._ == 'message' &amp;&amp; historyMessage.media ? true : false}"> <div class="im_message_body" ng-class="::{im_message_body_media: historyMessage._ == 'message' &amp;&amp; historyMessage.media ? true : false}">
<a class="im_message_author" my-peer-link="historyMessage.fromID" short="historyMessage.toID > 0" color="historyMessage.toID < 0" no-watch="true"></a><span ng-if="::historyMessage.viaBotID && !historyMessage.fwdFromID" class="im_message_author_via" my-i18n="message_via_bot"><my-i18n-param name="bot"><a class="im_message_fwd_author" my-peer-link="historyMessage.viaBotID" username="true" no-watch="true"></a></my-i18n-param></span> <a class="im_message_author" my-peer-link="historyMessage.fromID" short="historyMessage.toID > 0" color="historyMessage.toID < 0" no-watch="true"></a><span ng-if="::historyMessage.viaBotID && !historyMessage.fwdFromID" class="im_message_author_via" my-i18n="message_via_bot"><my-i18n-param name="bot"><a class="im_message_fwd_author" my-peer-link="historyMessage.viaBotID" username="true" no-watch="true" ng-click="selectInlineBot(historyMessage.viaBotID, $event)"></a></my-i18n-param></span>
<a class="im_message_reply_wrap" my-reply-message="historyMessage.reply_to_msg" ng-if="::historyMessage.reply_to_mid"></a> <a class="im_message_reply_wrap" my-reply-message="historyMessage.reply_to_msg" ng-if="::historyMessage.reply_to_mid"></a>

Loading…
Cancel
Save