From b0b5e2c75c103d361733988ffb4c9d1ba746eca2 Mon Sep 17 00:00:00 2001 From: Igor Zhukov Date: Mon, 28 Sep 2015 23:25:55 +0300 Subject: [PATCH] Channels improvements Supported load important history Added channel profile --- app/img/icons/ProfileIcons.png | Bin 3237 -> 3533 bytes app/img/icons/ProfileIcons_2x.png | Bin 6157 -> 7097 bytes app/js/controllers.js | 282 +++++++++++-- app/js/directives.js | 30 +- app/js/locales/en-us.json | 17 + app/js/services.js | 391 +++++++++++++----- app/less/app.less | 15 +- app/partials/desktop/channel_edit_modal.html | 28 ++ app/partials/desktop/channel_modal.html | 136 ++++++ .../desktop/chat_invite_link_modal.html | 26 +- app/partials/desktop/confirm_modal.html | 2 + app/partials/desktop/dialog.html | 2 +- app/partials/desktop/im.html | 2 +- app/partials/desktop/message.html | 14 +- app/partials/desktop/reply_message.html | 2 +- app/partials/mobile/dialog.html | 2 +- app/partials/mobile/im.html | 2 +- app/partials/mobile/message.html | 12 +- 18 files changed, 801 insertions(+), 162 deletions(-) create mode 100644 app/partials/desktop/channel_edit_modal.html create mode 100644 app/partials/desktop/channel_modal.html diff --git a/app/img/icons/ProfileIcons.png b/app/img/icons/ProfileIcons.png index f7135af97c92920cbb8be79966dd83fe5311a84e..2051aa1095b90330e2b754223de863cb4f64bdbb 100644 GIT binary patch delta 2889 zcmZ`*dpOhW8{dXG#&Rm-WhCb*HbjYSj!~~SqZlEZ9FkKh$M2X)@5FMd4axhGMV7N1 z3Y9}eSj=ge)0-kBN+SJKzxVgg@9KK~xv$T4-`D+ouKV+O?x$FxN0GiutVje30)cQF zXQC@r_d-1O8(}awG#YD+GBq(Uur|VCP&QVEhA1m*v@y=w+Efj3DLC}#Q8TL$-wVN{ zbLZ`?%+l5r5qP^Q+oSeE}?>12+l= zfy6NORv7%H(RcY#DK0KboxL|k5|LI7JMndY;N`WiAhV#^!~kZURt=+pII4?cDJ`e7 z{A^GjRaj^D9;CMJlOygVcfFqOs+4ElS~@aD5nr~NTTa|#tSv?_J~)%Nl%R6v_01Um z29G}yKfCtg_+Ve3w})hT>NJo*7F0)o31;5&EU5u5myI1LpW*0tk+FppPvmw)B3Cjz zJ)ceSpL+z&N>9|2*!gqP6PhIU1>DG?F1glh61$vw_M%Xzc2VIqn|)Tut>>baMEXv*1WR0VQKNb`a5se^%^%$o5VozmvAw^&6d{0gdy}+p<$;CyWkASEa+*>uL7z0CvfvZ01*S)$~@aOM6T#$ycnmv}T|MW6G3N z&>Qvv{1#gQ&7U4;pey>n1Bik} z?JObE6(g(dhvQbCrYNJwn=x;h#PPWUs|^LSPm|E20}$eQq-hl_si9 z3+QLHiN|uH8$rVp(a2MinRPs&)-ZQf_+>BEi~mTnYHJS8Sa-Q*x4TnXZdt#&8zYV8 zl{Y1NYTC3k&j$QeL-f zDOZgX_ah39Hc4|-jo(3*I8zuytaP}ZVbCGfF}8xsv+gZ?Fm`wC z`BWM6$XjmJNySrP_;JXH$wVaMv6+COGGc7`okSe_Y-71z4mLhWb`ha)BX2c-b1t)TFasPUUN_G@q+L;)T- z#L|M-fHg67zPZ^p@?*T>O3zY~lZKYOrnb%3n0saFg*`~ZOPoVQf?KzqG16zzm9CKy z7=Mp5A@A%r)OQ0unwJbE5poLjv`E$KA+%j-wC%|Q-_^|~px)3(l0n7m97Dygg^m(j zu}~AWXx^;+`g=u?siruMd6Swm?WQFozdIzPUI0&Ao3Uo2QCi1@*4-R5`jms4k@@^b zMk7689^+(j(DBP+=bM-7TD^p?G2W_7xt|F>okQew<9El{)jbqgA4Z!)W(2151^28q z1B{{jC+n&#+OjMPW*ny%Xr~G;Dx8ltze1ec#caPyQTnt}yZ$Y~f+3JL$fs-sF?6Yl zMFG@9;Elo$JW=}9(mTE`MC4NwJH_;tjTtu@4a3Z6%n<88YBbbYvMk|33l1)iY&gQs zCeqrMibIlprGMC2{nlRJ$uHW^V}UqSb*D#0K?pRoFcw92JOnlo-C{_4=lfMJjzxX*tEehIw zPm80}yo*K21%{pFI}W=$@+IO>N|~^qdR^Epp7*hzV=jKT9r&T&Z3o_7a;p&CHChcD zibC)F)94D2(cu-=@r*pp7@4BWfX)F%dX@K`S%6J@Ht8HrpU4veL;0>u%bx=xV{@Uy1@l5b-qZY-H7vf zl_eWp@v(!z$x(v2XUPrs5}>jDrInyAjJxxY8zj$H&M78K-=}&nqS?Fo@%8e+ z(56q)50Id=-`kZ1i}a>q%vJOBpqn~I6gm3oJ6(Sy-NP8$B*lyp!&(gyC5lVlOIkti za+n`FcX!mYigTTB?R0A;mT*U zNTZU7&i0@3YVGR4vO>RRk+L(?)`iccH>lqPV4w%8cfJ6^x^2{+Dg0LonII+fWJPMK zO=AlaJ5fj0OZ!LV$?WK|6r`&WsF3zR+zKqa0>J1WVY*vw9j4MH60WOK5JRtT2+-)n zj#rf&9JA_LgeV}DU~GYcc5?&$DgWBJ*y{}JUZ$gM(w&(d@owjERdtIkVzU_ zUZ-MxZ0F*_6z-R$&VHMh4K2HGdro*}g#nsN|MMlH-fB}n$`5UMu_V7eY$8=sPCfO= zQ3|uXS6y#Sps;ZP3 ZAiI%`(;5!NPTOB*kiGRWD>l|U@jt6#BC-Gg delta 2591 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 Ub^O*I`T8C()HT(q(t<_)4;FP%0ssI2 delta 5235 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 diff --git a/app/js/controllers.js b/app/js/controllers.js index da2217bf..504660bc 100644 --- a/app/js/controllers.js +++ b/app/js/controllers.js @@ -706,7 +706,7 @@ angular.module('myApp.controllers', ['myApp.i18n']) $scope.$on('history_delete', function (e, historyUpdate) { for (var i = 0; i < $scope.dialogs.length; i++) { if ($scope.dialogs[i].peerID == historyUpdate.peerID) { - if (historyUpdate.msgs[$scope.dialogs[i].id]) { + if (historyUpdate.msgs[$scope.dialogs[i].mid]) { $scope.dialogs[i].deleted = true; } break; @@ -1490,7 +1490,7 @@ angular.module('myApp.controllers', ['myApp.i18n']) i, startPos, curMessageID; for (i = 0; i < peerHistory.messages.length; i++) { - if (peerHistory.messages[i].id == lastSelectID) { + if (peerHistory.messages[i].mid == lastSelectID) { startPos = i; break; } @@ -1498,7 +1498,7 @@ angular.module('myApp.controllers', ['myApp.i18n']) i = startPos; while (peerHistory.messages[i] && - (curMessageID = peerHistory.messages[i].id) != messageID) { + (curMessageID = peerHistory.messages[i].mid) != messageID) { if (!$scope.selectedMsgs[curMessageID]) { $scope.selectedMsgs[curMessageID] = true; $scope.selectedCount++; @@ -1646,7 +1646,7 @@ angular.module('myApp.controllers', ['myApp.i18n']) return; } AppMessagesManager.sendText(peerID, button.text, { - replyToMsgID: peerID < 0 && replyKeyboard.id + replyToMsgID: peerID < 0 && replyKeyboard.mid }); }); @@ -1700,7 +1700,7 @@ angular.module('myApp.controllers', ['myApp.i18n']) !historyMessage.out && !(history.messages[history.messages.length - 2] || {}).unread) { - $scope.historyUnreadAfter = historyMessage.id; + $scope.historyUnreadAfter = historyMessage.mid; unreadAfterIdle = true; $scope.$broadcast('messages_unread_after'); } @@ -1740,7 +1740,7 @@ angular.module('myApp.controllers', ['myApp.i18n']) if (len > 10) { if (curPeer) { if (exlen > 10) { - minID = history.messages[exlen - 1].id; + minID = history.messages[exlen - 1].mid; $scope.historyState.skipped = hasLess = minID > 0; if (hasLess) { loadAfterSync = peerID; @@ -1825,7 +1825,7 @@ angular.module('myApp.controllers', ['myApp.i18n']) i; for (i = 0; i < history.messages.length; i++) { - if (!historyUpdate.msgs[history.messages[i].id]) { + if (!historyUpdate.msgs[history.messages[i].mid]) { newMessages.push(history.messages[i]); } }; @@ -1949,7 +1949,7 @@ angular.module('myApp.controllers', ['myApp.i18n']) var timeout = 0; var options = { - replyToMsgID: $scope.draftMessage.replyToMessage && $scope.draftMessage.replyToMessage.id + replyToMsgID: $scope.draftMessage.replyToMessage && $scope.draftMessage.replyToMessage.mid }; do { @@ -2074,7 +2074,7 @@ angular.module('myApp.controllers', ['myApp.i18n']) var message = $scope.draftMessage.replyToMessage; if (message && $scope.historyState.replyKeyboard && - $scope.historyState.replyKeyboard.id == message.id && + $scope.historyState.replyKeyboard.mid == message.mid && !$scope.historyState.replyKeyboard.pFlags.hidden) { $scope.historyState.replyKeyboard.pFlags.hidden = true; $scope.$broadcast('ui_keyboard_update'); @@ -2110,7 +2110,7 @@ angular.module('myApp.controllers', ['myApp.i18n']) (replyKeyboard._ == 'replyKeyboardMarkup' && peerID < 0)); if (addReplyMessage) { - replySelect(replyKeyboard.id); + replySelect(replyKeyboard.mid); replyToMarkup = true; } else if (replyToMarkup) { @@ -2162,7 +2162,7 @@ angular.module('myApp.controllers', ['myApp.i18n']) return; } var options = { - replyToMsgID: $scope.draftMessage.replyToMessage && $scope.draftMessage.replyToMessage.id, + replyToMsgID: $scope.draftMessage.replyToMessage && $scope.draftMessage.replyToMessage.mid, isMedia: $scope.draftMessage.isMedia }; @@ -2196,7 +2196,7 @@ angular.module('myApp.controllers', ['myApp.i18n']) } } var options = { - replyToMsgID: $scope.draftMessage.replyToMessage && $scope.draftMessage.replyToMessage.id + replyToMsgID: $scope.draftMessage.replyToMessage && $scope.draftMessage.replyToMessage.mid }; AppMessagesManager.sendOther($scope.curDialog.peerID, inputMedia, options); $scope.$broadcast('ui_message_send'); @@ -2647,15 +2647,18 @@ angular.module('myApp.controllers', ['myApp.i18n']) }) - .controller('ChatpicModalController', function ($q, $scope, $rootScope, $modalInstance, MtpApiManager, AppPhotosManager, AppChatsManager, AppPeersManager, AppMessagesManager, PeersSelectService, ErrorService) { + .controller('ChatpicModalController', function ($q, $scope, $rootScope, $modalInstance, MtpApiManager, AppPhotosManager, AppChatsManager, AppPeersManager, AppMessagesManager, ApiUpdatesManager, PeersSelectService, ErrorService) { $scope.photo = AppPhotosManager.wrapForFull($scope.photoID); $scope.photo.thumb = { location: AppPhotosManager.choosePhotoSize($scope.photo, 0, 0).location }; + var chat = AppChatsManager.getChat($scope.chatID); + var isChannel = AppChatsManager.isChannel($scope.chatID); + $scope.canForward = true; - $scope.canDelete = true; + $scope.canDelete = isChannel ? chat.pFlags.creator : true; $scope.forward = function () { PeersSelectService.selectPeers({confirm_type: 'FORWARD_PEER'}).then(function (peerStrings) { @@ -2679,10 +2682,19 @@ angular.module('myApp.controllers', ['myApp.i18n']) $scope['delete'] = function () { ErrorService.confirm({type: 'PHOTO_DELETE'}).then(function () { $scope.photo.updating = true; - MtpApiManager.invokeApi('messages.editChatPhoto', { - chat_id: AppChatsManager.getChatInput($scope.chatID), - photo: {_: 'inputChatPhotoEmpty'} - }).then(function (updates) { + var apiPromise; + if (AppChatsManager.isChannel($scope.chatID)) { + apiPromise = MtpApiManager.invokeApi('channels.editPhoto', { + channel: AppChatsManager.getChannelInput($scope.chatID), + photo: {_: 'inputChatPhotoEmpty'} + }); + } else { + apiPromise = MtpApiManager.invokeApi('messages.editChatPhoto', { + chat_id: AppChatsManager.getChatInput($scope.chatID), + photo: {_: 'inputChatPhotoEmpty'} + }); + } + apiPromise.then(function (updates) { ApiUpdatesManager.processUpdateMessage(updates); $modalInstance.dismiss(); $rootScope.$broadcast('history_focus', {peerString: AppChatsManager.getChatString($scope.chatID)}); @@ -3069,6 +3081,150 @@ angular.module('myApp.controllers', ['myApp.i18n']) }) + .controller('ChannelModalController', function ($scope, $timeout, $rootScope, $modal, AppUsersManager, AppChatsManager, AppPhotosManager, MtpApiManager, MtpApiFileManager, NotificationsManager, AppMessagesManager, AppPeersManager, ApiUpdatesManager, ContactsSelectService, ErrorService) { + + $scope.chatFull = AppChatsManager.wrapForFull($scope.chatID, {}); + $scope.settings = {notifications: true}; + + AppChatsManager.getChannelFull($scope.chatID, true).then(function (chatFull) { + $scope.chatFull = AppChatsManager.wrapForFull($scope.chatID, chatFull); + $scope.$broadcast('ui_height'); + + NotificationsManager.savePeerSettings(-$scope.chatID, chatFull.notify_settings); + + NotificationsManager.getPeerMuted(-$scope.chatID).then(function (muted) { + $scope.settings.notifications = !muted; + + $scope.$watch('settings.notifications', function(newValue, oldValue) { + if (newValue === oldValue) { + return false; + } + NotificationsManager.getPeerSettings(-$scope.chatID).then(function (settings) { + if (newValue) { + settings.mute_until = 0; + } else { + settings.mute_until = 2000000000; + } + NotificationsManager.updatePeerSettings(-$scope.chatID, settings); + }); + }); + }); + }); + + + function onChatUpdated (updates) { + ApiUpdatesManager.processUpdateMessage(updates); + $rootScope.$broadcast('history_focus', {peerString: $scope.chatFull.peerString}); + } + + $scope.leaveChannel = function () { + MtpApiManager.invokeApi('channels.leaveChannel', { + channel: AppChatsManager.getChannelInput($scope.chatID) + }).then(onChatUpdated); + }; + + $scope.deleteChannel = function () { + return ErrorService.confirm({type: 'CHANNEL_DELETE'}).then(function () { + MtpApiManager.invokeApi('channels.deleteChannel', { + channel: AppChatsManager.getChannelInput($scope.chatID) + }).then(onChatUpdated); + }); + } + + $scope.joinChannel = function () { + MtpApiManager.invokeApi('channels.joinChannel', { + channel: AppChatsManager.getChannelInput($scope.chatID) + }).then(onChatUpdated); + }; + + $scope.inviteToChannel = function () { + var disabled = []; + angular.forEach(($scope.chatFull.participants || {}).participants || [], function(participant){ + disabled.push(participant.user_id); + }); + + ContactsSelectService.selectContacts({disabled: disabled}).then(function (userIDs) { + var inputUsers = []; + angular.forEach(userIDs, function (userID) { + inputUsers.push(AppUsersManager.getUserInput(userID)); + }); + MtpApiManager.invokeApi('channels.inviteToChannel', { + channel: AppChatsManager.getChannelInput($scope.chatID), + users: inputUsers + }).then(onChatUpdated); + }); + }; + + $scope.kickFromChannel = function (userID) { + MtpApiManager.invokeApi('channels.kickFromChannel', { + channel: AppChatsManager.getChannelInput($scope.chatID), + user_id: AppUsersManager.getUserInput(userID), + kicked: true + }).then(onChatUpdated); + }; + + $scope.shareLink = function ($event) { + var scope = $rootScope.$new(); + scope.chatID = $scope.chatID; + + $modal.open({ + templateUrl: templateUrl('chat_invite_link_modal'), + controller: 'ChatInviteLinkModalController', + scope: scope, + windowClass: 'md_simple_modal_window' + }); + + return cancelEvent($event); + } + + + $scope.photo = {}; + + $scope.$watch('photo.file', onPhotoSelected); + + function onPhotoSelected (photo) { + if (!photo || !photo.type || photo.type.indexOf('image') !== 0) { + return; + } + $scope.photo.updating = true; + MtpApiFileManager.uploadFile(photo).then(function (inputFile) { + return MtpApiManager.invokeApi('channels.editPhoto', { + channel: AppChatsManager.getChannelInput($scope.chatID), + photo: { + _: 'inputChatUploadedPhoto', + file: inputFile, + crop: {_: 'inputPhotoCropAuto'} + } + }).then(onChatUpdated); + })['finally'](function () { + $scope.photo.updating = false; + }); + }; + + $scope.deletePhoto = function () { + $scope.photo.updating = true; + MtpApiManager.invokeApi('messages.editChatPhoto', { + channel: AppChatsManager.getChannelInput($scope.chatID), + photo: {_: 'inputChatPhotoEmpty'} + }).then(onChatUpdated)['finally'](function () { + $scope.photo.updating = false; + }); + }; + + $scope.editChannel = function () { + var scope = $rootScope.$new(); + scope.chatID = $scope.chatID; + + $modal.open({ + templateUrl: templateUrl('channel_edit_modal'), + controller: 'ChannelEditModalController', + scope: scope, + windowClass: 'md_simple_modal_window mobile_modal' + }); + } + + }) + .controller('SettingsModalController', function ($rootScope, $scope, $timeout, $modal, AppUsersManager, AppChatsManager, AppPhotosManager, MtpApiManager, Storage, NotificationsManager, MtpApiFileManager, PasswordManager, ApiUpdatesManager, ChangelogNotifyService, LayoutSwitchService, AppRuntimeManager, ErrorService, _) { $scope.profile = {}; @@ -3911,12 +4067,21 @@ angular.module('myApp.controllers', ['myApp.i18n']) $scope.group.updating = true; - return MtpApiManager.invokeApi('messages.editChatTitle', { - chat_id: AppChatsManager.getChatInput($scope.chatID), - title: $scope.group.name - }).then(function (updates) { - ApiUpdatesManager.processUpdateMessage(updates); + var apiPromise; + if (AppChatsManager.isChannel($scope.chatID)) { + apiPromise = MtpApiManager.invokeApi('channels.editTitle', { + channel: AppChatsManager.getChannelInput($scope.chatID), + title: $scope.group.name + }); + } else { + apiPromise = MtpApiManager.invokeApi('messages.editChatTitle', { + chat_id: AppChatsManager.getChatInput($scope.chatID), + title: $scope.group.name + }); + } + return apiPromise.then(function (updates) { + ApiUpdatesManager.processUpdateMessage(updates); var peerString = AppChatsManager.getChatString($scope.chatID); $rootScope.$broadcast('history_focus', {peerString: peerString}); })['finally'](function () { @@ -3925,19 +4090,80 @@ angular.module('myApp.controllers', ['myApp.i18n']) }; }) + .controller('ChannelEditModalController', function ($q, $scope, $modalInstance, $rootScope, MtpApiManager, AppUsersManager, AppChatsManager, ApiUpdatesManager) { + + var channel = AppChatsManager.getChat($scope.chatID); + var initial = {title: channel.title}; + $scope.channel = {title: channel.title}; + + AppChatsManager.getChannelFull($scope.chatID).then(function (channelFull) { + initial.about = channelFull.about; + $scope.channel.about = channelFull.about; + }); + + $scope.updateChannel = function () { + if (!$scope.channel.title.length) { + return; + } + var promises = []; + if ($scope.channel.title != initial.title) { + promises.push(editTitle()); + } + if ($scope.channel.about != initial.about) { + promises.push(editAbout()); + } + + return $q.all(promises).then(function () { + var peerString = AppChatsManager.getChatString($scope.chatID); + $rootScope.$broadcast('history_focus', {peerString: peerString}); + })['finally'](function () { + delete $scope.channel.updating; + }); + }; + + function editTitle () { + return MtpApiManager.invokeApi('channels.editTitle', { + channel: AppChatsManager.getChannelInput($scope.chatID), + title: $scope.channel.title + }).then(function (updates) { + ApiUpdatesManager.processUpdateMessage(updates); + }); + } + + function editAbout () { + return MtpApiManager.invokeApi('channels.editAbout', { + channel: AppChatsManager.getChannelInput($scope.chatID), + about: $scope.channel.about + }); + } + }) + .controller('ChatInviteLinkModalController', function (_, $scope, $timeout, $modalInstance, AppChatsManager, ErrorService) { $scope.exportedInvite = {link: _('group_invite_link_loading_raw')}; + var isChannel = AppChatsManager.isChannel($scope.chatID); + + function selectLink () { + $timeout(function () { + $scope.$broadcast('ui_invite_select'); + }, 100); + } + function updateLink (force) { + var chat = AppChatsManager.getChat($scope.chatID); + if (chat.username) { + $scope.exportedInvite = {link: 'https://telegram.me/' + chat.username, short: true}; + selectLink(); + return; + } if (force) { $scope.exportedInvite.revoking = true; } AppChatsManager.getChatInviteLink($scope.chatID, force).then(function (link) { - $scope.exportedInvite = {link: link}; - $timeout(function () { - $scope.$broadcast('ui_invite_select'); - }, 100); + $scope.exportedInvite = {link: link, canRevoke: true}; + selectLink(); + })['finally'](function () { delete $scope.exportedInvite.revoking; }); @@ -3945,7 +4171,7 @@ angular.module('myApp.controllers', ['myApp.i18n']) $scope.revokeLink = function () { ErrorService.confirm({ - type: 'REVOKE_GROUP_INVITE_LINK' + type: isChannel ? 'REVOKE_CHANNEL_INVITE_LINK' : 'REVOKE_GROUP_INVITE_LINK' }).then(function () { updateLink(true); }) diff --git a/app/js/directives.js b/app/js/directives.js index 74c5a5fb..68e1be5b 100755 --- a/app/js/directives.js +++ b/app/js/directives.js @@ -65,7 +65,7 @@ angular.module('myApp.directives', ['myApp.filters']) needDate = false, unreadAfter = false, applySelected = function () { - if (selected != ($scope.selectedMsgs[$scope.historyMessage.id] || false)) { + if (selected != ($scope.selectedMsgs[$scope.historyMessage.mid] || false)) { selected = !selected; element.toggleClass(selectedClass, selected); } @@ -109,7 +109,7 @@ angular.module('myApp.directives', ['myApp.filters']) $scope.$on('messages_regroup', applyGrouped); $scope.$on('messages_focus', function (e, focusedMsgID) { - if ((focusedMsgID == $scope.historyMessage.id) != focused) { + if ((focusedMsgID == $scope.historyMessage.mid) != focused) { focused = !focused; element.toggleClass(focusClass, focused); } @@ -122,7 +122,7 @@ angular.module('myApp.directives', ['myApp.filters']) if ($scope.peerHistory.peerID != $scope.historyPeer.id) { return; } - if (unreadAfter != ($scope.historyUnreadAfter == $scope.historyMessage.id)) { + if (unreadAfter != ($scope.historyUnreadAfter == $scope.historyMessage.mid)) { unreadAfter = !unreadAfter; if (unreadAfter) { if (unreadAfterSplit) { @@ -362,10 +362,10 @@ angular.module('myApp.directives', ['myApp.filters']) if (!message.loading) { updateMessage($scope, element); } else { - var messageID = message.id; - var stopWaiting = $scope.$on('messages_downloaded', function (e, msgIDs) { - if (msgIDs.indexOf(messageID) != -1) { - $scope.replyMessage = AppMessagesManager.wrapForDialog(messageID); + var mid = message.mid; + var stopWaiting = $scope.$on('messages_downloaded', function (e, mids) { + if (mids.indexOf(mid) != -1) { + $scope.replyMessage = AppMessagesManager.wrapForDialog(mid); updateMessage($scope, element); stopWaiting(); } @@ -379,6 +379,7 @@ angular.module('myApp.directives', ['myApp.filters']) $(element).remove(); return; } + console.log(element[0], message, AppPeersManager.getPeer(message.fromID)); var thumbWidth = 42; var thumbHeight = 42; var thumbPhotoSize; @@ -421,10 +422,14 @@ angular.module('myApp.directives', ['myApp.filters']) var peerID = AppMessagesManager.getMessagePeer(message); var peerString = AppPeersManager.getPeerString(peerID); - $rootScope.$broadcast('history_focus', {peerString: peerString, messageID: message.id}); + $rootScope.$broadcast('history_focus', {peerString: peerString, messageID: message.mid}); }) } + + onContentLoaded(function () { + $scope.$emit('ui_height'); + }) } }) @@ -465,7 +470,7 @@ angular.module('myApp.directives', ['myApp.filters']) function link ($scope, element, attrs) { var message = $scope.message; - var msgID = message.id; + var msgID = message.mid; // var msgID = $scope.$eval(attrs.myMessageText); // var message = AppMessagesManager.getMessage(msgID); @@ -473,7 +478,7 @@ angular.module('myApp.directives', ['myApp.filters']) if (message.pending) { var unlink = $scope.$on('messages_pending', function () { - if (message.id != msgID) { + if (message.mid != msgID) { updateHtml(message, element); unlink(); } @@ -2546,6 +2551,9 @@ angular.module('myApp.directives', ['myApp.filters']) return; } AppChatsManager.getChatFull(chatID).then(function (chatFull) { + if (chatFull.participants_count) { + participantsCount = chatFull.participants_count; + } var participantsVector = (chatFull.participants || {}).participants || []; participantsCount = participantsVector.length; angular.forEach(participantsVector, function (participant) { @@ -2920,7 +2928,7 @@ angular.module('myApp.directives', ['myApp.filters']) if ($scope.message && !$scope.message.out && $scope.message.media_unread) { - AppMessagesManager.readMessages([$scope.message.id]); + AppMessagesManager.readMessages([$scope.message.mid]); } }, 300); }); diff --git a/app/js/locales/en-us.json b/app/js/locales/en-us.json index c843099f..4df24d72 100644 --- a/app/js/locales/en-us.json +++ b/app/js/locales/en-us.json @@ -27,6 +27,14 @@ "group_modal_members": "Members", "group_modal_members_kick": "Remove", + "channel_modal_info": "Channel info", + "channel_modal_share_link": "Share link", + "channel_modal_share_loading": "Loading{dots}", + "channel_modal_join": "Join channel", + "channel_modal_add_member": "Invite members", + "channel_modal_leave_channel": "Leave channel", + "channel_modal_delete_channel": "Delete channel", + "country_select_modal_title": "Country", "settings_modal_title": "Settings", @@ -173,6 +181,12 @@ "group_edit_submit": "Save", "group_edit_submit_active": "Saving...", + "channel_edit_modal_title": "Edit channel", + "channel_edit_name": "Channel name", + "channel_edit_about": "Channel description", + "channel_edit_submit": "Save", + "channel_edit_submit_active": "Saving...", + "group_invite_link_modal_title": "Invite link", "group_invite_link_link_label": "Copy link", "group_invite_link_loading": "Loading...", @@ -204,6 +218,9 @@ "confirm_modal_reset_account_md": "Are you sure?\nThis action can not be undone.\n\nYou will lose all your chats and messages, along with any media and files you shared, if you proceed with resetting your account.", "confirm_modal_join_group_link": "Do you want to join the group «{title}»?", "confirm_modal_revoke_group_link": "Are you sure you want to revoke this link? Once you do, no one will be able to join the group using it.", + "confirm_modal_revoke_channel_link": "Are you sure you want to revoke this link? Once you do, no one will be able to join the channel using it.", + "confirm_modal_delete_channel_md": "Are you sure you want to delete this channel?\n\nAll messages will be removed and all messages will be lost.", + "confirm_modal_are_u_sure": "Are you sure?", "confirm_modal_logout_submit": "Log out", diff --git a/app/js/services.js b/app/js/services.js index 6f223e0f..718d0b3f 100755 --- a/app/js/services.js +++ b/app/js/services.js @@ -113,8 +113,6 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) usernames[searchUsername] = userID; } - apiUser.sortName = SearchIndexManager.cleanSearchText(apiUser.first_name + ' ' + (apiUser.last_name || '')); - apiUser.pFlags = { self: (apiUser.flags & (1 << 10)) > 0, contact: (apiUser.flags & (1 << 11)) > 0, @@ -125,6 +123,8 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) botNoGroups: (apiUser.flags & (1 << 16)) > 0 }; + apiUser.sortName = apiUser.pFlags.deleted ? '' : SearchIndexManager.cleanSearchText(apiUser.first_name + ' ' + (apiUser.last_name || '')); + var nameWords = apiUser.sortName.split(' '); var firstWord = nameWords.shift(); var lastWord = nameWords.pop(); @@ -553,6 +553,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) usernames = {}, chatsFull = {}, chatFullPromises = {}, + channelAccess = {}, cachedPhotoLocations = {}; function saveApiChats (apiChats) { @@ -565,6 +566,23 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) } apiChat.rTitle = RichTextProcessor.wrapRichText(apiChat.title, {noLinks: true, noLinebreaks: true}) || _('chat_title_deleted'); + var flags = apiChat.flags; + apiChat.pFlags = { + creator: (flags & (1 << 0)) > 0, + kicked: (flags & (1 << 1)) > 0, + left: (flags & (1 << 2)) > 0 + }; + + if (apiChat._ == 'channel') { + angular.extend(apiChat.pFlags, { + editor: (apiChat.flags & (1 << 3)) > 0, + moderator: (apiChat.flags & (1 << 4)) > 0, + broadcast: (apiChat.flags & (1 << 5)) > 0, + username: (apiChat.flags & (1 << 6)) > 0, + verified: (apiChat.flags & (1 << 7)) > 0 + }); + }; + var titleWords = SearchIndexManager.cleanSearchText(apiChat.title || '').split(' '); var firstWord = titleWords.shift(); var lastWord = titleWords.pop(); @@ -597,8 +615,17 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) return usernames[username] || 0; } + function saveChannelAccess (id, accessHash) { + channelAccess[id] = accessHash; + } + function isChannel (id) { - return (chats[id] || {})._ == 'channel'; + var chat = chats[id]; + if (chat && chat._ == 'channel' || + channelAccess[id]) { + return true; + } + return false; } function getChatInput (id) { @@ -612,11 +639,14 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) return { _: 'inputChannel', channel_id: id, - access_hash: getChat(id).access_hash || 0 + access_hash: getChat(id).access_hash || channelAccess[id] || 0 } } function getChatFull(id) { + if (isChannel(id)) { + return getChannelFull(id); + } if (chatsFull[id] !== undefined) { if (chats[id].version == chatsFull[id].participants.version || chats[id].left) { @@ -631,13 +661,72 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) }).then(function (result) { saveApiChats(result.chats); AppUsersManager.saveApiUsers(result.users); - if (result.full_chat && result.full_chat.chat_photo.id) { - AppPhotosManager.savePhoto(result.full_chat.chat_photo); + var fullChat = result.full_chat; + if (fullChat && fullChat.chat_photo.id) { + AppPhotosManager.savePhoto(fullChat.chat_photo); } delete chatFullPromises[id]; + chatsFull[id] = fullChat; $rootScope.$broadcast('chat_full_update', id); - return chatsFull[id] = result.full_chat; + return fullChat; + }); + } + + function getChannelFull (id, force) { + if (chatsFull[id] !== undefined && !force) { + return $q.when(chatsFull[id]); + } + if (chatFullPromises[id] !== undefined) { + return chatFullPromises[id]; + } + + return chatFullPromises[id] = MtpApiManager.invokeApi('channels.getFullChannel', { + channel: getChannelInput(id) + }).then(function (result) { + saveApiChats(result.chats); + AppUsersManager.saveApiUsers(result.users); + var fullChannel = result.full_chat; + var chat = getChat(id); + if (fullChannel && fullChannel.chat_photo.id) { + AppPhotosManager.savePhoto(fullChannel.chat_photo); + } + var participantsPromise; + if ((fullChannel.flags & 8) || + chat.pFlags.creator || + chat.pFlags.editor || + chat.pFlags.moderator) { + participantsPromise = getChannelParticipants(id).then(function (participants) { + delete chatFullPromises[id]; + fullChannel.participants = { + _: 'channelParticipants', + participants: participants + }; + }, function (error) { + error.handled = true; + }); + } else { + participantsPromise = $q.when(); + } + return participantsPromise.then(function () { + delete chatFullPromises[id]; + chatsFull[id] = fullChannel; + $rootScope.$broadcast('chat_full_update', id); + + return fullChannel; + }); + }); + } + + function getChannelParticipants (id) { + return MtpApiManager.invokeApi('channels.getParticipants', { + channel: getChannelInput(id), + filter: {_: 'channelParticipantsRecent'}, + offset: 0, + limit: 200 + }).then(function (result) { + AppUsersManager.saveApiUsers(result.users); + return result.participants; }); } @@ -688,25 +777,33 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) var chatFull = angular.copy(fullChat), chat = getChat(id); - if (chatFull.participants && chatFull.participants._ == 'chatParticipants') { MtpApiManager.getUserID().then(function (myID) { chatFull.isAdmin = (myID == chatFull.participants.admin_id); angular.forEach(chatFull.participants.participants, function(participant){ - participant.user = AppUsersManager.getUser(participant.user_id); participant.canLeave = myID == participant.user_id; participant.canKick = !participant.canLeave && (chatFull.isAdmin || myID == participant.inviter_id); + + // just for order by last seen + participant.user = AppUsersManager.getUser(participant.user_id); }); }); } + if (chatFull.participants && chatFull.participants._ == 'channelParticipants') { + var isAdmin = chat.pFlags.creator || chat.pFlags.editor || chat.pFlags.moderator; + angular.forEach(chatFull.participants.participants, function(participant) { + participant.canLeave = !chat.pFlags.creator && participant._ == 'channelParticipantSelf'; + participant.canKick = isAdmin && participant._ == 'channelParticipant'; + + // just for order by last seen + participant.user = AppUsersManager.getUser(participant.user_id); + }); + } + + if (chatFull.about) { + chatFull.rAbout = RichTextProcessor.wrapRichText(chatFull.about, {noLinebreaks: true}); + } - chatFull.thumb = { - placeholder: 'img/placeholders/GroupAvatar'+(Config.Mobile ? chat.num : Math.ceil(chat.num / 2))+'@2x.png', - location: chat && chat.photo && chat.photo.photo_small, - width: 72, - height: 72, - size: 0 - }; chatFull.peerString = getChatString(id); chatFull.chat = chat; @@ -717,12 +814,21 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) var scope = $rootScope.$new(); scope.chatID = chatID; - var modalInstance = $modal.open({ - templateUrl: templateUrl('chat_modal'), - controller: 'ChatModalController', - scope: scope, - windowClass: 'chat_modal_window mobile_modal' - }); + if (isChannel(chatID)) { + var modalInstance = $modal.open({ + templateUrl: templateUrl('channel_modal'), + controller: 'ChannelModalController', + scope: scope, + windowClass: 'chat_modal_window channel_modal_window mobile_modal' + }); + } else { + var modalInstance = $modal.open({ + templateUrl: templateUrl('chat_modal'), + controller: 'ChatModalController', + scope: scope, + windowClass: 'chat_modal_window mobile_modal' + }); + } } $rootScope.$on('apiUpdate', function (e, update) { @@ -779,9 +885,11 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) saveApiChat: saveApiChat, getChat: getChat, isChannel: isChannel, + saveChannelAccess: saveChannelAccess, getChatInput: getChatInput, getChannelInput: getChannelInput, getChatFull: getChatFull, + getChannelFull: getChannelFull, getChatPhoto: getChatPhoto, getChatString: getChatString, getChatInviteLink: getChatInviteLink, @@ -806,6 +914,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) }; } else if (firstChar == 'c') { + AppChatsManager.saveChannelAccess(peerParams[0], peerParams[1]); return { _: 'inputPeerChannel', channel_id: peerParams[0], @@ -875,13 +984,13 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) function resolveUsername (username) { var searchUserName = SearchIndexManager.cleanUsername(username); var foundUserID, foundChatID, foundPeerID, foundUsername; - if (foundUserID == AppUsersManager.resolveUsername(searchUserName)) { + if (foundUserID = AppUsersManager.resolveUsername(searchUserName)) { foundUsername = AppUsersManager.getUser(foundUserID).username; if (SearchIndexManager.cleanUsername(foundUsername) == searchUserName) { return qSync.when(foundUserID); } } - if (foundChatID == AppChatsManager.resolveUsername(searchUserName)) { + if (foundChatID = AppChatsManager.resolveUsername(searchUserName)) { foundUsername = AppChatsManager.getChat(foundChatID).username; if (SearchIndexManager.cleanUsername(foundUsername) == searchUserName) { return qSync.when(-foundChatID); @@ -1171,8 +1280,8 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) var peerText = AppPeersManager.getPeerSearchText(peerID); SearchIndexManager.indexObject(peerID, peerText, dialogsIndex); - var messageID = dialog.top_important_message; - dialog.top_message = peerID + '_' + messageID; + var mid = getFullMessageID(dialog.top_important_message, -peerID); + dialog.top_message = mid; var message = getMessage(dialog.top_message); var topDate = message.date; @@ -1187,7 +1296,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) pushDialogToStorage(dialog); if (historiesStorage[peerID] === undefined) { - var historyStorage = {count: null, history: [messageID], pending: []}; + var historyStorage = {count: null, history: [mid], pending: []}; historiesStorage[peerID] = historyStorage; } @@ -1305,15 +1414,15 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) var promise; if (AppPeersManager.isChannel(peerID)) { promise = MtpApiManager.invokeApi('channels.getImportantHistory', { - peer: inputPeer, - offset_id: maxID || 0, + channel: AppChatsManager.getChannelInput(-peerID), + offset_id: maxID ? getMessageLocalID(maxID) : 0, add_offset: offset || 0, limit: limit || 0 }, {noErrorBox: true}); } else { promise = MtpApiManager.invokeApi('messages.getHistory', { peer: inputPeer, - offset_id: maxID || 0, + offset_id: maxID ? getMessageLocalID(maxID) : 0, add_offset: offset || 0, limit: limit || 0 }, {noErrorBox: true}); @@ -1359,6 +1468,40 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) }); } + var channelLocals = {}; + var channelsByLocals = {}; + var channelCurLocal = 0; + var fullMsgIDModulus = 4294967296; + + function getFullMessageID (msgID, channelID) { + if (!channelID) { + return msgID; + } + msgID = getMessageLocalID(msgID); + var localStart = channelLocals[channelID]; + if (!localStart) { + localStart = (++channelCurLocal) * fullMsgIDModulus; + channelsByLocals[localStart] = channelID; + channelLocals[channelID] = localStart; + } + + return localStart + msgID; + } + + function getMessageIDInfo (fullMsgID) { + if (fullMsgID < fullMsgIDModulus) { + return [fullMsgID, 0]; + } + var msgID = fullMsgID % fullMsgIDModulus; + var channelID = channelsByLocals[fullMsgID - msgID]; + + return [msgID, channelID]; + } + + function getMessageLocalID (fullMsgID) { + return fullMsgID % fullMsgIDModulus; + } + function fillHistoryStorage (inputPeer, maxID, fullLimit, historyStorage) { // console.log('fill history storage', inputPeer, maxID, fullLimit, angular.copy(historyStorage)); return requestHistory (inputPeer, maxID, fullLimit).then(function (historyResult) { @@ -1366,7 +1509,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) var offset = 0; if (!maxID && historyResult.messages.length) { - maxID = historyResult.messages[0].id + 1; + maxID = historyResult.messages[0].mid + 1; } if (maxID > 0) { for (offset = 0; offset < historyStorage.history.length; offset++) { @@ -1381,7 +1524,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) if (mergeReplyKeyboard(historyStorage, message)) { $rootScope.$broadcast('history_reply_markup', {peerID: AppPeersManager.getPeerID(inputPeer)}); } - historyStorage.history.push(message.id); + historyStorage.history.push(message.mid); }); fullLimit -= historyResult.messages.length; @@ -1479,7 +1622,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) var history = []; angular.forEach(historyResult.messages, function (message) { - history.push(message.id); + history.push(message.mid); }); if (!maxID && historyStorage.pending.length) { history = historyStorage.pending.slice().concat(history); @@ -1523,7 +1666,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) } function mergeReplyKeyboard (historyStorage, message) { - // console.log('merge', message.id, message.reply_markup, historyStorage.reply_markup); + // console.log('merge', message.mid, message.reply_markup, historyStorage.reply_markup); if (!message.reply_markup && !message.out && !message.action) { @@ -1532,7 +1675,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) var messageReplyMarkup = message.reply_markup; var lastReplyMarkup = historyStorage.reply_markup; if (messageReplyMarkup) { - if (lastReplyMarkup && lastReplyMarkup.id >= message.id) { + if (lastReplyMarkup && lastReplyMarkup.mid >= message.mid) { return false; } if (messageReplyMarkup.pFlags.selective && @@ -1540,12 +1683,12 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) return false; } if (historyStorage.maxOutID && - message.id < historyStorage.maxOutID && + message.mid < historyStorage.maxOutID && messageReplyMarkup.pFlags.one_time) { messageReplyMarkup.pFlags.hidden = true; } messageReplyMarkup = angular.extend({ - id: message.id + id: message.mid }, messageReplyMarkup); if (messageReplyMarkup._ != 'replyKeyboardHide') { messageReplyMarkup.fromID = message.from_id; @@ -1559,15 +1702,15 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) if (lastReplyMarkup) { if (lastReplyMarkup.pFlags.one_time && !lastReplyMarkup.pFlags.hidden && - (message.id > lastReplyMarkup.id || message.id < 0) && + (message.mid > lastReplyMarkup.mid || message.mid < 0) && message.message) { lastReplyMarkup.pFlags.hidden = true; // console.log('set', historyStorage.reply_markup); return true; } } else if (!historyStorage.maxOutID || - message.id > historyStorage.maxOutID) { - historyStorage.maxOutID = message.id; + message.mid > historyStorage.maxOutID) { + historyStorage.maxOutID = message.mid; } } @@ -1580,7 +1723,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) ) { historyStorage.reply_markup = { _: 'replyKeyboardHide', - id: message.id, + mid: message.mid, flags: 0, pFlags: {} }; @@ -1636,7 +1779,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) for (i = 0; i < historyStorage.history.length; i++) { message = messagesStorage[historyStorage.history[i]]; if (message.media && neededContents[message.media._]) { - foundMsgs.push(message.id); + foundMsgs.push(message.mid); if (foundMsgs.length >= neededLimit) { break; } @@ -1688,7 +1831,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) foundMsgs = []; angular.forEach(searchResult.messages, function (message) { - foundMsgs.push(message.id); + foundMsgs.push(message.mid); }); if (useSearchCache) { @@ -1753,6 +1896,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) function readHistory (inputPeer) { // console.trace('start read'); var peerID = AppPeersManager.getPeerID(inputPeer), + isChannel = AppPeersManager.isChannel(peerID), historyStorage = historiesStorage[peerID], foundDialog = getDialogByPeerID(peerID); @@ -1783,13 +1927,23 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) return historyStorage.readPromise; } - historyStorage.readPromise = MtpApiManager.invokeApi('messages.readHistory', { - peer: inputPeer, - offset: 0, - max_id: 0 - }).then(function (affectedHistory) { - return processAffectedHistory(inputPeer, affectedHistory, 'messages.readHistory'); - }).then(function () { + var apiPromise; + if (isChannel) { + apiPromise = MtpApiManager.invokeApi('channels.readHistory', { + channel: AppChatsManager.getChannelInput(-peerID), + max_id: 0 + }); + } else { + apiPromise = MtpApiManager.invokeApi('messages.readHistory', { + peer: inputPeer, + offset: 0, + max_id: 0 + }).then(function (affectedHistory) { + return processAffectedHistory(inputPeer, affectedHistory, 'messages.readHistory'); + }); + } + + historyStorage.readPromise = apiPromise.then(function () { if (foundDialog[0]) { // console.log('done read history', peerID); foundDialog[0].unread_count = 0; @@ -1861,17 +2015,25 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) function saveMessages (apiMessages) { angular.forEach(apiMessages, function (apiMessage) { + if (apiMessage._ == 'messageEmpty') { + return; + } apiMessage.unread = apiMessage.flags & 1 ? true : false; apiMessage.out = apiMessage.flags & 2 ? true : false; apiMessage.media_unread = apiMessage.flags & 32 ? true : false; var toPeerID = AppPeersManager.getPeerID(apiMessage.to_id); var isChannel = apiMessage.to_id._ == 'peerChannel'; - var mid = isChannel ? toPeerID + '_' + apiMessage.id : apiMessage.id; - apiMessage.mid = mid; + var channelID = isChannel ? -toPeerID : 0; + var mid = getFullMessageID(apiMessage.id, channelID); + apiMessage.mid = mid; messagesStorage[mid] = apiMessage; + if (apiMessage.reply_to_msg_id) { + apiMessage.reply_to_mid = getFullMessageID(apiMessage.reply_to_msg_id, channelID); + } + apiMessage.date -= serverTimeOffset; apiMessage.toID = toPeerID; apiMessage.fromID = apiMessage.from_id || toPeerID; @@ -1902,7 +2064,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) AppAudioManager.saveAudio(apiMessage.media.audio); break; case 'messageMediaWebPage': - AppWebPagesManager.saveWebPage(apiMessage.media.webpage, apiMessage.id, mediaContext); + AppWebPagesManager.saveWebPage(apiMessage.media.webpage, apiMessage.mid, mediaContext); break; } } @@ -1913,8 +2075,13 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) apiMessage.action._ = 'messageActionChannelEditPhoto'; } } - if (apiMessage.action._ == 'messageActionChatEditTitle' && isChannel) { - apiMessage.action._ = 'messageActionChannelEditTitle'; + else if (isChannel) { + if (apiMessage.action._ == 'messageActionChatEditTitle') { + apiMessage.action._ = 'messageActionChannelEditTitle'; + } + if (apiMessage.action._ == 'messageActionChatDeletePhoto') { + apiMessage.action._ = 'messageActionChannelDeletePhoto'; + } } } if (apiMessage.reply_markup) { @@ -2013,7 +2180,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) peer: inputPeer, message: text, random_id: randomID, - reply_to_msg_id: replyToMsgID, + reply_to_msg_id: getMessageLocalID(replyToMsgID), entities: entities }, sentRequestOptions).then(function (updates) { if (updates._ == 'updateShortSentMessage') { @@ -2187,7 +2354,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) peer: inputPeer, media: inputMedia, random_id: randomID, - reply_to_msg_id: replyToMsgID + reply_to_msg_id: getMessageLocalID(replyToMsgID) }).then(function (updates) { ApiUpdatesManager.processUpdateMessage(updates); }, function (error) { @@ -2319,7 +2486,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) peer: inputPeer, media: inputMedia, random_id: randomID, - reply_to_msg_id: replyToMsgID + reply_to_msg_id: getMessageLocalID(replyToMsgID) }).then(function (updates) { ApiUpdatesManager.processUpdateMessage(updates); }, function (error) { @@ -2337,20 +2504,26 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) pendingByRandomID[randomIDS] = [peerID, messageID]; } - function forwardMessages (peerID, msgIDs) { - msgIDs = msgIDs.sort(); + function forwardMessages (peerID, mids) { + mids = mids.sort(); + var flags = 0; + var msgIDs = []; var randomIDs = []; - var i; - var len = msgIDs.length; - for (var i = 0; i < msgIDs.length; i++) { + var len = mids.length; + var i, mid, msgID; + var fromChannel = getMessageIDInfo(mids[0])[1]; + for (i = 0; i < len; i++) { + msgIDs.push(getMessageLocalID(mids[i])); randomIDs.push([nextRandomInt(0xFFFFFFFF), nextRandomInt(0xFFFFFFFF)]); } return MtpApiManager.invokeApi('messages.forwardMessages', { - peer: AppPeersManager.getInputPeerByID(peerID), + flags: flags, + from_peer: AppPeersManager.getInputPeerByID(-fromChannel), id: msgIDs, - random_id: randomIDs + random_id: randomIDs, + to_peer: AppPeersManager.getInputPeerByID(peerID), }).then(function (updates) { ApiUpdatesManager.processUpdateMessage(updates); }); @@ -2446,7 +2619,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) } if (historyMessage = messagesForHistory[tempID]) { - messagesForHistory[finalMessage.id] = angular.extend(historyMessage, wrapForHistory(finalMessage.id)); + messagesForHistory[finalMessage.mid] = angular.extend(historyMessage, wrapForHistory(finalMessage.mid)); delete historyMessage.pending; delete historyMessage.error; delete historyMessage.random_id; @@ -2654,12 +2827,12 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) } } - var replyToMsgID = message.reply_to_msg_id; + var replyToMsgID = message.reply_to_mid; if (replyToMsgID) { - if (messagesStorage[replyToMsgID] && false) { + if (messagesStorage[replyToMsgID]) { message.reply_to_msg = wrapForDialog(replyToMsgID); } else { - message.reply_to_msg = {id: replyToMsgID, loading: true}; + message.reply_to_msg = {mid: replyToMsgID, loading: true}; if (needSingleMessages.indexOf(replyToMsgID) == -1) { needSingleMessages.push(replyToMsgID); if (fetchSingleMessagesTimeout === false) { @@ -2705,16 +2878,43 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) if (!needSingleMessages.length) { return; } - var msgIDs = needSingleMessages.slice(); + var mids = needSingleMessages.slice(); needSingleMessages = []; - MtpApiManager.invokeApi('messages.getMessages', { - id: msgIDs - }).then(function (getMessagesResult) { - AppUsersManager.saveApiUsers(getMessagesResult.users); - AppChatsManager.saveApiChats(getMessagesResult.chats); - saveMessages(getMessagesResult.messages); - - $rootScope.$broadcast('messages_downloaded', msgIDs); + + var msgIDsByChannels = {}; + var midsByChannels = {}; + var i, mid, msgChannel, channelID; + for (i = 0; i < mids.length; i++) { + mid = mids[i]; + msgChannel = getMessageIDInfo(mid); + channelID = msgChannel[1]; + if (msgIDsByChannels[channelID] === undefined) { + msgIDsByChannels[channelID] = []; + midsByChannels[channelID] = []; + } + msgIDsByChannels[channelID].push(msgChannel[0]); + midsByChannels[channelID].push(mid); + } + angular.forEach(msgIDsByChannels, function (msgIDs, channelID) { + var promise; + if (channelID > 0) { + promise = MtpApiManager.invokeApi('channels.getMessages', { + channel: AppChatsManager.getChannelInput(channelID), + id: msgIDs + }); + } else { + promise = MtpApiManager.invokeApi('messages.getMessages', { + id: msgIDs + }); + } + + promise.then(function (getMessagesResult) { + AppUsersManager.saveApiUsers(getMessagesResult.users); + AppChatsManager.saveApiChats(getMessagesResult.chats); + saveMessages(getMessagesResult.messages); + + $rootScope.$broadcast('messages_downloaded', midsByChannels[channelID]); + }) }) } @@ -2757,20 +2957,20 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) if (curMessage.fwdFromID && curMessage.media && curMessage.media.document && - curMessage.media.document.sticker && - (curMessage.from_id != (prevMessage || {}).from_id || !(prevMessage || {}).fwdFromID)) { + (curMessage.media.document.sticker || curMessage.media.document.audioTitle) && + (curMessage.fromID != (prevMessage || {}).fromID || !(prevMessage || {}).fwdFromID)) { delete curMessage.fwdFromID; curMessage._ = 'message'; } if (prevMessage && - curMessage.from_id == prevMessage.from_id && + curMessage.fromID == prevMessage.fromID && !prevMessage.fwdFromID == !curMessage.fwdFromID && !prevMessage.action && !curMessage.action && curMessage.date < prevMessage.date + 900) { - var singleLine = curMessage.message && curMessage.message.length < 70 && curMessage.message.indexOf("\n") == -1 && !curMessage.reply_to_msg_id; + var singleLine = curMessage.message && curMessage.message.length < 70 && curMessage.message.indexOf("\n") == -1 && !curMessage.reply_to_mid; if (groupFwd && curMessage.fwdFromID && curMessage.fwdFromID == prevMessage.fwdFromID) { curMessage.grouped = singleLine ? 'im_grouped_fwd_short' : 'im_grouped_fwd'; } else { @@ -2922,13 +3122,13 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) notification.onclick = function () { $rootScope.$broadcast('history_focus', { peerString: peerString, - messageID: message.flags & 16 ? message.id : 0, + messageID: message.flags & 16 ? message.mid : 0, }); }; notification.message = notificationMessage; notification.image = notificationPhoto.placeholder; - notification.key = 'msg' + message.id; + notification.key = 'msg' + message.mid; notification.tag = peerString; if (notificationPhoto.location && !notificationPhoto.location.empty) { @@ -3025,14 +3225,16 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) peerID = getMessagePeer(message), historyStorage = historiesStorage[peerID]; + saveMessages([message]); + if (historyStorage !== undefined) { var history = historyStorage.history; - if (history.indexOf(message.id) != -1) { + if (history.indexOf(message.mid) != -1) { return false; } var topMsgID = history[0]; - history.unshift(message.id); - if (message.id > 0 && message.id < topMsgID) { + history.unshift(message.mid); + if (message.mid > 0 && message.mid < topMsgID) { history.sort(function (a, b) { return b - a; }); @@ -3043,12 +3245,11 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) } else { historyStorage = historiesStorage[peerID] = { count: null, - history: [message.id], + history: [message.mid], pending: [] }; } - saveMessages([message]); if (mergeReplyKeyboard(historyStorage, message)) { $rootScope.$broadcast('history_reply_markup', {peerID: peerID}) @@ -3058,21 +3259,21 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) AppUsersManager.forceUserOnline(message.from_id); } - var randomID = pendingByMessageID[message.id], + var randomID = pendingByMessageID[message.mid], pendingMessage; if (randomID) { if (pendingMessage = finalizePendingMessage(randomID, message)) { $rootScope.$broadcast('history_update', {peerID: peerID}); } - delete pendingByMessageID[message.id]; + delete pendingByMessageID[message.mid]; } if (!pendingMessage) { if (newMessagesToHandle[peerID] === undefined) { newMessagesToHandle[peerID] = []; } - newMessagesToHandle[peerID].push(message.id); + newMessagesToHandle[peerID].push(message.mid); if (!newMessagesHandlePromise) { newMessagesHandlePromise = $timeout(handleNewMessages, 0); } @@ -3088,7 +3289,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) dialogsStorage.dialogs.splice(foundDialog[1], 1); dialogsStorage.dialogs.unshift(dialog); } - dialog.top_message = message.id; + dialog.top_message = message.mid; if (inboxUnread) { dialog.unread_count++; } @@ -3098,7 +3299,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) dialog = { peerID: peerID, unread_count: inboxUnread ? 1 : 0, - top_message: message.id + top_message: message.mid }; dialogsStorage.dialogs.unshift(dialog); } @@ -4586,7 +4787,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) message: updateMessage.message, fwd_from_id: updateMessage.fwd_from_id, fwd_date: updateMessage.fwd_date, - reply_to_msg_id: updateMessage.reply_to_msg_id, + reply_to_msg_id: updateMessage.reply_to_msg_id }, pts: updateMessage.pts, pts_count: updateMessage.pts_count diff --git a/app/less/app.less b/app/less/app.less index cabee4d3..541db142 100644 --- a/app/less/app.less +++ b/app/less/app.less @@ -3652,7 +3652,7 @@ a.countries_modal_search_clear { height: 18px; margin: 17px 0 0 16px; - .image-2x('../img/icons/ProfileIcons.png', 40px, 360px); + .image-2x('../img/icons/ProfileIcons.png', 40px, 420px); background-position: -10px -164px; } @@ -3667,7 +3667,7 @@ a.countries_modal_search_clear { height: 22px; margin: 17px 0 0 13px; - .image-2x('../img/icons/ProfileIcons.png', 40px, 360px); + .image-2x('../img/icons/ProfileIcons.png', 40px, 420px); background-position: -7px -280px; } @@ -3682,7 +3682,7 @@ a.countries_modal_search_clear { height: 20px; margin: 18px 0 0 16px; - .image-2x('../img/icons/ProfileIcons.png', 40px, 360px); + .image-2x('../img/icons/ProfileIcons.png', 40px, 420px); background-position: -10px -220px; } @@ -3728,7 +3728,7 @@ a.countries_modal_search_clear { margin-top: 5px; position: absolute; - .image-2x('../img/icons/ProfileIcons.png', 40px, 360px); + .image-2x('../img/icons/ProfileIcons.png', 40px, 420px); background-position: 0 0; .md_modal_iconed_section_toggle & { @@ -3748,6 +3748,13 @@ a.countries_modal_search_clear { margin-top: 3px; } + &_about { + width: 20px; + height: 20px; + background-position: -10px -344px; + margin-top: 0px; + } + &_notification { width: 17px; height: 20px; diff --git a/app/partials/desktop/channel_edit_modal.html b/app/partials/desktop/channel_edit_modal.html new file mode 100644 index 00000000..ab872653 --- /dev/null +++ b/app/partials/desktop/channel_edit_modal.html @@ -0,0 +1,28 @@ +
+ +
+ + + +
+ + + +
\ No newline at end of file diff --git a/app/partials/desktop/channel_modal.html b/app/partials/desktop/channel_modal.html new file mode 100644 index 00000000..be18859d --- /dev/null +++ b/app/partials/desktop/channel_modal.html @@ -0,0 +1,136 @@ +
+ +
+
+
+ + +
+
+
+ +
+
+
+ +
+ +
+
+
+
+ + +
+
+
+
+ +
+
+
+ + +
+
+
+ +
+ +
+ +
+ + +
+
+ +
+
+
+ +
+
+ +
+
+
+ +
+
+ + + + +
+
+
+
+ + + +
+ + + + + + + +
+ + + +
+ +
+ +
+ +
+ + +
+ +
+ + + + + + +
+ +
+
+
+ +
+ +
+ +
+
+ + +
diff --git a/app/partials/desktop/chat_invite_link_modal.html b/app/partials/desktop/chat_invite_link_modal.html index 5d45f8ca..3d70634a 100644 --- a/app/partials/desktop/chat_invite_link_modal.html +++ b/app/partials/desktop/chat_invite_link_modal.html @@ -6,18 +6,32 @@

-
- - +
+
+
+ + +
+
+ +
+
+ + +
+
+
- \ No newline at end of file diff --git a/app/partials/desktop/confirm_modal.html b/app/partials/desktop/confirm_modal.html index 2d846f9d..a94e02fa 100644 --- a/app/partials/desktop/confirm_modal.html +++ b/app/partials/desktop/confirm_modal.html @@ -49,6 +49,8 @@
+
+
diff --git a/app/partials/desktop/dialog.html b/app/partials/desktop/dialog.html index 57bb516e..f7842de5 100644 --- a/app/partials/desktop/dialog.html +++ b/app/partials/desktop/dialog.html @@ -1,4 +1,4 @@ - +