Browse Source

Improved inline bots support

master
Igor Zhukov 9 years ago
parent
commit
a3e06ea51d
  1. 20
      app/js/controllers.js
  2. 8
      app/js/directives.js
  3. 2
      app/js/lib/config.js
  4. 11
      app/js/lib/ng_utils.js
  5. 2
      app/js/lib/schema.tl.txt
  6. 32
      app/js/services.js
  7. 45
      app/less/app.less
  8. 24
      app/partials/desktop/inline_results.html
  9. 2
      package.json

20
app/js/controllers.js

@ -2151,13 +2151,7 @@ angular.module('myApp.controllers', ['myApp.i18n'])
var text = $scope.draftMessage.text; var text = $scope.draftMessage.text;
if (angular.isString(text) && text.length > 0) { if (angular.isString(text) && text.length > 0) {
text = text.replace(/:([a-z0-9\-\+\*_]+?):/gi, function (all, shortcut) { text = RichTextProcessor.parseEmojis(text);
var emojiCode = EmojiHelper.shortcuts[shortcut];
if (emojiCode !== undefined) {
return EmojiHelper.emojis[emojiCode][0];
}
return all;
});
var timeout = 0; var timeout = 0;
var options = { var options = {
@ -2469,6 +2463,7 @@ angular.module('myApp.controllers', ['myApp.i18n'])
var inlineUsernameRegex = /^@([a-zA-Z\d_]{1,32})( | )([\s\S]*)$/; var inlineUsernameRegex = /^@([a-zA-Z\d_]{1,32})( | )([\s\S]*)$/;
var getInlineResultsTO = false; var getInlineResultsTO = false;
var lastInlineBotID = false;
var jump = 0; var jump = 0;
function checkInlinePattern (message) { function checkInlinePattern (message) {
@ -2493,6 +2488,7 @@ angular.module('myApp.controllers', ['myApp.i18n'])
if (curJump != jump) { if (curJump != jump) {
return; return;
} }
lastInlineBotID = inlineBot.id;
$scope.$broadcast('inline_placeholder', { $scope.$broadcast('inline_placeholder', {
prefix: '@' + username + matches[2], prefix: '@' + username + matches[2],
placeholder: inlineBot.placeholder placeholder: inlineBot.placeholder
@ -2501,7 +2497,8 @@ angular.module('myApp.controllers', ['myApp.i18n'])
$timeout.cancel(getInlineResultsTO); $timeout.cancel(getInlineResultsTO);
} }
getInlineResultsTO = $timeout(function () { getInlineResultsTO = $timeout(function () {
AppInlineBotsManager.getInlineResults(inlineBot.id, matches[3], '').then(function (botResults) { var query = RichTextProcessor.parseEmojis(matches[3]);
AppInlineBotsManager.getInlineResults($scope.curDialog.peerID, inlineBot.id, query, '').then(function (botResults) {
getInlineResultsTO = false; getInlineResultsTO = false;
if (curJump != jump) { if (curJump != jump) {
return; return;
@ -2599,6 +2596,13 @@ angular.module('myApp.controllers', ['myApp.i18n'])
if (!qID) { if (!qID) {
return; return;
} }
if (qID.substr(0, 11) == '_switch_pm_') {
var botID = lastInlineBotID;
var startParam = qID.substr(11);
return AppInlineBotsManager.switchToPM($scope.curDialog.peerID, botID, startParam);
}
var options = { var options = {
replyToMsgID: $scope.draftMessage.replyToMessage && $scope.draftMessage.replyToMessage.mid replyToMsgID: $scope.draftMessage.replyToMessage && $scope.draftMessage.replyToMessage.mid
}; };

8
app/js/directives.js

@ -2205,12 +2205,12 @@ angular.module('myApp.directives', ['myApp.filters'])
function link ($scope, element, attrs) { function link ($scope, element, attrs) {
var width = element.attr('width') || 200; var width = element.attr('width') || 200;
var height = element.attr('height') || 200; var height = element.attr('height') || 200;
var apiKey = Config.ExtCredentials.gmaps.api_key;
var zoom = width > 200 ? 15 : 13;
element.attr('src', 'img/blank.gif'); element.attr('src', 'img/blank.gif');
var apiKey = Config.ExtCredentials.gmaps.api_key; var src = 'https://maps.googleapis.com/maps/api/staticmap?sensor=false&center=' + $scope.point['lat'] + ',' + $scope.point['long'] + '&zoom=' + zoom + '&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.downloadByURL(src).then(function (url) { ExternalResourcesManager.downloadByURL(src).then(function (url) {
element.attr('src', url.valueOf()); element.attr('src', url.valueOf());
@ -3342,7 +3342,7 @@ angular.module('myApp.directives', ['myApp.filters'])
result.contentUrl = url; result.contentUrl = url;
}); });
} }
if (result.type == 'gif' && result.document) { if ((result.type == 'gif' || result.type == 'sticker') && result.document) {
AppDocsManager.downloadDoc(result.document.id); AppDocsManager.downloadDoc(result.document.id);
} }
if (result.type == 'photo' && result.photo) { if (result.type == 'photo' && result.photo) {

2
app/js/lib/config.js

File diff suppressed because one or more lines are too long

11
app/js/lib/ng_utils.js

@ -1219,6 +1219,7 @@ angular.module('izhukov.utils', [])
wrapPlainText: wrapPlainText, wrapPlainText: wrapPlainText,
parseEntities: parseEntities, parseEntities: parseEntities,
parseMarkdown: parseMarkdown, parseMarkdown: parseMarkdown,
parseEmojis: parseEmojis,
mergeEntities: mergeEntities mergeEntities: mergeEntities
}; };
@ -1347,6 +1348,16 @@ angular.module('izhukov.utils', [])
return entities; return entities;
} }
function parseEmojis(text) {
return text.replace(/:([a-z0-9\-\+\*_]+?):/gi, function (all, shortcut) {
var emojiCode = EmojiHelper.shortcuts[shortcut];
if (emojiCode !== undefined) {
return EmojiHelper.emojis[emojiCode][0];
}
return all;
});
}
function parseMarkdown (text, entities) { function parseMarkdown (text, entities) {
if (text.indexOf('`') == -1) { if (text.indexOf('`') == -1) {
return text; return text;

2
app/js/lib/schema.tl.txt

@ -677,7 +677,7 @@ messages.getDocumentByHash#338e2464 sha256:bytes size:int mime_type:string = Doc
messages.searchGifs#bf9a776b q:string offset:int = messages.FoundGifs; messages.searchGifs#bf9a776b q:string offset:int = messages.FoundGifs;
messages.getSavedGifs#83bf3d52 hash:int = messages.SavedGifs; messages.getSavedGifs#83bf3d52 hash:int = messages.SavedGifs;
messages.saveGif#327a30cb id:InputDocument unsave:Bool = Bool; messages.saveGif#327a30cb id:InputDocument unsave:Bool = Bool;
messages.getInlineBotResults#94e7b170 flags:# bot:InputUser geo_point:flags.0?InputGeoPoint query:string offset:string = messages.BotResults; messages.getInlineBotResults#514e999d flags:# bot:InputUser peer:InputPeer geo_point:flags.0?InputGeoPoint query:string offset:string = messages.BotResults;
messages.setInlineBotResults#eb5ea206 flags:# gallery:flags.0?true private:flags.1?true query_id:long results:Vector<InputBotInlineResult> cache_time:int next_offset:flags.2?string switch_pm:flags.3?InlineBotSwitchPM = Bool; messages.setInlineBotResults#eb5ea206 flags:# gallery:flags.0?true private:flags.1?true query_id:long results:Vector<InputBotInlineResult> cache_time:int next_offset:flags.2?string switch_pm:flags.3?InlineBotSwitchPM = Bool;
messages.sendInlineBotResult#b16e06fe flags:# broadcast:flags.4?true silent:flags.5?true background:flags.6?true peer:InputPeer reply_to_msg_id:flags.0?int random_id:long query_id:long id:string = Updates; messages.sendInlineBotResult#b16e06fe flags:# broadcast:flags.4?true silent:flags.5?true background:flags.6?true peer:InputPeer reply_to_msg_id:flags.0?int random_id:long query_id:long id:string = Updates;
messages.getMessageEditData#fda68d36 peer:InputPeer id:int = messages.MessageEditData; messages.getMessageEditData#fda68d36 peer:InputPeer id:int = messages.MessageEditData;

32
app/js/services.js

@ -2388,13 +2388,14 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
} }
}) })
.service('AppInlineBotsManager', function ($rootScope, Storage, MtpApiManager, AppMessagesManager, AppDocsManager, AppPhotosManager, RichTextProcessor, AppUsersManager) { .service('AppInlineBotsManager', function ($rootScope, Storage, MtpApiManager, AppMessagesManager, AppDocsManager, AppPhotosManager, RichTextProcessor, AppUsersManager, AppPeersManager) {
var inlineResults = {}; var inlineResults = {};
return { return {
sendInlineResult: sendInlineResult, sendInlineResult: sendInlineResult,
regroupWrappedResults: regroupWrappedResults, regroupWrappedResults: regroupWrappedResults,
switchToPM: switchToPM,
getInlineResults: getInlineResults, getInlineResults: getInlineResults,
getPopularBots: getPopularBots getPopularBots: getPopularBots
}; };
@ -2450,9 +2451,11 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
}); });
} }
function getInlineResults (botID, query, offset) { function getInlineResults (peerID, botID, query, offset) {
return MtpApiManager.invokeApi('messages.getInlineBotResults', { return MtpApiManager.invokeApi('messages.getInlineBotResults', {
flags: 0,
bot: AppUsersManager.getUserInput(botID), bot: AppUsersManager.getUserInput(botID),
peer: AppPeersManager.getInputPeerByID(peerID),
query: query, query: query,
offset: offset offset: offset
}, {timeout: 1, stopTime: -1, noErrorBox: true}).then(function(botResults) { }, {timeout: 1, stopTime: -1, noErrorBox: true}).then(function(botResults) {
@ -2461,6 +2464,10 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
delete botResults.flags; delete botResults.flags;
delete botResults.query_id; delete botResults.query_id;
if (botResults.switch_pm) {
botResults.switch_pm.rText = RichTextProcessor.wrapRichText(botResults.switch_pm.text, {noLinebreaks: true, noLinks: true});
}
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;
@ -2483,24 +2490,35 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
}); });
} }
function switchToPM(fromPeerID, botID, startParam) {
var setHash = {};
var peerString = AppPeersManager.getPeerString(fromPeerID);
setHash['inline_switch_pm' + botID] = {peer: peerString, time: tsNow()};
Storage.set(setHash);
$rootScope.$broadcast('history_focus', {peerString: AppPeersManager.getPeerString(botID)});
AppMessagesManager.startBot(botID, 0, startParam);
}
function regroupWrappedResults (results, rowW, rowH) { function regroupWrappedResults (results, rowW, rowH) {
if (!results || if (!results ||
!results[0] || !results[0] ||
results[0].type != 'photo' && results[0].type != 'gif') { results[0].type != 'photo' && results[0].type != 'gif' && results[0].type != 'sticker') {
return; return;
} }
var ratios = []; var ratios = [];
angular.forEach(results, function (result) { angular.forEach(results, function (result) {
var w, h; var w, h, doc, photo;
if (result._ == 'botInlineMediaResultDocument') { if (result._ == 'botInlineMediaResult') {
if (doc = result.document) {
w = result.document.w; w = result.document.w;
h = result.document.h; h = result.document.h;
} }
else if (result._ == 'botInlineMediaResultPhoto') { else if (photo = result.photo) {
var photoSize = (result.photo.sizes || [])[0]; var photoSize = (photo.sizes || [])[0];
w = photoSize && photoSize.w; w = photoSize && photoSize.w;
h = photoSize && photoSize.h; h = photoSize && photoSize.h;
} }
}
else { else {
w = result.w; w = result.w;
h = result.h; h = result.h;

45
app/less/app.less

@ -2437,11 +2437,16 @@ textarea.im_message_field {
resize: none; resize: none;
} }
.im_inline_placeholder_wrap { .im_inline_placeholder_wrap {
color: #9aa2ab;
position: absolute; position: absolute;
margin-top: 2px; margin-top: 2px;
white-space: nowrap; white-space: nowrap;
pointer-events: none; pointer-events: none;
text-overflow: ellipsis;
display: none; display: none;
width: 100%;
width: ~"calc(100% - 30px)";
overflow: hidden;
} }
.im_inline_placeholder_wrap.active { .im_inline_placeholder_wrap.active {
display: block; display: block;
@ -2449,9 +2454,6 @@ textarea.im_message_field {
.im_inline_placeholder_prefix { .im_inline_placeholder_prefix {
visibility: hidden; visibility: hidden;
} }
.im_inline_placeholder {
color: #9aa2ab;
}
.icon-online { .icon-online {
background: #6ec26d; background: #6ec26d;
@ -3225,6 +3227,21 @@ _:-ms-lang(x), .composer_rich_textarea:empty:focus:before {
} }
} }
.inline_switch_pm {
text-decoration: none !important;
display: block;
font-size: 13px;
font-weight: bold;
line-height: 15px;
padding: 10px 10px;
text-align: center;
color: #52719a;
&:hover {
background: #f2f6fa;
color: #52719a;
}
}
.inline_results_wrap { .inline_results_wrap {
line-height: 0; line-height: 0;
} }
@ -3232,7 +3249,8 @@ _:-ms-lang(x), .composer_rich_textarea:empty:focus:before {
display: block; display: block;
} }
.inline_result_gif, .inline_result_gif,
.inline_result_photo { .inline_result_photo,
.inline_result_sticker {
display: inline-block; display: inline-block;
padding: 1px; padding: 1px;
} }
@ -3251,6 +3269,14 @@ _:-ms-lang(x), .composer_rich_textarea:empty:focus:before {
max-height: 50px; max-height: 50px;
line-height: 0; line-height: 0;
} }
.inline_result_video .inline_article_thumb_wrap {
width: 90px;
height: 50px;
}
.inline_result_video .inline_article_thumb {
max-width: 90px;
max-height: 50px;
}
.inline_article_thumb_initials { .inline_article_thumb_initials {
color: #999; color: #999;
background: #EEE; background: #EEE;
@ -3278,10 +3304,12 @@ _:-ms-lang(x), .composer_rich_textarea:empty:focus:before {
.inline_article_title { .inline_article_title {
color: #222; color: #222;
font-weight: bold; font-weight: bold;
font-size: 12px;
} }
.inline_article_description { .inline_article_description {
color: #808080; color: #808080;
padding-top: 4px; padding-top: 4px;
font-size: 12px;
li a:hover &, li a:hover &,
li.composer_autocomplete_option_active a & { li.composer_autocomplete_option_active a & {
color: #698192; color: #698192;
@ -3289,14 +3317,16 @@ _:-ms-lang(x), .composer_rich_textarea:empty:focus:before {
} }
.composer_dropdown > li.inline_result_gif > a, .composer_dropdown > li.inline_result_gif > a,
.composer_dropdown > li.inline_result_photo > a { .composer_dropdown > li.inline_result_photo > a,
.composer_dropdown > li.inline_result_sticker > a {
padding: 0; padding: 0;
line-height: 0; line-height: 0;
display: block; display: block;
overflow: hidden; overflow: hidden;
} }
li.inline_result_gif.composer_autocomplete_option_active a, li.inline_result_gif.composer_autocomplete_option_active a,
li.inline_result_photo.composer_autocomplete_option_active a { li.inline_result_photo.composer_autocomplete_option_active a,
li.inline_result_sticker.composer_autocomplete_option_active a {
position: relative; position: relative;
.inline_result_ind { .inline_result_ind {
@ -3311,6 +3341,9 @@ li.inline_result_photo.composer_autocomplete_option_active a {
.inline_result_photo .inline_result_photo_image { .inline_result_photo .inline_result_photo_image {
object-fit: cover; object-fit: cover;
} }
.inline_result_sticker img {
object-fit: contain;
}
.inline_result_gif_mtproto, .inline_result_gif_mtproto,
.inline_result_gif_http, .inline_result_gif_http,
.inline_result_photo_mtproto, .inline_result_photo_mtproto,

24
app/partials/desktop/inline_results.html

@ -1,9 +1,11 @@
<a ng-if="botResults.switch_pm !== undefined" class="inline_switch_pm" ng-bind-html="botResults.switch_pm.rText" data-inlineid="_switch_pm_{{botResults.switch_pm.start_param}}"></a>
<ul class="inline_results_wrap composer_dropdown"> <ul class="inline_results_wrap composer_dropdown">
<li class="inline_result_wrap" ng-class="'inline_result_' + result.type" ng-repeat="result in botResults.results track by result.qID" ng-switch="result.type"> <li class="inline_result_wrap" ng-class="'inline_result_' + result.type" ng-repeat="result in botResults.results track by result.qID" ng-switch="result.type">
<a ng-switch-when="gif" class="img_gif_with_progress_wrap" data-inlineid="{{result.qID}}" ng-style="::{width: result.thumbW, height: result.thumbH}" ng-switch="result._"> <a ng-switch-when="gif" class="img_gif_with_progress_wrap" data-inlineid="{{result.qID}}" ng-style="::{width: result.thumbW, height: result.thumbH}" ng-switch="result._">
<div class="inline_result_ind"></div> <div class="inline_result_ind"></div>
<div ng-switch-when="botInlineMediaResultDocument" ng-switch="result.document.url !== undefined" class="inline_result_gif_mtproto"> <div ng-switch-when="botInlineMediaResult" ng-switch="result.document.url !== undefined" class="inline_result_gif_mtproto">
<div ng-switch-when="true" ng-switch="result.document.mime_type == 'video/mp4'"> <div ng-switch-when="true" ng-switch="result.document.mime_type == 'video/mp4'">
<video ng-switch-when="true" width="{{result.thumbW}}" height="{{result.thumbH}}" loop autoplay class="img_gif_video"> <video ng-switch-when="true" width="{{result.thumbW}}" height="{{result.thumbH}}" loop autoplay class="img_gif_video">
<source ng-src="{{result.document.url}}" type="video/mp4"> <source ng-src="{{result.document.url}}" type="video/mp4">
@ -25,9 +27,14 @@
</div> </div>
</a> </a>
<a ng-switch-when="sticker" data-inlineid="{{result.qID}}" ng-style="::{width: result.thumbW, height: result.thumbH}">
<div class="inline_result_ind"></div>
<div class="inline_result_sticker_image" my-load-sticker document="result.document" dim="{width: result.thumbW, height: result.thumbH}"></div>
</a>
<a ng-switch-when="photo" data-inlineid="{{result.qID}}" ng-style="::{width: result.thumbW, height: result.thumbH}" ng-switch="result._"> <a ng-switch-when="photo" data-inlineid="{{result.qID}}" ng-style="::{width: result.thumbW, height: result.thumbH}" ng-switch="result._">
<div class="inline_result_ind"></div> <div class="inline_result_ind"></div>
<div ng-switch-when="botInlineMediaResultPhoto" class="inline_result_photo_mtproto"> <div ng-switch-when="botInlineMediaResult" class="inline_result_photo_mtproto">
<img <img
class="inline_result_photo_image" class="inline_result_photo_image"
my-load-thumb my-load-thumb
@ -35,14 +42,21 @@
ng-style="::{width: result.thumbW, height: result.thumbH}" ng-style="::{width: result.thumbW, height: result.thumbH}"
/> />
</div> </div>
<div ng-switch-default ng-switch="result.contentUrl !== undefined" class="inline_result_photo_http"> <div ng-switch-default class="inline_result_photo_http">
<img ng-switch-default ng-if="result.thumbUrl !== undefined" class="inline_result_photo_image" width="{{result.thumbW}}" height="{{result.thumbH}}" ng-src="{{result.thumbUrl}}" /> <img ng-switch-default ng-if="result.thumbUrl !== undefined" class="inline_result_photo_image" width="{{result.thumbW}}" height="{{result.thumbH}}" ng-src="{{result.thumbUrl}}" />
</div> </div>
</a> </a>
<a ng-switch-default class="inline_result_article clearfix" data-inlineid="{{result.qID}}"> <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 !== undefined"> <div class="inline_article_thumb_wrap pull-left" ng-switch="result.thumbUrl !== undefined ? 'thumb' : (result.send_message.geo ? 'geo' : false)">
<img ng-switch-when="true" class="inline_article_thumb" ng-src="{{result.thumbUrl}}"/> <img ng-switch-when="thumb" class="inline_article_thumb" ng-src="{{result.thumbUrl}}"/>
<img
ng-switch-when="geo"
class="inline_article_thumb"
my-geo-point-map="result.send_message.geo"
width="50"
height="50"
/>
<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">

2
package.json

@ -48,7 +48,7 @@
"gulp-concat": "^2.1.7", "gulp-concat": "^2.1.7",
"gulp-grep-stream": "0.0.2", "gulp-grep-stream": "0.0.2",
"gulp-imagemin": "^2.3.0", "gulp-imagemin": "^2.3.0",
"gulp-less": "^3.0.2", "gulp-less": "^3.0.5",
"gulp-livereload": "^3.0.2", "gulp-livereload": "^3.0.2",
"gulp-load-plugins": "^0.4.0", "gulp-load-plugins": "^0.4.0",
"gulp-manifest": "0.0.3", "gulp-manifest": "0.0.3",

Loading…
Cancel
Save