From 5a4c00de6047ef7a9b22af074d5321251388a8d1 Mon Sep 17 00:00:00 2001 From: Mathias STRASSER Date: Tue, 8 Dec 2015 11:52:27 +0100 Subject: [PATCH 01/22] Replace true with multiple "true" is a wrong attribute value in multiple --- app/partials/desktop/im.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/partials/desktop/im.html b/app/partials/desktop/im.html index b8f04620..193031e3 100644 --- a/app/partials/desktop/im.html +++ b/app/partials/desktop/im.html @@ -200,12 +200,12 @@
- +
- +
@@ -234,4 +234,4 @@ - \ No newline at end of file + From e241dd2785a37448a55656aa400eb68108cfc275 Mon Sep 17 00:00:00 2001 From: Igor Zhukov Date: Mon, 18 Jan 2016 15:43:22 +0300 Subject: [PATCH 02/22] Improved gif download progress --- app/js/directives.js | 15 ++++++++--- app/js/lib/config.js | 3 ++- app/js/lib/mtproto_wrapper.js | 12 +++++---- app/less/app.less | 42 ++++++++++++++++++++++-------- app/partials/desktop/full_gif.html | 5 +--- 5 files changed, 53 insertions(+), 24 deletions(-) diff --git a/app/js/directives.js b/app/js/directives.js index 98a8d431..55befab0 100755 --- a/app/js/directives.js +++ b/app/js/directives.js @@ -1979,7 +1979,7 @@ angular.module('myApp.directives', ['myApp.filters']) }) - .directive('myLoadGif', function(AppDocsManager) { + .directive('myLoadGif', function(AppDocsManager, $timeout) { return { link: link, @@ -1996,6 +1996,15 @@ angular.module('myApp.directives', ['myApp.filters']) var downloadPromise = false; + // Demo + // $scope.document.progress = {enabled: true, percent: 30}; + // $timeout(function () { + // $scope.document.progress.percent = 60; + // }, 3000); + // $timeout(function () { + // $scope.document.progress.percent = 100; + // }, 10000); + $scope.isActive = false; $scope.toggle = function (e) { @@ -3248,8 +3257,8 @@ angular.module('myApp.directives', ['myApp.filters']) var bar = $('.progress-arc-bar', element); var width = attrs.width || 40; - var radius = width * 0.86; - var stroke = width * 0.14; + var radius = width / 2 * 0.86; + var stroke = width / 2 * 0.14; var center = width / 2; $(svgEl).attr('width', width); diff --git a/app/js/lib/config.js b/app/js/lib/config.js index 74a41205..469bfd94 100644 --- a/app/js/lib/config.js +++ b/app/js/lib/config.js @@ -36,7 +36,8 @@ Config.Modes = { webcrypto: location.search.indexOf('webcrypto=0')== -1, packed: location.protocol == 'app:' || location.protocol == 'chrome-extension:', ios_standalone: window.navigator.standalone && navigator.userAgent.match(/iOS|iPhone|iPad/), - chrome_packed: window.chrome && chrome.app && chrome.app.window && true || false + chrome_packed: window.chrome && chrome.app && chrome.app.window && true || false, + memory_only: false }; Config.Navigator = { diff --git a/app/js/lib/mtproto_wrapper.js b/app/js/lib/mtproto_wrapper.js index 103947af..55369e0c 100644 --- a/app/js/lib/mtproto_wrapper.js +++ b/app/js/lib/mtproto_wrapper.js @@ -384,11 +384,13 @@ angular.module('izhukov.mtproto.wrapper', ['izhukov.utils', 'izhukov.mtproto']) } function getFileStorage () { - if (TmpfsFileStorage.isAvailable()) { - return TmpfsFileStorage; - } - if (IdbFileStorage.isAvailable()) { - return IdbFileStorage; + if (!Config.Modes.memory_only) { + if (TmpfsFileStorage.isAvailable()) { + return TmpfsFileStorage; + } + if (IdbFileStorage.isAvailable()) { + return IdbFileStorage; + } } return MemoryFileStorage; } diff --git a/app/less/app.less b/app/less/app.less index 1fa9862c..96fa4867 100644 --- a/app/less/app.less +++ b/app/less/app.less @@ -427,6 +427,7 @@ a { } .progress-arc .progress-arc-bar { stroke: #FFF; + stroke: rgba(255,255,255,0.95); transform-origin: center center; transition: stroke-dasharray 500ms linear; @@ -3553,12 +3554,31 @@ h5 { } /* Gif documents */ -.img_gif_with_progress_wrap { +.img_gif_image_wrap { position: relative; +} +.img_gif_meta { + width: 40px; + height: 40px; + line-height: 0; + position: absolute; + z-index: 2; + border-radius: 50%; overflow: hidden; - float: left; - margin-top: 3px; - max-width: 100%; + margin: 0 auto; + top: 50%; + left: 50%; + margin-left: -20px; + margin-top: -20px; +} +.img_gif_label { + font-weight: bold; + color: #FFF; + display: block; + line-height: 40px; + font-size: 15px; + background: rgba(0,0,0,0.3); + text-align: center; } .img_gif_thumb { @@ -3585,13 +3605,13 @@ h5 { right: 0; padding: 4px; } -.img_gif_label, -.img_gif_size { - padding: 1px 8px; - background: rgba(0,0,0,0.5); - border-radius: 3px; - overflow: hidden; -} +// .img_gif_label, +// .img_gif_size { +// padding: 1px 8px; +// background: rgba(0,0,0,0.5); +// border-radius: 3px; +// overflow: hidden; +// } .img_gif_progress_wrap { position: absolute; diff --git a/app/partials/desktop/full_gif.html b/app/partials/desktop/full_gif.html index 4c96f20e..b857ddc4 100644 --- a/app/partials/desktop/full_gif.html +++ b/app/partials/desktop/full_gif.html @@ -4,10 +4,7 @@
-
-
GIF
-
-
+
GIF
From 1d3857273341e92d96ab4f38edda3c438958e695 Mon Sep 17 00:00:00 2001 From: Igor Zhukov Date: Mon, 18 Jan 2016 15:55:24 +0000 Subject: [PATCH 03/22] Handle add channel state with empty pts --- app/js/services.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/js/services.js b/app/js/services.js index dd036724..cbe48856 100755 --- a/app/js/services.js +++ b/app/js/services.js @@ -2828,6 +2828,9 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) } function addChannelState (channelID, pts) { + if (!pts) { + throw new Error('Add channel state without pts ' + channelID); + } if (channelStates[channelID] === undefined) { channelStates[channelID] = { pts: pts, @@ -2842,9 +2845,6 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) function getChannelState (channelID, pts) { if (channelStates[channelID] === undefined) { - if (!pts) { - throw new Error('Get channel empty state without pts ' + channelID); - } addChannelState(channelID, pts); } return channelStates[channelID]; From 736860a5b2d771b9d5c11500db3a88aaa8ead114 Mon Sep 17 00:00:00 2001 From: Igor Zhukov Date: Mon, 18 Jan 2016 19:39:24 +0000 Subject: [PATCH 04/22] Improved animations --- app/js/app.js | 1 + app/js/controllers.js | 7 +- app/js/directives.js | 63 +++++++++++---- app/less/app.less | 122 +++++++++++++++++------------ app/partials/desktop/full_gif.html | 10 ++- 5 files changed, 132 insertions(+), 71 deletions(-) diff --git a/app/js/app.js b/app/js/app.js index ef8a942f..6b27224c 100644 --- a/app/js/app.js +++ b/app/js/app.js @@ -12,6 +12,7 @@ angular.module('myApp', [ 'ngRoute', 'ngSanitize', 'ngTouch', + 'ngAnimate', 'ui.bootstrap', 'mediaPlayer', 'izhukov.utils', diff --git a/app/js/controllers.js b/app/js/controllers.js index 3ac9887e..080aca43 100644 --- a/app/js/controllers.js +++ b/app/js/controllers.js @@ -1548,13 +1548,18 @@ angular.module('myApp.controllers', ['myApp.i18n']) var target = $event.target; while (target) { - if (target.className.indexOf('im_message_outer_wrap') != -1) { + if (target instanceof SVGElement) { + target = target.parentNode; + continue; + } + if (target.className && target.className.indexOf('im_message_outer_wrap') != -1) { if (Config.Mobile) { return false; } break; } if (Config.Mobile && + target.className && target.className.indexOf('im_message_body') != -1) { break; } diff --git a/app/js/directives.js b/app/js/directives.js index 55befab0..3defe60c 100755 --- a/app/js/directives.js +++ b/app/js/directives.js @@ -1996,6 +1996,8 @@ angular.module('myApp.directives', ['myApp.filters']) var downloadPromise = false; + $scope.isActive = false; + // Demo // $scope.document.progress = {enabled: true, percent: 30}; // $timeout(function () { @@ -2005,8 +2007,6 @@ angular.module('myApp.directives', ['myApp.filters']) // $scope.document.progress.percent = 100; // }, 10000); - $scope.isActive = false; - $scope.toggle = function (e) { if (e && checkClick(e, true)) { AppDocsManager.saveDocFile($scope.document.id); @@ -2017,6 +2017,16 @@ angular.module('myApp.directives', ['myApp.filters']) onContentLoaded(function () { $scope.isActive = !$scope.isActive; $scope.$emit('ui_height'); + + var video = $('video', element)[0]; + if (video) { + if (!$scope.isActive) { + video.pause(); + video.currentTime = 0; + } else { + video.play(); + } + } }) return; } @@ -2030,8 +2040,9 @@ angular.module('myApp.directives', ['myApp.filters']) downloadPromise = AppDocsManager.downloadDoc($scope.document.id); downloadPromise.then(function () { - $scope.isActive = true; - $scope.$emit('ui_height'); + $timeout(function () { + $scope.isActive = true; + }, 200); }) } @@ -3242,7 +3253,25 @@ angular.module('myApp.directives', ['myApp.filters']) }) .directive('myArcProgress', function () { - var html = ''; + var html = +'\ + \ + \ + \ + \ + \ + \ + \ + \ +'; + + function updateProgress (bar, progress, fullLen) { + progress = Math.max(0.0, Math.min(progress, 1.0)); + var minProgress = 0.2; + progress = minProgress + (1 - minProgress) * progress; + bar.css({strokeDasharray: (progress * fullLen) + ', ' + ((1 - progress) * fullLen)}); + } + return { scope: { progress: '=myArcProgress' @@ -3252,8 +3281,13 @@ angular.module('myApp.directives', ['myApp.filters']) .html(html) .addClass('progress-arc-wrap'); + var intermediate = attrs.intermediate && $scope.$eval(attrs.intermediate); + + if (intermediate) { + element.addClass('progress-arc-intermediate'); + } + var svgEl = element[0].firstChild; - var circle = $('.progress-arc-circle', element); var bar = $('.progress-arc-bar', element); var width = attrs.width || 40; @@ -3263,10 +3297,6 @@ angular.module('myApp.directives', ['myApp.filters']) $(svgEl).attr('width', width); $(svgEl).attr('height', width); - circle.attr('cx', center); - circle.attr('cy', center); - circle.attr('r', radius); - circle.css({strokeWidth: stroke}); bar.attr('cx', center); bar.attr('cy', center); @@ -3274,12 +3304,13 @@ angular.module('myApp.directives', ['myApp.filters']) bar.css({strokeWidth: stroke}); var fullLen = 2 * Math.PI * radius; - $scope.$watch('progress', function (newProgress) { - var progress = newProgress / 100.0; - progress = Math.max(0.0, Math.min(progress, 1.0)); - bar.css({strokeDasharray: (progress * fullLen) + ', ' + ((1 - progress) * fullLen)}); - }); - + if (intermediate) { + updateProgress(bar, 0.3, fullLen); + } else { + $scope.$watch('progress', function (newProgress) { + updateProgress(bar, newProgress / 100.0, fullLen); + }); + } } } }) diff --git a/app/less/app.less b/app/less/app.less index 96fa4867..474675b3 100644 --- a/app/less/app.less +++ b/app/less/app.less @@ -420,14 +420,8 @@ a { display: block; border-radius: 100%; } -.progress-arc circle { - stroke-dashoffset: 0; - stroke: rgba(0,0,0,0.3); - stroke-width: 3px; -} .progress-arc .progress-arc-bar { - stroke: #FFF; - stroke: rgba(255,255,255,0.95); + stroke-dashoffset: 0; transform-origin: center center; transition: stroke-dasharray 500ms linear; @@ -435,6 +429,31 @@ a { -moz-animation: infinite_rotation 2s linear infinite; -ms-animation: infinite_rotation 2s linear infinite; animation: infinite_rotation 2s linear infinite; + + stroke: #FFF; + stroke: rgba(255,255,255,0.95); + + .progress-arc-intermediate & { + stroke: url(#grad-intermediate); + } +} +.stop0 { + stop-opacity: 1.0; + // .progress-arc-intermediate-blue & { + stop-color: #68a4d1; + // } +} +.stop60 { + stop-opacity: 1.0; + // .progress-arc-intermediate-blue & { + stop-color: #68a4d1; + // } +} +.stop100 { + stop-opacity: 0.0; + // .progress-arc-intermediate-blue & { + stop-color: #68a4d1; + // } } /* Infinite rotation */ @@ -3556,8 +3575,10 @@ h5 { /* Gif documents */ .img_gif_image_wrap { position: relative; + overflow: hidden; } .img_gif_meta { + background: rgba(0,0,0,0.3); width: 40px; height: 40px; line-height: 0; @@ -3576,61 +3597,58 @@ h5 { color: #FFF; display: block; line-height: 40px; - font-size: 15px; - background: rgba(0,0,0,0.3); + font-size: 13px; text-align: center; } +.icon-cancel { + position: absolute; + top: 50%; + left: 50%; + margin-left: -9px; + margin-top: -1px; + + .icon-bar { + display: block; + width: 18px; + height: 2px; + background: #FFF; + transform-origin: 50% 50%; + + &:first-child { + .transform(rotate(-45deg)); + } + &:last-child { + .transform(translate3d(0,-2px,0) rotate(45deg)); + } + } +} .img_gif_thumb { - -webkit-filter: blur(3px); - -moz-filter: blur(3px); - -o-filter: blur(3px); - -ms-filter: blur(3px); - filter: blur(3px); + -webkit-filter: blur(2px); + -moz-filter: blur(2px); + -o-filter: blur(2px); + -ms-filter: blur(2px); + filter: blur(2px); filter: progid:DXImageTransform.Microsoft.Blur(PixelRadius='3'); - margin: -1px; - padding: 1px; + transform-origin: center center; + -webkit-transform-origin: center center; + -webkit-transform: scale(1.02, 1.02); + transform: scale(1.02, 1.02); + max-width: 100%; height: auto; } -.img_gif_image { - max-width: 100%; + +.img_gif_meta_contents { + transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.2s; } -.img_gif_info_wrap { - color: #fff; - font-size: 10px; - position: absolute; - bottom: 0; - left: 0; - right: 0; - padding: 4px; +.img_gif_meta_contents.ng-leave.ng-leave-active, +.img_gif_meta_contents.ng-enter { + opacity: 0; } -// .img_gif_label, -// .img_gif_size { -// padding: 1px 8px; -// background: rgba(0,0,0,0.5); -// border-radius: 3px; -// overflow: hidden; -// } - -.img_gif_progress_wrap { - position: absolute; - bottom: 0; - left: 0; - right: 0; - - .tg_progress { - background: rgba(0,0,0, 0.6); - border-color: rgba(0,0,0, 0.6); - border-width: 8px; - height: 18px; - border-radius: 0; - } - - .progress-bar { - background: #fff; - height: 2px; - } +.img_gif_meta_contents.ng-leave, +.img_gif_meta_contents.ng-enter.ng-enter-active { + opacity: 1; } .countries_modal_window { diff --git a/app/partials/desktop/full_gif.html b/app/partials/desktop/full_gif.html index b857ddc4..cb02be0d 100644 --- a/app/partials/desktop/full_gif.html +++ b/app/partials/desktop/full_gif.html @@ -3,8 +3,14 @@
-
-
GIF
+
+ + + + +
+
+
GIF
From c4c67d4d6da207756c9c0477d865b22ef78c5217 Mon Sep 17 00:00:00 2001 From: Igor Zhukov Date: Mon, 18 Jan 2016 22:56:14 +0000 Subject: [PATCH 05/22] Added some animations --- app/js/app.js | 2 +- app/js/controllers.js | 12 +++--- app/js/directives.js | 55 ++++++++++++++------------- app/js/messages_manager.js | 5 ++- app/less/app.less | 73 ++++++++++++++++++++++++++---------- app/less/desktop.less | 7 +++- app/partials/desktop/im.html | 25 +++++++----- 7 files changed, 115 insertions(+), 64 deletions(-) diff --git a/app/js/app.js b/app/js/app.js index 6b27224c..1affa791 100644 --- a/app/js/app.js +++ b/app/js/app.js @@ -12,7 +12,7 @@ angular.module('myApp', [ 'ngRoute', 'ngSanitize', 'ngTouch', - 'ngAnimate', + // 'ngAnimate', 'ui.bootstrap', 'mediaPlayer', 'izhukov.utils', diff --git a/app/js/controllers.js b/app/js/controllers.js index 080aca43..d3dc45b1 100644 --- a/app/js/controllers.js +++ b/app/js/controllers.js @@ -1297,14 +1297,14 @@ angular.module('myApp.controllers', ['myApp.i18n']) return; } lessPending = false; - lessActive = true; + $scope.state.lessActive = lessActive = true; var curJump = jump, curLessJump = ++lessJump, limit = 0, backLimit = 20; AppMessagesManager.getHistory($scope.curDialog.peerID, minID, limit, backLimit).then(function (historyResult) { - lessActive = false; + $scope.state.lessActive = lessActive = false; if (curJump != jump || curLessJump != lessJump) return; var i, id; @@ -1345,7 +1345,7 @@ angular.module('myApp.controllers', ['myApp.i18n']) return; } morePending = false; - moreActive = true; + $scope.state.moreActive = moreActive = true; var curJump = jump, curMoreJump = ++moreJump, @@ -1356,7 +1356,7 @@ angular.module('myApp.controllers', ['myApp.i18n']) : AppMessagesManager.getHistory($scope.curDialog.peerID, maxID, limit); getMessagesPromise.then(function (historyResult) { - moreActive = false; + $scope.state.moreActive = moreActive = false; if (curJump != jump || curMoreJump != moreJump) return; angular.forEach(historyResult.history, function (id) { @@ -1403,9 +1403,9 @@ angular.module('myApp.controllers', ['myApp.i18n']) limit = 10; } - moreActive = false; + $scope.state.moreActive = moreActive = false; morePending = false; - lessActive = false; + $scope.state.lessActive = lessActive = false; lessPending = false; var prerenderedLen = peerHistory.messages.length; diff --git a/app/js/directives.js b/app/js/directives.js index 3defe60c..0542b8e0 100755 --- a/app/js/directives.js +++ b/app/js/directives.js @@ -1999,7 +1999,7 @@ angular.module('myApp.directives', ['myApp.filters']) $scope.isActive = false; // Demo - // $scope.document.progress = {enabled: true, percent: 30}; + $scope.document.progress = {enabled: true, percent: 30}; // $timeout(function () { // $scope.document.progress.percent = 60; // }, 3000); @@ -3256,13 +3256,13 @@ angular.module('myApp.directives', ['myApp.filters']) var html = '\ \ - \ - \ + \ + \ \ - \ + \ \ \ - \ + \ '; function updateProgress (bar, progress, fullLen) { @@ -3272,40 +3272,43 @@ angular.module('myApp.directives', ['myApp.filters']) bar.css({strokeDasharray: (progress * fullLen) + ', ' + ((1 - progress) * fullLen)}); } + var num = 0; + return { scope: { progress: '=myArcProgress' }, link: function ($scope, element, attrs) { - element - .html(html) - .addClass('progress-arc-wrap'); - - var intermediate = attrs.intermediate && $scope.$eval(attrs.intermediate); - - if (intermediate) { - element.addClass('progress-arc-intermediate'); - } + var intermediate = !attrs.myArcProgress; + var width = attrs.width || element.width() || 40; + var stroke = attrs.stroke || (width / 2 * 0.14); + var center = width / 2; + var radius = center - stroke; - var svgEl = element[0].firstChild; - var bar = $('.progress-arc-bar', element); + // Doesn't work without unique id for every gradient + var curNum = ++num; - var width = attrs.width || 40; - var radius = width / 2 * 0.86; - var stroke = width / 2 * 0.14; - var center = width / 2; + element + .html(html.replace('%id%', curNum)) + .addClass('progress-arc-wrap') + .addClass(intermediate ? 'progress-arc-intermediate' : 'progress-arc-percent') + .css({width: width, height: width}); - $(svgEl).attr('width', width); - $(svgEl).attr('height', width); + $(element[0].firstChild) + .attr('width', width) + .attr('height', width); - bar.attr('cx', center); - bar.attr('cy', center); - bar.attr('r', radius); - bar.css({strokeWidth: stroke}); + var bar = $('.progress-arc-bar', element); + bar + .attr('cx', center) + .attr('cy', center) + .attr('r', radius) + .css({strokeWidth: stroke}); var fullLen = 2 * Math.PI * radius; if (intermediate) { updateProgress(bar, 0.3, fullLen); + bar.css({stroke: 'url(#grad_intermediate' + curNum + ')'}); } else { $scope.$watch('progress', function (newProgress) { updateProgress(bar, newProgress / 100.0, fullLen); diff --git a/app/js/messages_manager.js b/app/js/messages_manager.js index 6ddd8272..41fbb4e8 100644 --- a/app/js/messages_manager.js +++ b/app/js/messages_manager.js @@ -181,7 +181,10 @@ angular.module('myApp.services') } NotificationsManager.savePeerSettings(peerID, dialog.notify_settings); - ApiUpdatesManager.addChannelState(channelID, dialog.pts); + + if (dialog.pts) { + ApiUpdatesManager.addChannelState(channelID, dialog.pts); + } } function getTopMessages (limit) { diff --git a/app/less/app.less b/app/less/app.less index 474675b3..4fb87269 100644 --- a/app/less/app.less +++ b/app/less/app.less @@ -423,37 +423,40 @@ a { .progress-arc .progress-arc-bar { stroke-dashoffset: 0; transform-origin: center center; - transition: stroke-dasharray 500ms linear; + fill: transparent; - -webkit-animation: infinite_rotation 2s linear infinite; - -moz-animation: infinite_rotation 2s linear infinite; - -ms-animation: infinite_rotation 2s linear infinite; - animation: infinite_rotation 2s linear infinite; + .progress-arc-intermediate & { + stroke: #68a4d1; + + -webkit-animation: infinite_rotation 0.8s linear infinite; + -moz-animation: infinite_rotation 0.8s linear infinite; + -ms-animation: infinite_rotation 0.8s linear infinite; + animation: infinite_rotation 0.8s linear infinite; + } - stroke: #FFF; - stroke: rgba(255,255,255,0.95); + .progress-arc-percent & { + stroke: #FFF; + stroke: rgba(255,255,255,0.95); - .progress-arc-intermediate & { - stroke: url(#grad-intermediate); + transition: stroke-dasharray 500ms linear; + + -webkit-animation: infinite_rotation 2s linear infinite; + -moz-animation: infinite_rotation 2s linear infinite; + -ms-animation: infinite_rotation 2s linear infinite; + animation: infinite_rotation 2s linear infinite; } } .stop0 { stop-opacity: 1.0; - // .progress-arc-intermediate-blue & { - stop-color: #68a4d1; - // } + stop-color: #68a4d1; } .stop60 { stop-opacity: 1.0; - // .progress-arc-intermediate-blue & { - stop-color: #68a4d1; - // } + stop-color: #68a4d1; } .stop100 { stop-opacity: 0.0; - // .progress-arc-intermediate-blue & { - stop-color: #68a4d1; - // } + stop-color: #68a4d1; } /* Infinite rotation */ @@ -1412,7 +1415,8 @@ a.im_dialog_selected { } .im_history { - padding: 20px 0 0 0; + // padding: 20px 0 0 0; + padding: 0; position: relative; } .im_message_unread_split { @@ -2265,6 +2269,37 @@ a.im_message_fwd_photo { display: none; } } +.im_history_loading { + width: 60px; + margin: 0 auto; + visibility: hidden; + + &.vertical-aligned { + visibility: visible; + } +} + +.im_history_loading_more { + display: block; + width: 26px; + margin: 0 auto; + padding: 20px 0 0; + visibility: hidden; + + &.im_history_loading_more_active { + visibility: visible; + } +} +.im_history_loading_less { + display: block; + width: 26px; + margin: 0 auto; + visibility: hidden; + + &.im_history_loading_less_active { + visibility: visible; + } +} .im_send_panel_wrap { margin: 0 auto; diff --git a/app/less/desktop.less b/app/less/desktop.less index b0027535..08b6f42e 100644 --- a/app/less/desktop.less +++ b/app/less/desktop.less @@ -748,7 +748,6 @@ a.footer_link.active:active { line-height: 18px; width: 100%; height: 39px; - padding: 13px 0 8px; overflow: hidden; -webkit-user-select: none; } @@ -758,7 +757,7 @@ a.footer_link.active:active { color: #999; max-width: 556px; margin: 0 auto; - padding: 0 81px 0 85px; + padding: 13px 81px 8px 85px; a.im_history_typing_author { color: #999; @@ -767,6 +766,10 @@ a.footer_link.active:active { } } +.im_history_loading_less { + margin-top: 5px; +} + /* Contacts modal */ .contacts_modal { &_window { diff --git a/app/partials/desktop/im.html b/app/partials/desktop/im.html index b8f04620..4df99cb1 100644 --- a/app/partials/desktop/im.html +++ b/app/partials/desktop/im.html @@ -90,8 +90,8 @@
-
- +
+
@@ -103,14 +103,18 @@
-
- - - - + +
+
+
+
+
+
+
+
@@ -118,9 +122,12 @@
-
+
-
+
+
+
+
From 4c67ba8fb59b7274068c18f700281260f6098b9f Mon Sep 17 00:00:00 2001 From: Igor Zhukov Date: Mon, 18 Jan 2016 22:56:56 +0000 Subject: [PATCH 06/22] Fixed progress --- app/js/directives.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/js/directives.js b/app/js/directives.js index 0542b8e0..09f440d0 100755 --- a/app/js/directives.js +++ b/app/js/directives.js @@ -1999,7 +1999,7 @@ angular.module('myApp.directives', ['myApp.filters']) $scope.isActive = false; // Demo - $scope.document.progress = {enabled: true, percent: 30}; + // $scope.document.progress = {enabled: true, percent: 30}; // $timeout(function () { // $scope.document.progress.percent = 60; // }, 3000); From d9e9be15f975ba94cdd0b2a9a5a7e63451c6dd11 Mon Sep 17 00:00:00 2001 From: Igor Zhukov Date: Mon, 18 Jan 2016 22:59:00 +0000 Subject: [PATCH 07/22] Sync translations --- app/js/locales/de-de.json | 3 +++ app/js/locales/it-it.json | 3 +++ app/js/locales/pt-br.json | 3 +++ app/js/locales/ru-ru.json | 9 ++++++--- 4 files changed, 15 insertions(+), 3 deletions(-) diff --git a/app/js/locales/de-de.json b/app/js/locales/de-de.json index 95e434be..0acd9c0d 100644 --- a/app/js/locales/de-de.json +++ b/app/js/locales/de-de.json @@ -253,6 +253,7 @@ "conversation_media_video": "Video", "conversation_media_document": "Datei", "conversation_media_sticker": "Sticker", + "conversation_media_gif": "GIF", "conversation_media_audio": "Audio", "conversation_media_location": "Standort", "conversation_media_contact": "Kontakt", @@ -472,7 +473,9 @@ "password_recover_submit": "Absenden", "login_controller_unknown_country": "Unbekannt", "message_forwarded_message": "Weitergeleitete Nachricht", + "message_via_bot": "via {bot}", "message_forwarded_message_mobile": "Weitergeleitet von {from}, {date}", + "message_forwarded_via_message_mobile": "Weitergeleitet von {from} via {bot}, {date}", "message_attach_audio_message": "Sprachnachricht", "message_attach_audio_play": "Abspielen", "message_attach_document_open": "Öffnen", diff --git a/app/js/locales/it-it.json b/app/js/locales/it-it.json index dc00c2d5..a111bb45 100644 --- a/app/js/locales/it-it.json +++ b/app/js/locales/it-it.json @@ -253,6 +253,7 @@ "conversation_media_video": "Video", "conversation_media_document": "File", "conversation_media_sticker": "Sticker", + "conversation_media_gif": "GIF", "conversation_media_audio": "Audio", "conversation_media_location": "Posizione", "conversation_media_contact": "Contatto", @@ -472,7 +473,9 @@ "password_recover_submit": "Invia", "login_controller_unknown_country": "Sconosciuto", "message_forwarded_message": "Messaggio inoltrato", + "message_via_bot": "via {bot}", "message_forwarded_message_mobile": "Inoltrato da {from},{date}", + "message_forwarded_via_message_mobile": "Inoltrato da {from} via {bot}, {date}", "message_attach_audio_message": "Nota vocale", "message_attach_audio_play": "Riproduci", "message_attach_document_open": "Apri", diff --git a/app/js/locales/pt-br.json b/app/js/locales/pt-br.json index 19080847..f9beec8a 100644 --- a/app/js/locales/pt-br.json +++ b/app/js/locales/pt-br.json @@ -253,6 +253,7 @@ "conversation_media_video": "Vídeo", "conversation_media_document": "Arquivo", "conversation_media_sticker": "Sticker", + "conversation_media_gif": "GIF", "conversation_media_audio": "Áudio", "conversation_media_location": "Localização", "conversation_media_contact": "Contato", @@ -472,7 +473,9 @@ "password_recover_submit": "Enviar", "login_controller_unknown_country": "Desconhecido", "message_forwarded_message": "Encaminhar mensagem", + "message_via_bot": "via {bot}", "message_forwarded_message_mobile": "Encaminhado de {from}, {date}", + "message_forwarded_via_message_mobile": "Encaminhado de {from} via {bot}, {date}", "message_attach_audio_message": "Mensagem de voz", "message_attach_audio_play": "Tocar", "message_attach_document_open": "Abrir", diff --git a/app/js/locales/ru-ru.json b/app/js/locales/ru-ru.json index fccd9c81..293b7b8f 100644 --- a/app/js/locales/ru-ru.json +++ b/app/js/locales/ru-ru.json @@ -251,8 +251,9 @@ "conversation_you": "Вы", "conversation_media_photo": "Фотография", "conversation_media_video": "Видео", - "conversation_media_document": "File", + "conversation_media_document": "Файл", "conversation_media_sticker": "Стикер", + "conversation_media_gif": "GIF", "conversation_media_audio": "Аудио", "conversation_media_location": "Местоположение", "conversation_media_contact": "Контакт", @@ -357,7 +358,7 @@ "head_edit_messages": "Редактировать сообщения", "head_media_photos": "Фотографии", "head_media_video": "Видео", - "head_media_documents": "Files", + "head_media_documents": "Файлы", "head_media_audio": "Голосовые сообщения", "head_about": "О приложении", "head_clear_all": "Очистить всё", @@ -393,7 +394,7 @@ "im_media": "Медиа", "im_media_photos": "Фотографии", "im_media_video": "Видео", - "im_media_documents": "Files", + "im_media_documents": "Файлы", "im_media_audio": "Голосовые сообщения", "im_pluralize_participants": "{'one': '{} участник', 'few': '{} участника', 'many': '{} участников', 'other': '{} участников'}", "im_show_recent_messages": "Показать последние сообщения", @@ -472,7 +473,9 @@ "password_recover_submit": "Отправить", "login_controller_unknown_country": "Неизвестно", "message_forwarded_message": "Пересланное сообщение", + "message_via_bot": "via {bot}", "message_forwarded_message_mobile": "Переслано от {from}, {date}", + "message_forwarded_via_message_mobile": "Forwarded from {from} via {bot}, {date}", "message_attach_audio_message": "Запись голоса", "message_attach_audio_play": "Воспроизвести", "message_attach_document_open": "Открыть", From 53bf2858006132fa9dc0c3c0e80d7be08d8c52b6 Mon Sep 17 00:00:00 2001 From: Igor Zhukov Date: Tue, 19 Jan 2016 23:33:51 +0300 Subject: [PATCH 08/22] Fixed glitch on jump --- app/js/controllers.js | 2 +- app/js/directives.js | 1 - app/partials/desktop/im.html | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/app/js/controllers.js b/app/js/controllers.js index d3dc45b1..2964f879 100644 --- a/app/js/controllers.js +++ b/app/js/controllers.js @@ -1193,7 +1193,7 @@ angular.module('myApp.controllers', ['myApp.i18n']) $scope.historyState.typing.splice(0, $scope.historyState.typing.length); $scope.$broadcast('ui_peer_change'); $scope.$broadcast('ui_history_change'); - safeReplaceObject($scope.state, {loaded: true, empty: !peerHistory.messages.length}); + safeReplaceObject($scope.state, {loaded: true, empty: !peerHistory.messages.length, mayBeHasMore: true}); updateBotActions(); updateChannelActions(); diff --git a/app/js/directives.js b/app/js/directives.js index 09f440d0..e8496750 100755 --- a/app/js/directives.js +++ b/app/js/directives.js @@ -1412,7 +1412,6 @@ angular.module('myApp.directives', ['myApp.filters']) $(historyMessagesEl).css({marginTop: 0}); var marginTop = scrollableWrap.offsetHeight - historyMessagesEl.offsetHeight - - 20 - (Config.Mobile ? 0 : 39); if (historyMessagesEl.offsetHeight > 0 && marginTop > 0) { diff --git a/app/partials/desktop/im.html b/app/partials/desktop/im.html index 4df99cb1..2c3a0c41 100644 --- a/app/partials/desktop/im.html +++ b/app/partials/desktop/im.html @@ -112,7 +112,7 @@
-
+
From 55b980bd0b23e3aac8022aea0c2ab222bd045556 Mon Sep 17 00:00:00 2001 From: Igor Zhukov Date: Wed, 20 Jan 2016 00:49:50 +0300 Subject: [PATCH 09/22] Bugfixes --- app/js/app.js | 9 +++++++-- app/js/directives.js | 2 ++ app/js/lib/config.js | 1 + app/js/lib/utils.js | 14 ++++++++++++++ app/js/services.js | 12 ++++++++++-- app/less/app.less | 4 ++++ app/partials/desktop/full_gif.html | 2 +- app/partials/desktop/im.html | 4 ++-- 8 files changed, 41 insertions(+), 7 deletions(-) diff --git a/app/js/app.js b/app/js/app.js index 1affa791..6f90ca53 100644 --- a/app/js/app.js +++ b/app/js/app.js @@ -7,12 +7,17 @@ 'use strict'; +var extraModules = []; +if (Config.Modes.animations) { + extraModules.push('ngAnimate'); +} + + // Declare app level module which depends on filters, and services angular.module('myApp', [ 'ngRoute', 'ngSanitize', 'ngTouch', - // 'ngAnimate', 'ui.bootstrap', 'mediaPlayer', 'izhukov.utils', @@ -25,7 +30,7 @@ angular.module('myApp', [ PRODUCTION_ONLY_END*/ 'myApp.directives', 'myApp.controllers' -]). +].concat(extraModules)). config(['$locationProvider', '$routeProvider', '$compileProvider', 'StorageProvider', function($locationProvider, $routeProvider, $compileProvider, StorageProvider) { $compileProvider.imgSrcSanitizationWhitelist(/^\s*(https?|ftp|file|blob|filesystem|chrome-extension|app):|data:image\//); diff --git a/app/js/directives.js b/app/js/directives.js index e8496750..88731a29 100755 --- a/app/js/directives.js +++ b/app/js/directives.js @@ -1099,6 +1099,7 @@ angular.module('myApp.directives', ['myApp.filters']) historyEl = $('.im_history', element)[0], scrollableWrap = $('.im_history_scrollable_wrap', element)[0], scrollable = $('.im_history_scrollable', element)[0], + emptyWrapEl = $('.im_history_empty_wrap', element)[0], bottomPanelWrap = $('.im_bottom_panel_wrap', element)[0], sendFormWrap = $('.im_send_form_wrap', element)[0], headWrap = $('.tg_page_head')[0], @@ -1412,6 +1413,7 @@ angular.module('myApp.directives', ['myApp.filters']) $(historyMessagesEl).css({marginTop: 0}); var marginTop = scrollableWrap.offsetHeight - historyMessagesEl.offsetHeight + - emptyWrapEl.offsetHeight - (Config.Mobile ? 0 : 39); if (historyMessagesEl.offsetHeight > 0 && marginTop > 0) { diff --git a/app/js/lib/config.js b/app/js/lib/config.js index 469bfd94..8b6b5d79 100644 --- a/app/js/lib/config.js +++ b/app/js/lib/config.js @@ -37,6 +37,7 @@ Config.Modes = { packed: location.protocol == 'app:' || location.protocol == 'chrome-extension:', ios_standalone: window.navigator.standalone && navigator.userAgent.match(/iOS|iPhone|iPad/), chrome_packed: window.chrome && chrome.app && chrome.app.window && true || false, + animations: true, memory_only: false }; diff --git a/app/js/lib/utils.js b/app/js/lib/utils.js index 1cc8b783..a1077165 100644 --- a/app/js/lib/utils.js +++ b/app/js/lib/utils.js @@ -289,7 +289,21 @@ function scrollToNode (scrollable, node, scroller) { } } +if (Config.Modes.animations && + typeof window.requestAnimationFrame == 'function') { + window.onAnimationFrameCallback = function (cb) { + return (function () { + window.requestAnimationFrame(cb); + }); + }; +} else { + window.onAnimationFrameCallback = function (cb) { + return cb; + }; +} + function onContentLoaded (cb) { + cb = onAnimationFrameCallback(cb); setZeroTimeout(cb); } diff --git a/app/js/services.js b/app/js/services.js index cbe48856..7e339210 100755 --- a/app/js/services.js +++ b/app/js/services.js @@ -1907,7 +1907,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) } }) -.service('AppDocsManager', function ($sce, $rootScope, $modal, $window, $q, RichTextProcessor, MtpApiFileManager, FileManager, qSync) { +.service('AppDocsManager', function ($sce, $rootScope, $modal, $window, $q, $timeout, RichTextProcessor, MtpApiFileManager, FileManager, qSync) { var docs = {}, docsForHistory = {}, windowW = $(window).width(), @@ -2096,13 +2096,16 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) }); downloadPromise.then(function (blob) { - delete historyDoc.progress; if (blob) { FileManager.getFileCorrectUrl(blob, doc.mime_type).then(function (url) { historyDoc.url = $sce.trustAsResourceUrl(url); }) historyDoc.downloaded = true; } + historyDoc.progress.percent = 100; + $timeout(function () { + delete historyDoc.progress; + }); console.log('file save done'); }, function (e) { console.log('document download failed', e); @@ -2283,6 +2286,11 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) var applied = false; var started = false; + // $rootScope.$on('apiUpdate', function (e, update) { + // if (update._ == 'updateStickerSets') { + // } + // }); + return { start: start, openStickersetLink: openStickersetLink, diff --git a/app/less/app.less b/app/less/app.less index 4fb87269..4dd37b09 100644 --- a/app/less/app.less +++ b/app/less/app.less @@ -1529,6 +1529,9 @@ div.im_message_video_thumb { .image-2x('../img/icons/IconsetW.png', 42px, 1171px); background-position: 0 -590px; + .is_2x & { + background-position: 0 -591px; + } } .im_message_geopoint { @@ -3626,6 +3629,7 @@ h5 { left: 50%; margin-left: -20px; margin-top: -20px; + pointer-events: none; } .img_gif_label { font-weight: bold; diff --git a/app/partials/desktop/full_gif.html b/app/partials/desktop/full_gif.html index cb02be0d..ae2f3024 100644 --- a/app/partials/desktop/full_gif.html +++ b/app/partials/desktop/full_gif.html @@ -10,7 +10,7 @@
-
GIF
+
GIF
diff --git a/app/partials/desktop/im.html b/app/partials/desktop/im.html index 2c3a0c41..22a72e87 100644 --- a/app/partials/desktop/im.html +++ b/app/partials/desktop/im.html @@ -104,7 +104,7 @@
-
+
@@ -112,7 +112,7 @@
-
+
From d0a2f7b587094a909b3083ccc25ac76ca688a6fe Mon Sep 17 00:00:00 2001 From: Igor Zhukov Date: Thu, 21 Jan 2016 14:15:21 +0300 Subject: [PATCH 10/22] stickers wip --- app/js/controllers.js | 2 +- app/js/services.js | 91 ++++++++++++++++--------------------------- 2 files changed, 34 insertions(+), 59 deletions(-) diff --git a/app/js/controllers.js b/app/js/controllers.js index 2964f879..271f3e46 100644 --- a/app/js/controllers.js +++ b/app/js/controllers.js @@ -4661,7 +4661,7 @@ angular.module('myApp.controllers', ['myApp.i18n']) $scope.$broadcast('ui_height'); $scope.stickersetLoaded = true; $scope.stickerset = result.set; - $scope.stickersetInstalled = result.installed; + $scope.stickersetInstalled = result.set.pFlags.installed; $scope.documents = result.documents; $scope.stickerEmojis = {}; diff --git a/app/js/services.js b/app/js/services.js index 7e339210..53cc3797 100755 --- a/app/js/services.js +++ b/app/js/services.js @@ -2281,15 +2281,17 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) var currentStickers = []; var currentStickersets = []; - var installedStickersets = {}; var stickersetItems = {}; var applied = false; var started = false; - // $rootScope.$on('apiUpdate', function (e, update) { - // if (update._ == 'updateStickerSets') { - // } - // }); + $rootScope.$on('apiUpdate', function (e, update) { + if (update._ == 'updateStickerSets') { + // Storage.remove('all_stickers').then(function () { + // getStickers(true); + // }); + } + }); return { start: start, @@ -2298,8 +2300,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) installStickerset: installStickerset, pushPopularSticker: pushPopularSticker, getStickers: getStickers, - getStickerset: getStickerset, - getStickersImages: getStickersImages + getStickerset: getStickerset }; function start () { @@ -2354,32 +2355,24 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) function processRawStickers(stickers) { if (applied !== stickers.hash) { applied = stickers.hash; - var i, j, len1, len2, doc, set, setItems, fullSet; + var i, j, len1, len2, doc, set, docIDs, documents; currentStickersets = []; - currentStickers = []; len1 = stickers.sets.length; for (i = 0; i < len1; i++) { set = stickers.sets[i]; - fullSet = stickers.fullSets[set.id]; - len2 = fullSet.documents.length; - setItems = []; + if (set.pFlags.disabled) { + continue; + } + documents = stickers.fullSets[set.id].documents; + len2 = documents.length; + docIDs = []; for (j = 0; j < len2; j++) { - doc = fullSet.documents[j]; + doc = documents[j]; AppDocsManager.saveDoc(doc); - currentStickers.push(doc.id); - setItems.push(doc.id); + docIDs.push(doc.id); } - currentStickersets.push({ - id: set.id, - title: set.title, - short_name: set.short_name, - installed: (set.flags & (1 << 0)) > 0, - disabled: (set.flags & (1 << 1)) > 0, - official: (set.flags & (1 << 2)) > 0, - docIDs: setItems - }); - installedStickersets[set.id] = true; + set.docIDs = docIDs; } } @@ -2387,20 +2380,17 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) var resultStickersets = currentStickersets; if (popularStickers.length) { resultStickersets = currentStickersets.slice(); - var setItems = []; + var docIDs = []; var i, len; for (i = 0, len = popularStickers.length; i < len; i++) { - setItems.push(popularStickers[i].id); + docIDs.push(popularStickers[i].id); } resultStickersets.unshift({ id: 0, title: _('im_stickers_tab_recent_raw'), short_name: '', - installed: true, - disabled: false, - official: false, - docIDs: setItems - }) + docIDs: docIDs + }); } return resultStickersets; @@ -2465,26 +2455,6 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) return $q.all(promises); } - function downloadStickerThumb (docID) { - var doc = AppDocsManager.getDoc(docID); - var thumbLocation = angular.copy(doc.thumb.location); - thumbLocation.sticker = true; - return MtpApiFileManager.downloadSmallFile(thumbLocation).then(function (blob) { - return { - id: doc.id, - src: FileManager.getUrl(blob, 'image/webp') - }; - }); - } - - function getStickersImages () { - var promises = []; - angular.forEach(currentStickers, function (docID) { - promises.push(downloadStickerThumb (docID)); - }); - return $q.all(promises); - } - function getStickerset (inputStickerset) { return MtpApiManager.invokeApi('messages.getStickerSet', { stickerset: inputStickerset @@ -2492,7 +2462,6 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) for (var i = 0; i < result.documents.length; i++) { AppDocsManager.saveDoc(result.documents[i]); } - result.installed = installedStickersets[result.set.id] !== undefined; return result; }); } @@ -2511,13 +2480,19 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) disabled: false }).then(function (result) { if (uninstall) { - delete installedStickersets[set.id]; + } else { - installedStickersets[set.id] = true; + ApiUpdatesManager.processUpdateMessage({ + _: 'updateShort', + update: { + _: 'updateNewStickerSet', + channel_id: channelID, + messages: msgIDs, + pts: affectedMessages.pts, + pts_count: affectedMessages.pts_count + } + }); } - Storage.remove('all_stickers').then(function () { - getStickers(true); - }); }); } From 39623095458992f00274d186c72d72d1c8914df8 Mon Sep 17 00:00:00 2001 From: Igor Zhukov Date: Thu, 21 Jan 2016 22:47:00 +0000 Subject: [PATCH 11/22] Improved stickers updates logic --- app/js/controllers.js | 7 +++- app/js/services.js | 98 ++++++++++++++++++++++++++++++++----------- 2 files changed, 78 insertions(+), 27 deletions(-) diff --git a/app/js/controllers.js b/app/js/controllers.js index 271f3e46..b79009c4 100644 --- a/app/js/controllers.js +++ b/app/js/controllers.js @@ -4657,11 +4657,14 @@ angular.module('myApp.controllers', ['myApp.i18n']) .controller('StickersetModalController', function ($scope, $rootScope, $modalInstance, MtpApiManager, RichTextProcessor, AppStickersManager, AppDocsManager, AppMessagesManager, LocationParamsService) { $scope.slice = {limit: 20, limitDelta: 20}; + var fullSet; + AppStickersManager.getStickerset($scope.inputStickerset).then(function (result) { $scope.$broadcast('ui_height'); $scope.stickersetLoaded = true; + fullSet = result; $scope.stickerset = result.set; - $scope.stickersetInstalled = result.set.pFlags.installed; + $scope.stickersetInstalled = result.set.pFlags.installed == true; $scope.documents = result.documents; $scope.stickerEmojis = {}; @@ -4676,7 +4679,7 @@ angular.module('myApp.controllers', ['myApp.i18n']) }); $scope.toggleInstalled = function (installed) { - AppStickersManager.installStickerset($scope.stickerset, !installed).then(function () { + AppStickersManager.installStickerset(fullSet, !installed).then(function () { $scope.stickersetInstalled = installed; }) }; diff --git a/app/js/services.js b/app/js/services.js index 53cc3797..92777ad7 100755 --- a/app/js/services.js +++ b/app/js/services.js @@ -1222,6 +1222,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) }); break; } + return $q.reject(error); }); } @@ -2277,20 +2278,69 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) } }) -.service('AppStickersManager', function ($q, $rootScope, $modal, _, FileManager, MtpApiManager, MtpApiFileManager, AppDocsManager, Storage) { +.service('AppStickersManager', function ($q, $rootScope, $modal, _, FileManager, MtpApiManager, MtpApiFileManager, AppDocsManager, Storage, ApiUpdatesManager) { - var currentStickers = []; - var currentStickersets = []; - var stickersetItems = {}; - var applied = false; var started = false; + var applied = false; + var currentStickerSets = []; $rootScope.$on('apiUpdate', function (e, update) { + var rewriteCached = false; if (update._ == 'updateStickerSets') { - // Storage.remove('all_stickers').then(function () { - // getStickers(true); - // }); + getStickers(true); + return true; } + if (update._ != 'updateNewStickerSet' && + update._ != 'updateDelStickerSet' && + update._ != 'updateStickerSetsOrder') { + return false; + } + + return Storage.get('all_stickers').then(function (stickers) { + if (stickers && + stickers.layer == Config.Schema.API.layer) { + switch (update._) { + case 'updateNewStickerSet': + var fullSet = update.stickerset; + var set = fullSet.set; + set.pFlags.installed = true; + stickers.fullSets[set.id] = fullSet; + var pos = false; + for (var i = 0, len = stickers.sets.length; i < len; i++) { + if (stickers.sets[i].id == set.id) { + pos = i; + break; + } + } + if (pos !== false) { + stickers.sets.splice(pos, 1); + } + stickers.sets.unshift(set); + break; + + case 'updateDelStickerSet': + for (var i = 0, len = stickers.sets.length; i < len; i++) { + if (stickers.sets[i].id == update.id) { + stickers.sets.splice(i, 1); + break; + } + } + delete stickers.fullSets[update.id]; + break; + + case 'updateStickerSetsOrder': + var order = update.order; + stickers.sets.sort(function (a, b) { + return order.indexOf(a.id) - order.indexOf(b.id); + }); + break; + } + Storage.set({all_stickers: stickers}).then(function () { + getStickers(true) + }); + } + }); + }); return { @@ -2357,7 +2407,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) applied = stickers.hash; var i, j, len1, len2, doc, set, docIDs, documents; - currentStickersets = []; + currentStickerSets = []; len1 = stickers.sets.length; for (i = 0; i < len1; i++) { set = stickers.sets[i]; @@ -2373,13 +2423,14 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) docIDs.push(doc.id); } set.docIDs = docIDs; + currentStickerSets.push(set); } } return getPopularStickers().then(function (popularStickers) { - var resultStickersets = currentStickersets; + var resultStickersets = currentStickerSets; if (popularStickers.length) { - resultStickersets = currentStickersets.slice(); + resultStickersets = currentStickerSets.slice(); var docIDs = []; var i, len; for (i = 0, len = popularStickers.length; i < len; i++) { @@ -2393,6 +2444,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) }); } + console.log('stickers', resultStickersets); return resultStickersets; }); } @@ -2466,33 +2518,29 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) }); } - function installStickerset (set, uninstall) { + function installStickerset (fullSet, uninstall) { var method = uninstall ? 'messages.uninstallStickerSet' : 'messages.installStickerSet'; var inputStickerset = { _: 'inputStickerSetID', - id: set.id, - access_hash: set.access_hash + id: fullSet.set.id, + access_hash: fullSet.set.access_hash }; return MtpApiManager.invokeApi(method, { stickerset: inputStickerset, disabled: false }).then(function (result) { + var update; if (uninstall) { - + update = {_: 'updateDelStickerSet', id: fullSet.set.id}; } else { - ApiUpdatesManager.processUpdateMessage({ - _: 'updateShort', - update: { - _: 'updateNewStickerSet', - channel_id: channelID, - messages: msgIDs, - pts: affectedMessages.pts, - pts_count: affectedMessages.pts_count - } - }); + update = {_: 'updateNewStickerSet', stickerset: fullSet}; } + ApiUpdatesManager.processUpdateMessage({ + _: 'updateShort', + update: update + }); }); } From 64efe888806d6d7134f6e2857ed0dde29e4daa03 Mon Sep 17 00:00:00 2001 From: Igor Zhukov Date: Fri, 22 Jan 2016 16:02:35 +0300 Subject: [PATCH 12/22] Supported stickers real-time update --- app/js/directives.js | 5 + app/js/message_composer.js | 8 +- app/js/services.js | 248 +++++++++++++++++++------------------ 3 files changed, 142 insertions(+), 119 deletions(-) diff --git a/app/js/directives.js b/app/js/directives.js index 88731a29..f3eb85d9 100755 --- a/app/js/directives.js +++ b/app/js/directives.js @@ -1493,6 +1493,11 @@ angular.module('myApp.directives', ['myApp.filters']) } }); + $scope.$on('stickers_changed', function () { + emojiTooltip.onStickersChanged(); + }); + + var composerEmojiPanel; if (emojiPanel) { composerEmojiPanel = new EmojiPanel(emojiPanel, { diff --git a/app/js/message_composer.js b/app/js/message_composer.js index 3cbea068..1ba65ce3 100644 --- a/app/js/message_composer.js +++ b/app/js/message_composer.js @@ -364,7 +364,7 @@ EmojiTooltip.prototype.createTooltip = function () { EmojiTooltip.prototype.selectCategory = function (cat, force) { - if (this.cat === cat && !force) { + if (!this.tab && this.cat === cat && !force) { return false; } $('.active', this.categoriesEl).removeClass('active'); @@ -566,6 +566,12 @@ EmojiTooltip.prototype.onStickersScroll = function (scrollable, scrollTop) { this.activateStickerCategory(); }; +EmojiTooltip.prototype.onStickersChanged = function () { + if (this.tab) { + this.updateStickersContents(true); + } +}; + EmojiTooltip.prototype.activateStickerCategory = function () { var categoriesEl = this.categoriesEl[1]; var categoryEl = categoriesEl.childNodes[this.cat]; diff --git a/app/js/services.js b/app/js/services.js index 92777ad7..ab4d4133 100755 --- a/app/js/services.js +++ b/app/js/services.js @@ -2285,71 +2285,73 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) var currentStickerSets = []; $rootScope.$on('apiUpdate', function (e, update) { - var rewriteCached = false; - if (update._ == 'updateStickerSets') { - getStickers(true); - return true; - } - if (update._ != 'updateNewStickerSet' && + if (update._ != 'updateStickerSets' && + update._ != 'updateNewStickerSet' && update._ != 'updateDelStickerSet' && update._ != 'updateStickerSetsOrder') { return false; } return Storage.get('all_stickers').then(function (stickers) { - if (stickers && - stickers.layer == Config.Schema.API.layer) { - switch (update._) { - case 'updateNewStickerSet': - var fullSet = update.stickerset; - var set = fullSet.set; - set.pFlags.installed = true; - stickers.fullSets[set.id] = fullSet; - var pos = false; - for (var i = 0, len = stickers.sets.length; i < len; i++) { - if (stickers.sets[i].id == set.id) { - pos = i; - break; - } - } - if (pos !== false) { - stickers.sets.splice(pos, 1); + if (!stickers || + stickers.layer != Config.Schema.API.layer) { + $rootScope.$broadcast('stickers_changed'); + } + switch (update._) { + case 'updateNewStickerSet': + var fullSet = update.stickerset; + var set = fullSet.set; + var pos = false; + for (var i = 0, len = stickers.sets.length; i < len; i++) { + if (stickers.sets[i].id == set.id) { + pos = i; + break; } - stickers.sets.unshift(set); - break; + } + if (pos !== false) { + stickers.sets.splice(pos, 1); + } + set.pFlags.installed = true; + stickers.sets.unshift(set); + stickers.fullSets[set.id] = fullSet; + break; - case 'updateDelStickerSet': - for (var i = 0, len = stickers.sets.length; i < len; i++) { - if (stickers.sets[i].id == update.id) { - stickers.sets.splice(i, 1); - break; - } + case 'updateDelStickerSet': + var set; + for (var i = 0, len = stickers.sets.length; i < len; i++) { + set = stickers.sets[i]; + if (set.id == update.id) { + set.pFlags.installed = false; + stickers.sets.splice(i, 1); + break; } - delete stickers.fullSets[update.id]; - break; + } + delete stickers.fullSets[update.id]; + break; - case 'updateStickerSetsOrder': - var order = update.order; - stickers.sets.sort(function (a, b) { - return order.indexOf(a.id) - order.indexOf(b.id); - }); - break; - } - Storage.set({all_stickers: stickers}).then(function () { - getStickers(true) - }); + case 'updateStickerSetsOrder': + var order = update.order; + stickers.sets.sort(function (a, b) { + return order.indexOf(a.id) - order.indexOf(b.id); + }); + break; } + stickers.hash = getStickerSetsHash(stickers.sets); + stickers.date = 0; + Storage.set({all_stickers: stickers}).then(function () { + $rootScope.$broadcast('stickers_changed'); + }); }); }); return { start: start, + getStickers: getStickers, openStickersetLink: openStickersetLink, openStickerset: openStickerset, installStickerset: installStickerset, pushPopularSticker: pushPopularSticker, - getStickers: getStickers, getStickerset: getStickerset }; @@ -2360,46 +2362,38 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) } } - function getPopularStickers () { - return Storage.get('stickers_popular').then(function (popStickers) { - var result = []; - var i, len, docID; - if (popStickers && popStickers.length) { - for (i = 0, len = popStickers.length; i < len; i++) { - docID = popStickers[i][0]; - if (AppDocsManager.hasDoc(docID)) { - result.push({id: docID, rate: popStickers[i][1]}); - } + function getStickers (force) { + return Storage.get('all_stickers').then(function (stickers) { + var layer = Config.Schema.API.layer; + if (stickers.layer != layer) { + stickers = false; + } + if (stickers && stickers.date > tsNow(true) && !force) { + return processRawStickers(stickers); + } + return MtpApiManager.invokeApi('messages.getAllStickers', { + hash: stickers && stickers.hash || '' + }).then(function (newStickers) { + var notModified = newStickers._ == 'messages.allStickersNotModified'; + if (notModified) { + newStickers = stickers; } - }; - return result; - }); - } + newStickers.date = tsNow(true) + 3600; + newStickers.layer = layer; + delete newStickers._; - function pushPopularSticker (id) { - getPopularStickers().then(function (popularStickers) { - var exists = false; - var count = popularStickers.length; - var result = []; - for (var i = 0; i < count; i++) { - if (popularStickers[i].id == id) { - exists = true; - popularStickers[i].rate++; + if (notModified) { + Storage.set({all_stickers: newStickers}); + return processRawStickers(newStickers); } - result.push([popularStickers[i].id, popularStickers[i].rate]); - } - if (exists) { - result.sort(function (a, b) { - return b[1] - a[1]; + + return getStickerSets(newStickers, stickers && stickers.fullSets).then(function () { + Storage.set({all_stickers: newStickers}); + return processRawStickers(newStickers); }); - } else { - if (result.length > 15) { - result = result.slice(0, 15); - } - result.push([id, 1]); - } - ConfigStorage.set({stickers_popular: result}); - }); + + }); + }) } function processRawStickers(stickers) { @@ -2443,49 +2437,13 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) docIDs: docIDs }); } - - console.log('stickers', resultStickersets); return resultStickersets; }); } - function getStickers (force) { - return Storage.get('all_stickers').then(function (stickers) { - var layer = Config.Schema.API.layer; - if (stickers.layer != layer) { - stickers = false; - } - if (stickers && stickers.date > tsNow(true) && !force) { - return processRawStickers(stickers); - } - return MtpApiManager.invokeApi('messages.getAllStickers', { - hash: stickers && stickers.hash || '' - }).then(function (newStickers) { - var notModified = newStickers._ == 'messages.allStickersNotModified'; - if (notModified) { - newStickers = stickers; - } - newStickers.date = tsNow(true) + 3600; - newStickers.layer = layer; - delete newStickers._; - - if (notModified) { - Storage.set({all_stickers: newStickers}); - return processRawStickers(newStickers); - } - - return getStickerSets(newStickers).then(function () { - Storage.set({all_stickers: newStickers}); - return processRawStickers(newStickers); - }); - - }); - }) - } - - function getStickerSets (allStickers) { + function getStickerSets (allStickers, prevCachedSets) { var promises = []; - var cachedSets = allStickers.fullSets || {}; + var cachedSets = prevCachedSets || allStickers.fullSets || {}; allStickers.fullSets = {}; angular.forEach(allStickers.sets, function (shortSet) { var fullSet = cachedSets[shortSet.id]; @@ -2507,6 +2465,48 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) return $q.all(promises); } + function getPopularStickers () { + return Storage.get('stickers_popular').then(function (popStickers) { + var result = []; + var i, len, docID; + if (popStickers && popStickers.length) { + for (i = 0, len = popStickers.length; i < len; i++) { + docID = popStickers[i][0]; + if (AppDocsManager.hasDoc(docID)) { + result.push({id: docID, rate: popStickers[i][1]}); + } + } + }; + return result; + }); + } + + function pushPopularSticker (id) { + getPopularStickers().then(function (popularStickers) { + var exists = false; + var count = popularStickers.length; + var result = []; + for (var i = 0; i < count; i++) { + if (popularStickers[i].id == id) { + exists = true; + popularStickers[i].rate++; + } + result.push([popularStickers[i].id, popularStickers[i].rate]); + } + if (exists) { + result.sort(function (a, b) { + return b[1] - a[1]; + }); + } else { + if (result.length > 15) { + result = result.slice(0, 15); + } + result.push([id, 1]); + } + ConfigStorage.set({stickers_popular: result}); + }); + } + function getStickerset (inputStickerset) { return MtpApiManager.invokeApi('messages.getStickerSet', { stickerset: inputStickerset @@ -2561,6 +2561,18 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) windowClass: 'stickerset_modal_window mobile_modal' }); } + + function getStickerSetsHash (stickerSets) { + var acc = 0, set; + for (var i = 0; i < stickerSets.length; i++) { + set = stickerSets[i]; + if (set.pFlags.disabled || !set.pFlags.installed) { + continue; + } + acc = ((acc * 20261) + 0x80000000 + set.hash) % 0x80000000; + } + return acc; + } }) .service('ApiUpdatesManager', function ($rootScope, MtpNetworkerFactory, AppUsersManager, AppChatsManager, AppPeersManager, MtpApiManager) { From a7bb7ecb730024192349adb41ca315c56585c82c Mon Sep 17 00:00:00 2001 From: Igor Zhukov Date: Fri, 22 Jan 2016 19:35:38 +0000 Subject: [PATCH 13/22] Inline bots wip --- app/js/controllers.js | 11 +++++++++++ app/js/services.js | 16 ++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/app/js/controllers.js b/app/js/controllers.js index b79009c4..57415f60 100644 --- a/app/js/controllers.js +++ b/app/js/controllers.js @@ -2380,6 +2380,8 @@ angular.module('myApp.controllers', ['myApp.i18n']) return cancelEvent($event); } + var inlineUsernameRegex = /^@([a-zA-Z\d_]{1,32}) /; + var lastInlineBot = false; function onMessageChange(newVal) { // console.log('ctrl text changed', newVal); // console.trace('ctrl text changed', newVal); @@ -2393,6 +2395,15 @@ angular.module('myApp.controllers', ['myApp.i18n']) backupDraftObj['draft' + $scope.curDialog.peerID] = newVal; 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; + }, function () { + + }) + } } else { Storage.remove('draft' + $scope.curDialog.peerID); // console.log(dT(), 'draft delete', 'draft' + $scope.curDialog.peerID); diff --git a/app/js/services.js b/app/js/services.js index ab4d4133..42fcb126 100755 --- a/app/js/services.js +++ b/app/js/services.js @@ -944,6 +944,21 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) }); } + function resolveInlineMention (username) { + return resolveUsername(username).then(function (peerID) { + if (peerID > 0) { + var bot = AppUsersManager.getUser(peerID); + if (bot.pFlags.bot && bot.bot_inline_placeholder !== undefined) { + return bot.bot_inline_placeholder; + } + } + return $q.reject(); + }, function (error) { + error.handled = true; + return $q.reject(error); + }); + } + function getPeerID (peerString) { if (angular.isObject(peerString)) { return peerString.user_id @@ -990,6 +1005,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) getPeer: getPeer, getPeerPhoto: getPeerPhoto, resolveUsername: resolveUsername, + resolveInlineMention: resolveInlineMention, isChannel: isChannel, isMegagroup: isMegagroup, isBot: isBot From 465a54fb56031e8c50ef93c927481a2ddbc64c1d Mon Sep 17 00:00:00 2001 From: Igor Zhukov Date: Fri, 22 Jan 2016 20:59:20 +0300 Subject: [PATCH 14/22] Click on date for reply or fwd --- app/js/controllers.js | 9 +++++++++ app/less/app.less | 4 ++++ app/partials/desktop/message.html | 2 +- 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/app/js/controllers.js b/app/js/controllers.js index 57415f60..698302d5 100644 --- a/app/js/controllers.js +++ b/app/js/controllers.js @@ -1558,6 +1558,15 @@ angular.module('myApp.controllers', ['myApp.i18n']) } break; } + if (target.className && + target.className.indexOf('im_message_date') != -1) { + if ($scope.historyState.canReply) { + selectedReply(messageID); + } else { + selectedForward(messageID); + } + return false; + } if (Config.Mobile && target.className && target.className.indexOf('im_message_body') != -1) { diff --git a/app/less/app.less b/app/less/app.less index 4dd37b09..681ecf98 100644 --- a/app/less/app.less +++ b/app/less/app.less @@ -2026,6 +2026,10 @@ img.im_message_document_thumb { padding: 0 0 20px 10px; } +.im_message_date { + cursor: pointer; +} + div.im_message_author, div.im_message_body { display: block; diff --git a/app/partials/desktop/message.html b/app/partials/desktop/message.html index 769bf864..da2424d3 100644 --- a/app/partials/desktop/message.html +++ b/app/partials/desktop/message.html @@ -39,7 +39,7 @@
- +
From 078d87162643677bc88bc6fa9085ee2aab5b2eb8 Mon Sep 17 00:00:00 2001 From: Igor Zhukov Date: Mon, 25 Jan 2016 21:05:15 +0000 Subject: [PATCH 15/22] inline bots wip --- app/js/controllers.js | 26 +++- app/js/directives.js | 73 ++++++++-- app/js/message_composer.js | 145 ++++++++------------ app/js/messages_manager.js | 27 +++- app/js/services.js | 5 +- app/less/app.less | 79 +++++++++++ app/partials/desktop/composer_dropdown.html | 27 ++++ app/partials/desktop/im.html | 5 +- app/partials/desktop/inline_results.html | 15 ++ 9 files changed, 296 insertions(+), 106 deletions(-) create mode 100644 app/partials/desktop/composer_dropdown.html create mode 100644 app/partials/desktop/inline_results.html 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 From 210dd48249b61727053ff9ea7e48dbc5ba7a715c Mon Sep 17 00:00:00 2001 From: Igor Zhukov Date: Tue, 26 Jan 2016 22:37:26 +0000 Subject: [PATCH 16/22] Bugfixes --- app/js/directives.js | 12 +++++------- app/js/message_composer.js | 3 ++- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/app/js/directives.js b/app/js/directives.js index da49bd58..29c25a2e 100755 --- a/app/js/directives.js +++ b/app/js/directives.js @@ -1507,9 +1507,6 @@ angular.module('myApp.directives', ['myApp.filters']) }); } - var peerPhotoCompiled = $compile(''); - var cachedPeerPhotos = {}; - var composer = new MessageComposer(messageField, { onTyping: function () { $scope.$emit('ui_typing'); @@ -3375,9 +3372,10 @@ angular.module('myApp.directives', ['myApp.filters']) html.push('
    • :' + encodeEntities(emoticonData[1][0]) + ':
    • '); } } - onContentLoaded(function () { - element.html(html); - }); + // onContentLoaded(function () { + element.html(html.join('')); + console.log(dT(), 'emoji done'); + // }); }); } }; @@ -3394,7 +3392,7 @@ angular.module('myApp.directives', ['myApp.filters']) link: function ($scope, element, attrs) { $scope.$watch('botResults.results.length', function (show) { - console.log($scope.botResults, show); + // console.log($scope.botResults, show); }); } } diff --git a/app/js/message_composer.js b/app/js/message_composer.js index cf04c492..e8f3f449 100644 --- a/app/js/message_composer.js +++ b/app/js/message_composer.js @@ -683,7 +683,7 @@ function MessageComposer (textarea, options) { this.autoCompleteWrapEl = $('
      ').appendTo(document.body); var autoCompleteEl = $('
      ').appendTo(this.autoCompleteWrapEl); - options.dropdownDirective(div, function (scope, autoCompleteEl) { + options.dropdownDirective(autoCompleteEl, function (scope, autoCompleteEl) { self.autoCompleteEl = autoCompleteEl; self.autoCompleteScope = scope; self.setUpAutoComplete(); @@ -1405,6 +1405,7 @@ MessageComposer.prototype.showCommandsSuggestions = function (commands) { MessageComposer.prototype.updatePosition = function () { var offset = (this.richTextareaEl || this.textareaEl).offset(); var height = this.scroller.updateHeight(); + console.log(dT(), 'pos', height); var width = $((this.richTextareaEl || this.textareaEl)[0].parentNode).outerWidth(); this.autoCompleteWrapEl.css({ top: offset.top - height, From 040da24417cb632638b20b97b1c1747dd5e8d188 Mon Sep 17 00:00:00 2001 From: Igor Zhukov Date: Thu, 28 Jan 2016 23:24:50 +0300 Subject: [PATCH 17/22] Added basic inline bots support --- app/js/controllers.js | 55 ++++++++--- app/js/directives.js | 35 +++++-- app/js/lib/ng_utils.js | 5 +- app/js/lib/utils.js | 12 +++ app/js/message_composer.js | 102 ++++++++++++++----- app/js/messages_manager.js | 104 ++++++++++++++++---- app/js/services.js | 2 +- app/less/app.less | 88 +++++++++-------- app/partials/desktop/composer_dropdown.html | 2 +- app/partials/desktop/im.html | 2 +- app/partials/desktop/inline_results.html | 16 +-- app/partials/desktop/message.html | 2 +- 12 files changed, 307 insertions(+), 118 deletions(-) diff --git a/app/js/controllers.js b/app/js/controllers.js index 6e9b7ad7..2adc6ac4 100644 --- a/app/js/controllers.js +++ b/app/js/controllers.js @@ -1056,6 +1056,7 @@ angular.module('myApp.controllers', ['myApp.i18n']) $scope.selectedReply = selectedReply; $scope.selectedCancel = selectedCancel; $scope.selectedFlush = selectedFlush; + $scope.selectInlineBot = selectInlineBot; $scope.startBot = startBot; $scope.cancelBot = cancelBot; @@ -1572,13 +1573,7 @@ angular.module('myApp.controllers', ['myApp.i18n']) target.className.indexOf('im_message_body') != -1) { break; } - if (target.tagName == 'A' || - target.onclick || - target.getAttribute('ng-click')) { - return false; - } - var events = $._data(target, 'events'); - if (events && (events.click || events.mousedown)) { + if (target.tagName == 'A' || hasOnlick(target)) { return false; } target = target.parentNode; @@ -1663,6 +1658,11 @@ angular.module('myApp.controllers', ['myApp.i18n']) $scope.$broadcast('messages_select'); } + function selectInlineBot (botID, $event) { + $scope.$broadcast('inline_bot_select', botID); + return cancelEvent($event); + } + function selectedCancel (noBroadcast) { $scope.selectedMsgs = {}; $scope.selectedCount = 0; @@ -2099,11 +2099,11 @@ 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); $scope.$watch('draftMessage.command', onCommandSelected); + $scope.$watch('draftMessage.inlineResultID', onInlineResultSelected); $scope.$on('history_reply_markup', function (e, peerData) { 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.toggleSlash = toggleSlash; @@ -2390,7 +2396,7 @@ angular.module('myApp.controllers', ['myApp.i18n']) 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; function onMessageChange(newVal) { // console.log('ctrl text changed', newVal); @@ -2409,11 +2415,14 @@ angular.module('myApp.controllers', ['myApp.i18n']) 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); + var username = matches[1]; + AppPeersManager.resolveInlineMention(username).then(function (inlineBot) { + $scope.$broadcast('inline_placeholder', { + prefix: '@' + username + matches[2], + placeholder: inlineBot.placeholder + }); + AppMessagesManager.getInlineResults(inlineBot.id, matches[3], '').then(function (botResults) { + $scope.$broadcast('inline_results', botResults); delete $scope.draftMessage.inlineProgress; }, function () { delete $scope.draftMessage.inlineProgress; @@ -2502,6 +2511,24 @@ angular.module('myApp.controllers', ['myApp.i18n']) delete $scope.draftMessage.sticker; delete $scope.draftMessage.text; 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_peer_draft'); } diff --git a/app/js/directives.js b/app/js/directives.js index 29c25a2e..df65729f 100755 --- a/app/js/directives.js +++ b/app/js/directives.js @@ -1516,7 +1516,7 @@ angular.module('myApp.directives', ['myApp.filters']) }, dropdownDirective: function (element, callback) { var scope = $scope.$new(true); - $compile('
      ')(scope, function (clonedElement) { + var clonedElement = $compile('
      ')(scope, function (clonedElement, scope) { element.replaceWith(clonedElement); callback(scope, clonedElement); }); @@ -1524,6 +1524,7 @@ angular.module('myApp.directives', ['myApp.filters']) mentions: $scope.mentions, commands: $scope.commands, onMessageSubmit: onMessageSubmit, + onInlineResultSend: onInlineResultSend, onFilePaste: onFilePaste, onCommandSend: function (command) { $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]; if (richTextarea) { $(richTextarea).on('keydown keyup', updateHeight); } + $scope.$on('inline_placeholder', function(e, data) { + composer.setInlinePlaceholder(data.prefix, data.placeholder); + }); + fileSelects.on('change', function () { var self = this; $scope.$apply(function () { @@ -1573,6 +1584,12 @@ angular.module('myApp.directives', ['myApp.filters']) return cancelEvent(e); } + function onInlineResultSend (qID) { + $scope.$apply(function () { + $scope.draftMessage.inlineResultID = qID; + }); + } + function updateValue () { if (richTextarea) { composer.onChange(); @@ -2265,7 +2282,7 @@ angular.module('myApp.directives', ['myApp.filters']) var src = 'https://maps.googleapis.com/maps/api/staticmap?sensor=false¢er=' + $scope.point['lat'] + ',' + $scope.point['long'] + '&zoom=15&size='+width+'x'+height+'&scale=2&key=' + apiKey; 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 () { if (peerID > 0) { AppUsersManager.openUser(peerID, override); @@ -3382,7 +3399,7 @@ angular.module('myApp.directives', ['myApp.filters']) }) - .directive('myInlineResults', function () { + .directive('myInlineResults', function (ExternalResourcesManager) { return { templateUrl: templateUrl('inline_results'), @@ -3391,8 +3408,14 @@ angular.module('myApp.directives', ['myApp.filters']) }, link: function ($scope, element, attrs) { - $scope.$watch('botResults.results.length', function (show) { - // console.log($scope.botResults, show); + $scope.$watch('botResults.results', function (results) { + angular.forEach(results, function (result) { + if (result.thumb_url && !result.thumbUrl) { + ExternalResourcesManager.downloadImage(result.thumb_url).then(function (url) { + result.thumbUrl = url; + }); + } + }) }); } } diff --git a/app/js/lib/ng_utils.js b/app/js/lib/ng_utils.js index 48a2f9ee..6898d8ee 100644 --- a/app/js/lib/ng_utils.js +++ b/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 = {}; function downloadImage (url) { @@ -974,7 +974,8 @@ angular.module('izhukov.utils', []) return urlPromises[url] = $http.get(url, {responseType: 'blob', transformRequest: null}) .then(function (response) { window.URL = window.URL || window.webkitURL; - return window.URL.createObjectURL(response.data); + var url = window.URL.createObjectURL(response.data); + return $sce.trustAsResourceUrl(url); }); } diff --git a/app/js/lib/utils.js b/app/js/lib/utils.js index a1077165..a55a83ff 100644 --- a/app/js/lib/utils.js +++ b/app/js/lib/utils.js @@ -60,6 +60,18 @@ function cancelEvent (event) { 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() { var outer = $('
      ').css({ position: 'absolute', diff --git a/app/js/message_composer.js b/app/js/message_composer.js index e8f3f449..b17d3c5a 100644 --- a/app/js/message_composer.js +++ b/app/js/message_composer.js @@ -683,8 +683,8 @@ function MessageComposer (textarea, options) { this.autoCompleteWrapEl = $('
      ').appendTo(document.body); var autoCompleteEl = $('
      ').appendTo(this.autoCompleteWrapEl); - options.dropdownDirective(autoCompleteEl, function (scope, autoCompleteEl) { - self.autoCompleteEl = autoCompleteEl; + options.dropdownDirective(autoCompleteEl, function (scope, newAutoCompleteEl) { + self.autoCompleteEl = newAutoCompleteEl; self.autoCompleteScope = scope; self.setUpAutoComplete(); }); @@ -696,6 +696,7 @@ function MessageComposer (textarea, options) { this.getSendOnEnter = options.getSendOnEnter; this.onFilePaste = options.onFilePaste; this.onCommandSend = options.onCommandSend; + this.onInlineResultSend = options.onInlineResultSend; this.mentions = options.mentions; this.commands = options.commands; } @@ -704,6 +705,10 @@ MessageComposer.autoCompleteRegEx = /(\s|^)(:|@|\/)([A-Za-z0-9\-\+\*@_]*)$/; MessageComposer.prototype.setUpInput = function () { + this.inlinePlaceholderWrap = $('
      ').prependTo(this.textareaEl[0].parentNode); + this.inlinePlaceholderPrefixEl = $('').appendTo(this.inlinePlaceholderWrap); + this.inlinePlaceholderEl = $('').appendTo(this.inlinePlaceholderWrap); + if ('contentEditable' in document.body) { this.setUpRich(); } 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 () { 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; + var target = $(e.target), mention, code, command, inlineID; if (target[0].tagName != 'A') { target = $(target[0].parentNode); } @@ -743,6 +762,12 @@ MessageComposer.prototype.setUpAutoComplete = function () { } self.hideSuggestions(); } + if (inlineID = target.attr('data-inlineid')) { + if (self.onInlineResultSend) { + self.onInlineResultSend(inlineID); + } + self.hideSuggestions(); + } return cancelEvent(e); }); } @@ -778,7 +803,7 @@ MessageComposer.prototype.onKeyEvent = function (e) { if (this.keyupStarted === undefined) { this.keyupStarted = now; } - if (now - this.keyupStarted > 10000) { + if (now - this.keyupStarted > 3000 || true) { this.onChange(); } else { @@ -786,6 +811,8 @@ MessageComposer.prototype.onKeyEvent = function (e) { if (this.wasEmpty != !length) { this.wasEmpty = !this.wasEmpty; this.onChange(); + } else if (this.inlinePlaceholderPrefix) { + this.onChange(); } else { this.updateValueTO = setTimeout(this.onChange.bind(this), 1000); } @@ -812,48 +839,57 @@ MessageComposer.prototype.onKeyEvent = function (e) { if (this.autocompleteShown) { if (e.keyCode == 38 || e.keyCode == 40) { // UP / DOWN var next = e.keyCode == 40; - var currentSelected = $(this.autoCompleteEl).find('.composer_autocomplete_option_active'); - - if (currentSelected.length) { - var currentSelectedWrap = currentSelected[0].parentNode; - var nextWrap = currentSelectedWrap[next ? 'nextSibling' : 'previousSibling']; - currentSelected.removeClass('composer_autocomplete_option_active'); - if (nextWrap) { - $(nextWrap).find('a').addClass('composer_autocomplete_option_active'); - this.scroller.scrollToNode(nextWrap); + var currentSel = $(this.autoCompleteEl).find('li.composer_autocomplete_option_active'); + var allLIs = Array.prototype.slice.call($(this.autoCompleteEl).find('li')); + var nextSel; + + if (currentSel.length) { + var pos = allLIs.indexOf(currentSel[0]); + var nextPos = pos + (next ? 1 : -1); + nextSel = allLIs[nextPos]; + currentSel.removeClass('composer_autocomplete_option_active'); + if (nextSel) { + $(nextSel).addClass('composer_autocomplete_option_active'); + this.scroller.scrollToNode(nextSel); return cancelEvent(e); } } - var childNodes = this.autoCompleteEl[0].childNodes; - var nextWrap = childNodes[next ? 0 : childNodes.length - 1]; - this.scroller.scrollToNode(nextWrap); - $(nextWrap).find('a').addClass('composer_autocomplete_option_active'); + nextSel = allLIs[next ? 0 : allLIs.length - 1]; + this.scroller.scrollToNode(nextSel); + $(nextSel).addClass('composer_autocomplete_option_active'); return cancelEvent(e); } if (e.keyCode == 13 || e.keyCode == 9) { // Enter or Tab - var currentSelected = $(this.autoCompleteEl).find('.composer_autocomplete_option_active'); - if (!currentSelected.length && e.keyCode == 9) { - currentSelected = $(this.autoCompleteEl[0].childNodes[0]).find('a'); + var currentSel = $(this.autoCompleteEl).find('li.composer_autocomplete_option_active'); + if (!currentSel.length && e.keyCode == 9) { + currentSel = $(this.autoCompleteEl).find('li:first'); } + currentSel = currentSel.find('a:first'); var code, mention, command; - if (code = currentSelected.attr('data-code')) { + if (code = currentSel.attr('data-code')) { this.onEmojiSelected(code, true); EmojiHelper.pushPopularEmoji(code); return cancelEvent(e); } - if (mention = currentSelected.attr('data-mention')) { + if (mention = currentSel.attr('data-mention')) { this.onMentionSelected(mention); return cancelEvent(e); } - if (command = currentSelected.attr('data-command')) { + if (command = currentSel.attr('data-command')) { if (this.onCommandSelected) { this.onCommandSelected(command, e.keyCode == 9); } return cancelEvent(e); } + if (inlineID = target.attr('data-inlineid')) { + if (self.onInlineResultSend) { + self.onInlineResultSend(inlineID); + } + return cancelEvent(e); + } checkSubmit = true; } } @@ -1283,6 +1319,7 @@ MessageComposer.prototype.onChange = function (e) { delete this.keyupStarted; this.textareaEl.val(getRichValue(this.richTextareaEl[0])).trigger('change'); } + this.updateInlinePlaceholder(); } 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 () { var offset = (this.richTextareaEl || this.textareaEl).offset(); var height = this.scroller.updateHeight(); - console.log(dT(), 'pos', height); var width = $((this.richTextareaEl || this.textareaEl)[0].parentNode).outerWidth(); this.autoCompleteWrapEl.css({ top: offset.top - height, @@ -1416,6 +1469,7 @@ MessageComposer.prototype.updatePosition = function () { } MessageComposer.prototype.hideSuggestions = function () { + // return; this.autoCompleteWrapEl.hide(); delete this.autocompleteShown; } diff --git a/app/js/messages_manager.js b/app/js/messages_manager.js index dd9d3007..8cab9292 100644 --- a/app/js/messages_manager.js +++ b/app/js/messages_manager.js @@ -1294,10 +1294,12 @@ angular.module('myApp.services') isChannel = AppPeersManager.isChannel(peerID), isMegagroup = isChannel && AppPeersManager.isMegagroup(peerID), asChannel = isChannel && !isMegagroup ? true : false, - entities = [], + entities = options.entities || [], message; - text = RichTextProcessor.parseMarkdown(text, entities); + if (!options.viaBotID) { + text = RichTextProcessor.parseMarkdown(text, entities); + } if (historyStorage === undefined) { historyStorage = historiesStorage[peerID] = {count: null, history: [], pending: []}; @@ -1331,6 +1333,7 @@ angular.module('myApp.services') message: text, random_id: randomIDS, reply_to_msg_id: replyToMsgID, + via_bot_id: options.viaBotID, entities: entities, views: asChannel && 1, pending: true @@ -1362,21 +1365,35 @@ angular.module('myApp.services') if (replyToMsgID) { flags |= 1; } - if (entities.length) { - flags |= 8; - } if (asChannel) { 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); - MtpApiManager.invokeApi('messages.sendMessage', { - flags: flags, - peer: AppPeersManager.getInputPeerByID(peerID), - message: text, - random_id: randomID, - reply_to_msg_id: getMessageLocalID(replyToMsgID), - entities: entities - }, sentRequestOptions).then(function (updates) { + apiPromise.then(function (updates) { if (updates._ == 'updateShortSentMessage') { message.flags = updates.flags; message.date = updates.date; @@ -1424,6 +1441,31 @@ angular.module('myApp.services') 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) { options = options || {}; var messageID = tempID--, @@ -1654,6 +1696,10 @@ angular.module('myApp.services') 'document': doc }; break; + + case 'messageMediaPending': + media = inputMedia; + break; } var flags = 0; @@ -1687,6 +1733,7 @@ angular.module('myApp.services') media: media, random_id: randomIDS, reply_to_msg_id: replyToMsgID, + via_bot_id: options.viaBotID, views: asChannel && 1, pending: true }; @@ -1721,13 +1768,27 @@ angular.module('myApp.services') sentRequestOptions.afterMessageID = pendingAfterMsgs[peerID].messageID; } - MtpApiManager.invokeApi('messages.sendMedia', { - flags: flags, - peer: AppPeersManager.getInputPeerByID(peerID), - media: inputMedia, - random_id: randomID, - reply_to_msg_id: getMessageLocalID(replyToMsgID) - }, sentRequestOptions).then(function (updates) { + 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 { + 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); }, function (error) { toggleError(true); @@ -3026,9 +3087,11 @@ angular.module('myApp.services') angular.forEach(botResults.results, function (result) { var qID = queryID + '_' + result.id; result.qID = qID; + result.botID = botID; result.rTitle = RichTextProcessor.wrapRichText(result.title, {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; }); @@ -3058,6 +3121,7 @@ angular.module('myApp.services') getMessageThumb: getMessageThumb, clearDialogCache: clearDialogCache, getInlineResults: getInlineResults, + sendInlineResult: sendInlineResult, wrapForDialog: wrapForDialog, wrapForHistory: wrapForHistory, wrapReplyMarkup: wrapReplyMarkup, diff --git a/app/js/services.js b/app/js/services.js index 3da6163d..75e879ab 100755 --- a/app/js/services.js +++ b/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) { var firstChar = peerString.charAt(0), diff --git a/app/less/app.less b/app/less/app.less index cb853137..2c578ce0 100644 --- a/app/less/app.less +++ b/app/less/app.less @@ -1440,12 +1440,14 @@ a.im_dialog_selected { margin: 10px 0; } .im_message_author, -.im_message_fwd_author { +.im_message_fwd_author, +.im_message_via_author { color: #3a6d99; font-weight: bold; } .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; } .im_message_author_via { @@ -2368,6 +2370,18 @@ textarea.im_message_field { height: 50px; 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 { background: #6ec26d; @@ -2858,6 +2872,7 @@ a.composer_emoji_btn { border-radius: 0; margin-top: -5px; margin-left: -1px; + width: 180px; } .composer_dropdown { @@ -2881,9 +2896,10 @@ a.composer_emoji_btn { } li a:hover, - li a.composer_autocomplete_option_active { + li.composer_autocomplete_option_active a { color: #52719a; background: #f2f6fa; + text-decoration: none; } } @@ -2903,7 +2919,7 @@ a.composer_emoji_btn { .composer_dropdown { 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; } } @@ -2962,7 +2978,7 @@ a.composer_emoji_btn { text-overflow: ellipsis; } 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; } .composer_command_desc .emoji { @@ -3132,44 +3148,36 @@ _:-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; +.inline_result_wrap { display: block; - float: none; - top: auto; - left: auto; - border: 0; - border-radius: 0; - padding: 0; - margin: 0; - z-index: auto; } - -.inline_result_wrap { +.inline_result_article { display: block; - font-size: 13px; - line-height: 15px; - padding: 4px 10px; - color: #52719a; - - &:hover, - &.composer_autocomplete_option_active { - color: #52719a; - background: #f2f6fa; - } +} +.inline_article_thumb_wrap { + width: 50px; + height: 50px; + margin-right: 10px; + pointer-events: none; +} +.inline_article_thumb { + 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; } diff --git a/app/partials/desktop/composer_dropdown.html b/app/partials/desktop/composer_dropdown.html index 3cc9dab6..f1d5e4ed 100644 --- a/app/partials/desktop/composer_dropdown.html +++ b/app/partials/desktop/composer_dropdown.html @@ -1,7 +1,7 @@
      diff --git a/app/partials/desktop/inline_results.html b/app/partials/desktop/inline_results.html index 66fd4259..b92c0a8c 100644 --- a/app/partials/desktop/inline_results.html +++ b/app/partials/desktop/inline_results.html @@ -1,8 +1,8 @@ -
      - \ No newline at end of file + + + \ No newline at end of file diff --git a/app/partials/desktop/message.html b/app/partials/desktop/message.html index da2424d3..fa92172e 100644 --- a/app/partials/desktop/message.html +++ b/app/partials/desktop/message.html @@ -44,7 +44,7 @@
      - + From b44e49cb3132dc2b1db0817c4a86bccb0bd6fb82 Mon Sep 17 00:00:00 2001 From: Yuri Date: Fri, 29 Jan 2016 02:35:04 +0300 Subject: [PATCH 18/22] Added svg logo --- app/img/Telegram.svg | 1 + app/less/app.less | 9 +++++---- 2 files changed, 6 insertions(+), 4 deletions(-) create mode 100644 app/img/Telegram.svg diff --git a/app/img/Telegram.svg b/app/img/Telegram.svg new file mode 100644 index 00000000..314eb135 --- /dev/null +++ b/app/img/Telegram.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/less/app.less b/app/less/app.less index 2c578ce0..d7756ee7 100644 --- a/app/less/app.less +++ b/app/less/app.less @@ -962,13 +962,14 @@ a.tg_radio_on:hover i.icon-radio { background-position: -5px -10px; } .icon-tg-title { - width: 63px; - height: 16px; + width: 62px; + height: 14px; display: inline-block; vertical-align: middle; - - .image-2x('../img/Telegram.png', 63px, 16px); + background-image: url('../img/Telegram.svg'); + background-repeat: no-repeat; background-position: 0 0; + margin-top: 1px; } .login_head_submit_wrap, .login_head_submit_progress { From 70e39704e13132dc31c12cedb5feb9dd992a992a Mon Sep 17 00:00:00 2001 From: Igor Zhukov Date: Fri, 29 Jan 2016 15:20:47 +0300 Subject: [PATCH 19/22] Improved inline bots --- app/js/controllers.js | 7 +-- app/js/filters.js | 3 ++ app/js/message_composer.js | 11 ++-- app/js/messages_manager.js | 61 ++-------------------- app/js/services.js | 102 +++++++++++++++++++++++++++++++++++-- app/less/app.less | 10 ++-- 6 files changed, 124 insertions(+), 70 deletions(-) diff --git a/app/js/controllers.js b/app/js/controllers.js index 2adc6ac4..1347fdba 100644 --- a/app/js/controllers.js +++ b/app/js/controllers.js @@ -2081,7 +2081,7 @@ angular.module('myApp.controllers', ['myApp.i18n']) $scope.$on('user_update', angular.noop); }) - .controller('AppImSendController', function ($scope, $timeout, MtpApiManager, Storage, AppProfileManager, AppChatsManager, AppUsersManager, AppPeersManager, AppDocsManager, AppMessagesManager, MtpApiFileManager, RichTextProcessor) { + .controller('AppImSendController', function ($scope, $timeout, MtpApiManager, Storage, AppProfileManager, AppChatsManager, AppUsersManager, AppPeersManager, AppDocsManager, AppMessagesManager, AppInlineBotsManager, MtpApiFileManager, RichTextProcessor) { $scope.$watch('curDialog.peer', resetDraft); $scope.$on('user_update', angular.noop); @@ -2421,7 +2421,7 @@ angular.module('myApp.controllers', ['myApp.i18n']) prefix: '@' + username + matches[2], placeholder: inlineBot.placeholder }); - AppMessagesManager.getInlineResults(inlineBot.id, matches[3], '').then(function (botResults) { + AppInlineBotsManager.getInlineResults(inlineBot.id, matches[3], '').then(function (botResults) { $scope.$broadcast('inline_results', botResults); delete $scope.draftMessage.inlineProgress; }, function () { @@ -2523,7 +2523,8 @@ angular.module('myApp.controllers', ['myApp.i18n']) var options = { replyToMsgID: $scope.draftMessage.replyToMessage && $scope.draftMessage.replyToMessage.mid }; - AppMessagesManager.sendInlineResult($scope.curDialog.peerID, qID, options); + AppInlineBotsManager.sendInlineResult($scope.curDialog.peerID, qID, options); + fwdsSend(); resetDraft(); delete $scope.draftMessage.sticker; delete $scope.draftMessage.text; diff --git a/app/js/filters.js b/app/js/filters.js index 30869b5d..b840ce32 100644 --- a/app/js/filters.js +++ b/app/js/filters.js @@ -187,6 +187,9 @@ angular.module('myApp.filters', ['myApp.i18n']) .filter('formatSizeProgress', function ($filter, _) { var formatSizeFilter = $filter('formatSize'); return function (progress) { + if (!progress.total) { + return ''; + } var done = formatSizeFilter(progress.done, true), doneParts = done.split(' '), total = formatSizeFilter(progress.total), diff --git a/app/js/message_composer.js b/app/js/message_composer.js index b17d3c5a..cc9a273f 100644 --- a/app/js/message_composer.js +++ b/app/js/message_composer.js @@ -727,13 +727,14 @@ MessageComposer.prototype.setInlinePlaceholder = function (prefix, placeholder) this.inlinePlaceholderPrefix = prefix this.inlinePlaceholderPrefixEl.html(encodeEntities(prefix)); this.inlinePlaceholderEl.html(encodeEntities(placeholder)); + this.onChange(); } MessageComposer.prototype.updateInlinePlaceholder = function () { var prefix = this.inlinePlaceholderPrefix; if (prefix) { var value = this.textareaEl.val(); - this.inlinePlaceholderWrap.toggle(value == prefix); + this.inlinePlaceholderWrap.toggleClass('active', value == prefix); } } @@ -868,7 +869,7 @@ MessageComposer.prototype.onKeyEvent = function (e) { currentSel = $(this.autoCompleteEl).find('li:first'); } currentSel = currentSel.find('a:first'); - var code, mention, command; + var code, mention, command, inlineID; if (code = currentSel.attr('data-code')) { this.onEmojiSelected(code, true); EmojiHelper.pushPopularEmoji(code); @@ -884,7 +885,7 @@ MessageComposer.prototype.onKeyEvent = function (e) { } return cancelEvent(e); } - if (inlineID = target.attr('data-inlineid')) { + if (inlineID = currentSel.attr('data-inlineid')) { if (self.onInlineResultSend) { self.onInlineResultSend(inlineID); } @@ -953,6 +954,9 @@ MessageComposer.prototype.restoreSelection = function () { MessageComposer.prototype.checkAutocomplete = function (forceFull) { + if (this.autocompleteShown && this.autoCompleteScope.type == 'inline') { + return; + } var pos, value; if (this.richTextareaEl) { var textarea = this.richTextareaEl[0]; @@ -1469,6 +1473,7 @@ MessageComposer.prototype.updatePosition = function () { } MessageComposer.prototype.hideSuggestions = function () { + // console.trace(); // return; this.autoCompleteWrapEl.hide(); delete this.autocompleteShown; diff --git a/app/js/messages_manager.js b/app/js/messages_manager.js index 8cab9292..547bedcd 100644 --- a/app/js/messages_manager.js +++ b/app/js/messages_manager.js @@ -1370,7 +1370,6 @@ angular.module('myApp.services') } var apiPromise; if (options.viaBotID) { - console.warn(options); apiPromise = MtpApiManager.invokeApi('messages.sendInlineBotResult', { flags: flags, peer: AppPeersManager.getInputPeerByID(peerID), @@ -1441,31 +1440,6 @@ angular.module('myApp.services') 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) { options = options || {}; var messageID = tempID--, @@ -1682,7 +1656,8 @@ angular.module('myApp.services') case 'inputMediaPhoto': media = { _: 'messageMediaPhoto', - photo: AppPhotosManager.getPhoto(inputMedia.id.id) + photo: AppPhotosManager.getPhoto(inputMedia.id.id), + caption: inputMedia.caption || '' }; break; @@ -1693,7 +1668,8 @@ angular.module('myApp.services') }; media = { _: 'messageMediaDocument', - 'document': doc + 'document': doc, + caption: inputMedia.caption || '' }; break; @@ -1770,7 +1746,6 @@ angular.module('myApp.services') var apiPromise; if (options.viaBotID) { - console.warn(options); apiPromise = MtpApiManager.invokeApi('messages.sendInlineBotResult', { flags: flags, peer: AppPeersManager.getInputPeerByID(peerID), @@ -3073,32 +3048,6 @@ 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.botID = botID; - - result.rTitle = RichTextProcessor.wrapRichText(result.title, {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; - }); - return botResults; - }); - } - return { getConversations: getConversations, getHistory: getHistory, @@ -3120,8 +3069,6 @@ angular.module('myApp.services') getMessagePeer: getMessagePeer, getMessageThumb: getMessageThumb, clearDialogCache: clearDialogCache, - getInlineResults: getInlineResults, - sendInlineResult: sendInlineResult, wrapForDialog: wrapForDialog, wrapForHistory: wrapForHistory, wrapReplyMarkup: wrapReplyMarkup, diff --git a/app/js/services.js b/app/js/services.js index 75e879ab..aa5284a2 100755 --- a/app/js/services.js +++ b/app/js/services.js @@ -11,7 +11,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) -.service('AppUsersManager', function ($rootScope, $modal, $modalStack, $filter, $q, qSync, MtpApiFileManager, MtpApiManager, RichTextProcessor, ErrorService, Storage, _) { +.service('AppUsersManager', function ($rootScope, $modal, $modalStack, $filter, $q, qSync, MtpApiManager, RichTextProcessor, ErrorService, Storage, _) { var users = {}, usernames = {}, userAccess = {}, @@ -575,7 +575,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) }) -.service('AppChatsManager', function ($q, $rootScope, $modal, _, MtpApiFileManager, MtpApiManager, AppUsersManager, AppPhotosManager, RichTextProcessor) { +.service('AppChatsManager', function ($q, $rootScope, $modal, _, MtpApiManager, AppUsersManager, AppPhotosManager, RichTextProcessor) { var chats = {}, usernames = {}, channelAccess = {}, @@ -2297,7 +2297,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) } }) -.service('AppStickersManager', function ($q, $rootScope, $modal, _, FileManager, MtpApiManager, MtpApiFileManager, AppDocsManager, Storage, ApiUpdatesManager) { +.service('AppStickersManager', function ($q, $rootScope, $modal, _, FileManager, MtpApiManager, AppDocsManager, Storage, ApiUpdatesManager) { var started = false; var applied = false; @@ -2594,6 +2594,100 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) } }) +.service('AppInlineBotsManager', function (MtpApiManager, AppMessagesManager, AppDocsManager, AppPhotosManager, RichTextProcessor, AppUsersManager) { + + var inlineResults = {}; + + return { + sendInlineResult: sendInlineResult, + getInlineResults: getInlineResults + }; + + 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.botID = botID; + + result.rTitle = RichTextProcessor.wrapRichText(result.title, {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) + + if (result._ == 'botInlineMediaResultDocument') { + AppDocsManager.saveDoc(result.document); + } + else if (result._ == 'botInlineMediaResultPhoto') { + AppPhotosManager.savePhoto(result.photo); + } + + inlineResults[qID] = result; + }); + return botResults; + }); + } + + 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; + AppMessagesManager.sendText(peerID, inlineResult.send_message.message, options); + } else { + var caption = ''; + if (inlineResult.send_message._ == 'botInlineMessageMediaAuto') { + caption = inlineResult.send_message.caption; + } + var inputMedia = false; + if (inlineResult._ == 'botInlineMediaResultDocument') { + var doc = inlineResult.document; + inputMedia = { + _: 'inputMediaDocument', + id: {_: 'inputDocument', id: doc.id, access_hash: doc.access_hash}, + caption: caption + }; + } + else if (inlineResult._ == 'botInlineMediaResultPhoto') { + var photo = inlineResult.photo; + inputMedia = { + _: 'inputMediaPhoto', + id: {_: 'inputPhoto', id: photo.id, access_hash: photo.access_hash}, + caption: caption + }; + } + if (!inputMedia) { + inputMedia = { + _: 'messageMediaPending', + type: inlineResult.type, + file_name: inlineResult.title || inlineResult.content_url || inlineResult.url, + size: 0, + progress: {percent: 30, total: 0} + }; + } + AppMessagesManager.sendOther(peerID, inputMedia, options); + } + } + +}) + .service('ApiUpdatesManager', function ($rootScope, MtpNetworkerFactory, AppUsersManager, AppChatsManager, AppPeersManager, MtpApiManager) { var updatesState = { @@ -4204,4 +4298,4 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) start: start, shareUrl: shareUrl }; -}) +}) \ No newline at end of file diff --git a/app/less/app.less b/app/less/app.less index 2c578ce0..9c7bab0a 100644 --- a/app/less/app.less +++ b/app/less/app.less @@ -2372,15 +2372,19 @@ textarea.im_message_field { } .im_inline_placeholder_wrap { position: absolute; - left: 0; margin-top: 2px; - white-space: nowrap + white-space: nowrap; + pointer-events: none; + display: none; +} +.im_inline_placeholder_wrap.active { + display: block; } .im_inline_placeholder_prefix { visibility: hidden; } .im_inline_placeholder { - color: #999; + color: #9aa2ab; } .icon-online { From 5f1f7d78467bdd2833afff8e68f39dac3e6fc07b Mon Sep 17 00:00:00 2001 From: Igor Zhukov Date: Fri, 29 Jan 2016 19:34:17 +0000 Subject: [PATCH 20/22] Added regrouping --- app/js/directives.js | 5 +- app/js/services.js | 79 +++++++++++++++++++++++- app/less/app.less | 9 ++- app/partials/desktop/inline_results.html | 8 ++- 4 files changed, 96 insertions(+), 5 deletions(-) diff --git a/app/js/directives.js b/app/js/directives.js index df65729f..44429727 100755 --- a/app/js/directives.js +++ b/app/js/directives.js @@ -1430,7 +1430,7 @@ angular.module('myApp.directives', ['myApp.filters']) }) - .directive('mySendForm', function (_, $timeout, $compile, $modalStack, $http, $interpolate, Storage, AppStickersManager, AppDocsManager, ErrorService, shouldFocusOnInteraction) { + .directive('mySendForm', function (_, $timeout, $compile, $modalStack, $http, $interpolate, Storage, AppStickersManager, AppDocsManager, ErrorService, AppInlineBotsManager, shouldFocusOnInteraction) { return { link: link, scope: { @@ -1534,6 +1534,9 @@ angular.module('myApp.directives', ['myApp.filters']) }); $scope.$on('inline_results', function (e, inlineResults) { + var w = 180; + var h = 50; + AppInlineBotsManager.regroupWrappedResults(inlineResults.results, w, h); setZeroTimeout(function () { composer.showInlineSuggestions(inlineResults); }); diff --git a/app/js/services.js b/app/js/services.js index aa5284a2..0f304503 100755 --- a/app/js/services.js +++ b/app/js/services.js @@ -2600,6 +2600,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) return { sendInlineResult: sendInlineResult, + regroupWrappedResults: regroupWrappedResults, getInlineResults: getInlineResults }; @@ -2613,6 +2614,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) delete botResults._; delete botResults.flags; delete botResults.query_id; + angular.forEach(botResults.results, function (result) { var qID = queryID + '_' + result.id; result.qID = qID; @@ -2622,10 +2624,10 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) result.rDescription = RichTextProcessor.wrapRichText(result.description, {noLinebreaks: true, noLinks: true}); result.initials = (result.url || result.title || result.type || '').substr(0, 1) - if (result._ == 'botInlineMediaResultDocument') { + if (result.document) { AppDocsManager.saveDoc(result.document); } - else if (result._ == 'botInlineMediaResultPhoto') { + if (result.photo) { AppPhotosManager.savePhoto(result.photo); } @@ -2635,6 +2637,79 @@ 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') { + return; + } + var ratios = []; + angular.forEach(results, function (result) { + var w, h; + if (result._ == 'botInlineMediaResultDocument') { + w = result.document.w; + h = result.document.h; + } + else if (result._ == 'botInlineMediaResultPhoto') { + var photoSize = (result.photo.sizes || [])[0]; + w = photoSize && photoSize.w; + h = photoSize && photoSize.h; + } + else { + w = result.w; + h = result.h; + } + if (!w || !h) { + w = h = 1; + } + ratios.push(w / h); + }); + + var rows = []; + var curCnt = 0; + var curW = 0; + angular.forEach(ratios, function (ratio) { + var w = ratio * rowH; + curW += w; + console.log(curCnt, w, curW, rowW); + if (!curCnt || curCnt < 4 && curW < (rowW * 1.1)) { + curCnt++; + } else { + rows.push(curCnt); + curCnt = 1; + curW = w; + } + }); + if (curCnt) { + rows.push(curCnt); + } + + var i = 0; + var thumbs = []; + var lastRowI = rows.length - 1; + angular.forEach(rows, function (rowCnt, rowI) { + var lastRow = rowI == lastRowI; + var curRatios = ratios.slice(i, i + rowCnt); + var sumRatios = 0; + angular.forEach(curRatios, function (ratio) { + sumRatios += ratio; + }); + angular.forEach(curRatios, function (ratio, j) { + var thumbH = rowH; + var thumbW = rowW * ratio / sumRatios; + var realW = thumbH * ratio; + if (lastRow && thumbW > realW) { + thumbW = realW; + } + var result = results[i + j]; + result.thumbW = Math.floor(thumbW); + result.thumbH = Math.floor(thumbH); + }); + + i += rowCnt; + }); + } + function sendInlineResult (peerID, qID, options) { var inlineResult = inlineResults[qID]; if (inlineResult === undefined) { diff --git a/app/less/app.less b/app/less/app.less index 9c7bab0a..1c9cc057 100644 --- a/app/less/app.less +++ b/app/less/app.less @@ -3153,7 +3153,7 @@ _:-ms-lang(x), .composer_rich_textarea:empty:focus:before { } .inline_result_wrap { - display: block; + display: inline-block; } .inline_result_article { display: block; @@ -3184,6 +3184,13 @@ _:-ms-lang(x), .composer_rich_textarea:empty:focus:before { font-weight: bold; } +.inline_result_gif { + display: inline-block; +} +.composer_dropdown > li > a.inline_result_gif { + padding: 0; +} + .error_modal_window { .modal-dialog { diff --git a/app/partials/desktop/inline_results.html b/app/partials/desktop/inline_results.html index b92c0a8c..3ea3ea5c 100644 --- a/app/partials/desktop/inline_results.html +++ b/app/partials/desktop/inline_results.html @@ -1,5 +1,11 @@