diff --git a/app/js/controllers.js b/app/js/controllers.js index eaaf15f0..b7ffc75d 100644 --- a/app/js/controllers.js +++ b/app/js/controllers.js @@ -3848,6 +3848,13 @@ angular.module('myApp.controllers', ['myApp.i18n']) $scope.$broadcast('ui_height'); $scope.stickersetLoaded = true; $scope.stickerset = result.set; + $scope.stickersetInstalled = result.installed; $scope.documents = result.documents; }); + + $scope.toggleInstalled = function (installed) { + AppStickersManager.installStickerset($scope.stickerset, !installed).then(function () { + $scope.stickersetInstalled = installed; + }) + } }) diff --git a/app/js/directives.js b/app/js/directives.js index 5066c9bc..1b6ba3a0 100755 --- a/app/js/directives.js +++ b/app/js/directives.js @@ -925,6 +925,33 @@ angular.module('myApp.directives', ['myApp.filters']) }) + .directive('myStickersList', function($window, $timeout) { + + return { + link: link + }; + + function link ($scope, element, attrs) { + var stickersWrap = $('.stickerset_wrap', element)[0]; + + onContentLoaded(function () { + $(stickersWrap).nanoScroller({preventPageScrolling: true, tabIndex: -1, iOSNativeScrolling: true}); + updateSizes(); + }); + + function updateSizes () { + $(element).css({ + height: Math.min(500, $($window).height() + - (Config.Mobile ? 46 + 18 : 200)) + }); + $(stickersWrap).nanoScroller(); + } + + $($window).on('resize', updateSizes); + }; + + }) + .directive('myHistory', function ($window, $timeout, $rootScope, $transition) { return { @@ -1249,7 +1276,7 @@ angular.module('myApp.directives', ['myApp.filters']) }) - .directive('mySendForm', function ($timeout, $compile, $modalStack, $http, $interpolate, Storage, AppStickersManager, ErrorService) { + .directive('mySendForm', function ($timeout, $compile, $modalStack, $http, $interpolate, Storage, AppStickersManager, AppDocsManager, ErrorService) { return { link: link, @@ -1270,14 +1297,28 @@ angular.module('myApp.directives', ['myApp.filters']) var dragStarted, dragTimeout; var submitBtn = $('.im_submit', element)[0]; + var stickerImageCompiled = $compile(''); + var cachedStickerImages = {}; + var emojiTooltip = new EmojiTooltip(emojiButton, { getStickers: function (callback) { - AppStickersManager.getStickers().then(function () { - AppStickersManager.getStickersImages().then(function (stickersData) { - callback(stickersData); - }); + AppStickersManager.getStickers().then(callback); + }, + getStickerImage: function (element, docID) { + if (cachedStickerImages[docID]) { + element.replaceWith(cachedStickerImages[docID]); + return; + } + var scope = $scope.$new(true); + scope.document = AppDocsManager.getDoc(docID); + stickerImageCompiled(scope, function (clonedElement) { + cachedStickerImages[docID] = clonedElement; + element.replaceWith(clonedElement); }); }, + onStickersetSelected: function (stickerset) { + AppStickersManager.openStickersetLink(stickerset); + }, onEmojiSelected: function (code) { $scope.$apply(function () { composer.onEmojiSelected(code); @@ -1817,7 +1858,9 @@ angular.module('myApp.directives', ['myApp.filters']) }; function link ($scope, element, attrs) { - var imgElement = $('').appendTo(element); + var imgElement = $('') + .appendTo(element) + .addClass(attrs.imgClass); var setSrc = function (blob) { if (WebpManager.isWebpSupported()) { @@ -1863,11 +1906,19 @@ angular.module('myApp.directives', ['myApp.filters']) imgElement.attr('src', emptySrc); } - MtpApiFileManager.downloadFile($scope.document.dc_id, fullLocation, $scope.document.size).then(function (blob) { - setSrc(blob); - }, function (e) { - console.log('Download sticker failed', e, fullLocation); - }); + if (attrs.thumb) { + MtpApiFileManager.downloadSmallFile(smallLocation).then(function (blob) { + setSrc(blob); + }, function (e) { + console.log('Download sticker failed', e, fullLocation); + }); + } else { + MtpApiFileManager.downloadFile($scope.document.dc_id, fullLocation, $scope.document.size).then(function (blob) { + setSrc(blob); + }, function (e) { + console.log('Download sticker failed', e, fullLocation); + }); + } } }) diff --git a/app/js/locales/en-us.json b/app/js/locales/en-us.json index f9e9f910..02d81eaf 100644 --- a/app/js/locales/en-us.json +++ b/app/js/locales/en-us.json @@ -86,6 +86,11 @@ "sessions_modal_terminate_all": "Terminate all other sessions", "sessions_modal_active_sessions": "Active sessions", + "stickerset_modal_title_loading": "Stickerset", + "stickerset_modal_install": "Add stickers", + "stickerset_modal_uninstall": "Remove stickers", + "stickerset_modal_loading": "Loading{dots}", + "page_title_pluralize_notifications": "{'0': 'No notifications', 'one': '1 notification', 'other': '{} notifications'}", "profile_edit_modal_title": "Edit profile", diff --git a/app/js/message_composer.js b/app/js/message_composer.js index e03a07b9..b6ed70c6 100644 --- a/app/js/message_composer.js +++ b/app/js/message_composer.js @@ -138,6 +138,8 @@ function EmojiTooltip (btnEl, options) { this.onEmojiSelected = options.onEmojiSelected; this.onStickerSelected = options.onStickerSelected; this.getStickers = options.getStickers; + this.getStickerImage = options.getStickerImage; + this.onStickersetSelected = options.onStickersetSelected; if (!Config.Navigator.touch) { $(this.btnEl).on('mouseenter mouseleave', function (e) { @@ -258,7 +260,7 @@ EmojiTooltip.prototype.createTooltip = function () { this.contentEl.on('mousedown', function (e) { e = e.originalEvent || e; - var target = $(e.target), code, sticker; + var target = $(e.target), code, sticker, stickerset; if (target[0].tagName != 'A') { target = $(target[0].parentNode); } @@ -276,6 +278,12 @@ EmojiTooltip.prototype.createTooltip = function () { self.hide(); } } + if (stickerset = target.attr('data-stickerset')) { + if (self.onStickersetSelected) { + self.onStickersetSelected(stickerset); + } + self.hide(); + } return cancelEvent(e); }); @@ -298,7 +306,7 @@ EmojiTooltip.prototype.createTooltip = function () { EmojiTooltip.prototype.selectTab = function (tab) { - if (this.tab === tab) { + if (this.tab === tab && tab != 6) { return false; } $('.active', this.tabsEl).removeClass('active'); @@ -308,7 +316,7 @@ EmojiTooltip.prototype.selectTab = function (tab) { this.updateTabContents(); }; -EmojiTooltip.prototype.updateTabContents = function (tab) { +EmojiTooltip.prototype.updateTabContents = function () { var html = []; var self = this; var iconSize = Config.Mobile ? 26 : 20; @@ -325,14 +333,32 @@ EmojiTooltip.prototype.updateTabContents = function (tab) { } if (this.tab == 6) { // Stickers - var renderStickers = function (stickers) { - var sticker, i; - var count = stickers.length; - for (i = 0; i < count; i++) { - sticker = stickers[i]; - html.push(''); + var renderStickers = function (stickersets) { + var set, docID, i, j, len1, len2; + for (i = 0, len1 = stickersets.length; i < len1; i++) { + set = stickersets[i]; + if (!set.docIDs.length) { + continue; + } + if (set.id && set.title) { + html.push( + '', + encodeEntities(set.title), + '' + ); + } + for (j = 0, len2 = set.docIDs.length; j < len2; j++) { + docID = set.docIDs[j]; + html.push(''); + } } renderContent(); + + self.contentEl.find('.composer_sticker_btn').each(function (k, element) { + self.getStickerImage($(element), element.getAttribute('data-sticker')); + }); }; this.getStickers(renderStickers); } @@ -381,6 +407,7 @@ EmojiTooltip.prototype.updatePosition = function () { EmojiTooltip.prototype.show = function () { this.updatePosition(); + this.updateTabContents(); this.tooltipEl.addClass('composer_emoji_tooltip_shown'); this.btnEl.addClass('composer_emoji_insert_btn_on'); delete this.showTimeout; diff --git a/app/js/services.js b/app/js/services.js index bf678957..fa3bcbf6 100755 --- a/app/js/services.js +++ b/app/js/services.js @@ -3348,6 +3348,10 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) apiDoc.stickerEmojiRaw = attribute.alt; apiDoc.stickerEmoji = RichTextProcessor.wrapRichText(apiDoc.stickerEmojiRaw, {noLinks: true, noLinebreaks: true}); } + if (attribute.stickerset && + attribute.stickerset._ == 'inputStickerSetID') { + apiDoc.stickerSetID = attribute.stickerset.id; + } break; case 'documentAttributeImageSize': apiDoc.w = attribute.w; @@ -3640,16 +3644,19 @@ 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) { var currentStickers = []; + var currentStickersets = []; var installedStickersets = {}; + var stickersetItems = {}; var applied = false; var started = false; return { start: start, openStickersetLink: openStickersetLink, + installStickerset: installStickerset, getStickers: getStickers, getStickerset: getStickerset, getStickersImages: getStickersImages @@ -3670,26 +3677,49 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) function processRawStickers(stickers) { if (applied !== stickers.hash) { applied = stickers.hash; - var i, j, len1, len2, doc; + var i, j, len1, len2, doc, setID, set; len1 = stickers.documents.length; currentStickers = []; + stickersetItems = {}; for (i = 0; i < len1; i++) { doc = stickers.documents[i]; AppDocsManager.saveDoc(doc); currentStickers.push(doc.id); + setID = doc.stickerSetID || 0; + if (stickersetItems[setID] === undefined) { + stickersetItems[setID] = []; + } + stickersetItems[setID].push(doc.id); + } + + if (stickersetItems[0] !== undefined) { + currentStickersets.push({ + _: 'stickerSetDefault', + id: 0, + docIDs: stickersetItems[0] + }); } + len1 = stickers.sets.length; + for (i = 0; i < len1; i++) { + set = stickers.sets[i]; + installedStickersets[set.id] = true; + set.docIDs = stickersetItems[set.id] || []; + currentStickersets.push(set); + } + } - return currentStickers; + + return currentStickersets; } - function getStickers () { + 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)) { + if (stickers && stickers.date > tsNow(true) && !force) { return processRawStickers(stickers); } return MtpApiManager.invokeApi('messages.getAllStickers', { @@ -3742,10 +3772,32 @@ 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; }); } + function installStickerset (set, uninstall) { + var method = uninstall + ? 'messages.uninstallStickerSet' + : 'messages.installStickerSet'; + var inputStickerset = { + _: 'inputStickerSetID', + id: set.id, + access_hash: set.access_hash + }; + return MtpApiManager.invokeApi(method, { + stickerset: inputStickerset + }).then(function (result) { + if (uninstall) { + delete installedStickersets[set.id]; + } else { + installedStickersets[set.id] = true; + } + getStickers(true); + }); + } + function openStickersetLink (shortName) { var scope = $rootScope.$new(true); scope.inputStickerset = { @@ -3755,8 +3807,8 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) var modal = $modal.open({ templateUrl: templateUrl('stickerset_modal'), controller: 'StickersetModalController', - scope: scope/*, - windowClass: 'error_modal_window'*/ + scope: scope, + windowClass: 'stickerset_modal_window' }); } }) diff --git a/app/less/app.less b/app/less/app.less index e359c45b..9be84bb6 100644 --- a/app/less/app.less +++ b/app/less/app.less @@ -2474,6 +2474,20 @@ a.composer_emoji_btn { vertical-align: top; } +.composer_stickerset_title { + display: block; + clear: both; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + font-weight: bold; + font-size: 13px; + color: #444; + margin: 10px 0 3px; +} +.composer_stickerset_title:hover { + color: #444; +} .composer_sticker_btn { width: 78px; height: 78px; @@ -2917,6 +2931,49 @@ a.contacts_modal_contact:hover .md_modal_list_peer_description, margin: 15px 0 20px 24px; } + +.stickerset_modal_window .modal-dialog { + max-width: 474px; +} +.stickerset_modal_stickers_list { + padding: 25px; +} +.stickerset_modal_sticker_wrap { + list-style: none; + margin: 0 10px 10px 0; + padding: 0; + position: relative; + display: block; + width: 96px; + height: 96px; + float: left; +} +.stickerset_modal_sticker { + width: 96px; + height: 96px; +} +.stickerset_modal_sticker img { + width: 96px; + height: 96px; +} +.stickerset_modal_sticker_alt { + position: absolute; + bottom: 0; + right: 0; +} +.stickerset_actions { + padding: 10px 20px; + text-align: center; +} +.stickerset_modal_loading { + text-align: center; + color: #999; + font-size: 16px; + line-height: 18px; + padding: 1px 50px; + margin: 0; +} + .modal-dialog { .md_simple_modal_window &, .confirm_modal_window &, diff --git a/app/less/desktop.less b/app/less/desktop.less index 781b5807..d00863ac 100644 --- a/app/less/desktop.less +++ b/app/less/desktop.less @@ -646,6 +646,7 @@ a.footer_link.active:active { .im_history_col .nano > &, .contacts_modal_col .nano > &, .sessions_modal_col .nano > &, + .stickerset_modal_col .nano > &, .im_dialogs_modal_col .nano > & { background : rgba(216,223,225,0.45); /*45% d8dfe5*/ width : 9px; @@ -661,7 +662,8 @@ a.footer_link.active:active { right: 4px; } - .sessions_modal_col .nano > & { + .sessions_modal_col .nano > &, + .stickerset_modal_col .nano > & { top: 4px; bottom: 4px; width: 5px; @@ -681,6 +683,7 @@ a.footer_link.active:active { .im_history_col .nano > &, .contacts_modal_col .nano > &, .sessions_modal_col .nano > &, + .stickerset_modal_col .nano > &, .im_dialogs_modal_col .nano > & { & > .nano-slider { background : rgba(137,160,179,0.50); /*50% 89a0b3*/ diff --git a/app/partials/desktop/stickerset_modal.html b/app/partials/desktop/stickerset_modal.html index 445ea124..09065913 100644 --- a/app/partials/desktop/stickerset_modal.html +++ b/app/partials/desktop/stickerset_modal.html @@ -6,12 +6,15 @@ -
+
+ + +
-
+
@@ -22,17 +25,11 @@
-
-

- -
    - -
  • - -
    -
  • - -
+
+
+
+
+
@@ -40,6 +37,13 @@
+
+
+ + +
+
+