diff --git a/app/css/app.css b/app/css/app.css index 29a314b9..fc4674cb 100644 --- a/app/css/app.css +++ b/app/css/app.css @@ -41,6 +41,7 @@ a:hover { cursor: pointer; } .form-control { + color: #000; border: 1px solid #d9dbde; border-radius: 2px; -webkit-box-shadow: none; @@ -405,7 +406,7 @@ fieldset[disabled] .btn-tg.active { background-size: 42px 430px; border: 1px solid #F2F2F2; border-radius: 3px; - padding: 6px 6px 6px 30px; + padding: 6px 20px 6px 30px; margin-bottom: 0; margin: 0; } @@ -631,14 +632,14 @@ a.im_dialog:hover .im_dialog_date { margin-left: 5px; } .im_history_panel_info_link { - color: #999; + color: #3a6d99; font-size: 13px; font-weight: normal; padding-top: 5px; line-height: 1; } .im_history_panel_info_link:hover { - text-decoration: none; + text-decoration: underline; } @@ -1067,10 +1068,10 @@ span.emoji { .im_panel_fixed_bottom .im_send_form_wrap1 { position: relative; } -.im_panel_fixed_bottom .im_send_form_wrap { - width: 514px; +.im_panel_fixed_bottom .im_send_form { position: absolute; bottom: 0; + left: 62px; } .im_send_form { width: 389px; @@ -1280,7 +1281,7 @@ img.img_fullsize { max-width: 506px; } .user_modal_wrap .modal-body { - padding: 23px 25px 70px; + padding: 23px 25px 30px; } .user_modal_image_wrap { width: 120px; @@ -1291,16 +1292,32 @@ img.img_fullsize { height: 120px; } .user_modal_header { + font-weight: bold; margin: 0 0 5px; } .user_modal_status { color: #999; } .user_modal_send_btn { - padding-left: 0; - padding-right: 0; + border: 0; + background: #4E9CD8; font-size: 12px; margin-top: 8px; + padding-left: 16px; + padding-right: 16px; +} +.user_modal_send_btn:hover { + background: #539BD1; +} + +.user_modal_settings_wrap { + margin-top: 25px; +} +.user_modal_notifications { + font-weight: bold; +} +.user_modal_clear { + margin-top: 20px; } @@ -1324,6 +1341,20 @@ img.img_fullsize { .chat_modal_members_count { color: #999; } + +.chat_modal_settings_wrap { + margin-top: 10px; +} +.chat_modal_notifications { + font-weight: bold; +} +.chat_modal_clear { + margin-top: 10px; +} +.chat_modal_leave { + margin-top: 10px; +} + .chat_modal_invite_btn { padding-left: 0; padding-right: 0; @@ -1357,6 +1388,9 @@ img.img_fullsize { margin-right: 10px; overflow: hidden; } +.chat_modal_members_forbidden { + color: #999; +} @@ -1380,6 +1414,7 @@ img.img_fullsize { height: auto; max-height: 300px; overflow: auto; + line-height: 17px; border: 1px solid #d9dbde; border-radius: 2px; @@ -1525,12 +1560,22 @@ img.img_fullsize { } .settings_profile_first_name, .settings_profile_last_name { - width: 210px; + width: 180px; float: left; } .settings_profile_first_name { margin-right: 22px; } +.settings_profile_last_name { + margin-right: 10px; +} +.settings_profile_save { + padding-top: 23px; + float: left; +} +.settings_profile_save_btn { + width: 50px; +} .settings_profile_edit_form input { font-size: 12px; diff --git a/app/index.html b/app/index.html index 7bbf0d58..00c04702 100644 --- a/app/index.html +++ b/app/index.html @@ -7,7 +7,7 @@ - + @@ -48,14 +48,14 @@ - + - - + + - + diff --git a/app/js/controllers.js b/app/js/controllers.js index 45f6f280..5ab48fcc 100644 --- a/app/js/controllers.js +++ b/app/js/controllers.js @@ -162,7 +162,7 @@ angular.module('myApp.controllers', []) $scope.isLoggedIn = true; $scope.openSettings = function () { $modal.open({ - templateUrl: 'partials/settings_modal.html?2', + templateUrl: 'partials/settings_modal.html?3', controller: 'SettingsModalController', scope: $rootScope.$new(), windowClass: 'settings_modal_window' @@ -226,6 +226,15 @@ angular.module('myApp.controllers', []) $scope.dialogs.unshift(wrappedDialog); }); + $scope.$on('dialog_flush', function (e, dialog) { + for (var i = 0; i < $scope.dialogs.length; i++) { + if ($scope.dialogs[i].peerID == dialog.peerID) { + $scope.dialogs.splice(i, 1); + break; + } + } + }); + $scope.$watch('search.query', loadDialogs); function loadDialogs () { @@ -421,6 +430,12 @@ angular.module('myApp.controllers', []) } }); + $scope.$on('dialog_flush', function (e, dialog) { + if (dialog.peerID == $scope.curDialog.peerID) { + $scope.history = []; + } + }); + $scope.$on('apiUpdate', function (e, update) { // console.log('on apiUpdate inline', update); switch (update._) { @@ -571,14 +586,42 @@ angular.module('myApp.controllers', []) $scope.video = AppVideoManager.wrapForFull($scope.videoID); }) - .controller('UserModalController', function ($scope, $location, $rootScope, $modalStack, AppUsersManager) { + .controller('UserModalController', function ($scope, $location, $rootScope, $modalStack, AppUsersManager, NotificationsManager, AppMessagesManager, AppPeersManager) { $scope.user = AppUsersManager.wrapForFull($scope.userID); + + $scope.settings = {notifications: true}; + + NotificationsManager.getPeerMuted($scope.userID).then(function (muted) { + $scope.settings.notifications = !muted; + }); + + $scope.$watch('settings.notifications', function(newValue) { + NotificationsManager.getPeerSettings($scope.userID).then(function (settings) { + if (newValue) { + settings.mute_until = 0; + } else { + settings.mute_until = 2000000000; + } + NotificationsManager.savePeerSettings($scope.userID, settings); + }); + }); + $scope.goToHistory = function () { $rootScope.$broadcast('history_focus', {peerString: $scope.user.peerString}); }; + + $scope.flushHistory = function () { + if (confirm('Are you sure? This can not be undone!') !== true) { + return false; + } + AppMessagesManager.flushHistory(AppPeersManager.getInputPeerByID($scope.userID)).then(function () { + $scope.goToHistory(); + }); + }; }) - .controller('ChatModalController', function ($scope, $timeout, AppUsersManager, AppChatsManager, MtpApiManager) { + .controller('ChatModalController', function ($scope, $timeout, $rootScope, AppUsersManager, AppChatsManager, MtpApiManager, NotificationsManager, AppMessagesManager, AppPeersManager, ApiUpdatesManager) { + $scope.chatFull = AppChatsManager.wrapForFull($scope.chatID, {}); MtpApiManager.invokeApi('messages.getFullChat', { @@ -589,6 +632,70 @@ angular.module('myApp.controllers', []) $scope.chatFull = AppChatsManager.wrapForFull($scope.chatID, result.full_chat); }); + + $scope.settings = {notifications: true}; + + NotificationsManager.getPeerMuted(-$scope.chatID).then(function (muted) { + $scope.settings.notifications = !muted; + }); + + $scope.$watch('settings.notifications', function(newValue) { + NotificationsManager.getPeerSettings(-$scope.chatID).then(function (settings) { + if (newValue) { + settings.mute_until = 0; + } else { + settings.mute_until = 2000000000; + } + NotificationsManager.savePeerSettings(-$scope.chatID, settings); + }); + }); + + $scope.leaveGroup = function () { + MtpApiManager.invokeApi('messages.deleteChatUser', { + chat_id: $scope.chatID, + user_id: {_: 'inputUserSelf'} + }).then(function (result) { + if (ApiUpdatesManager.saveSeq(result.seq)) { + ApiUpdatesManager.saveUpdate({ + _: 'updateNewMessage', + message: result.message, + pts: result.pts + }); + } + + $rootScope.$broadcast('history_focus', {peerString: $scope.chatFull.peerString}); + }); + }; + + $scope.returnToGroup = function () { + MtpApiManager.invokeApi('messages.addChatUser', { + chat_id: $scope.chatID, + user_id: {_: 'inputUserSelf'} + }).then(function (result) { + if (ApiUpdatesManager.saveSeq(result.seq)) { + ApiUpdatesManager.saveUpdate({ + _: 'updateNewMessage', + message: result.message, + pts: result.pts + }); + } + + $rootScope.$broadcast('history_focus', {peerString: $scope.chatFull.peerString}); + }); + }; + + + + $scope.flushHistory = function () { + if (confirm('Are you sure? This can not be undone!') !== true) { + return; + } + AppMessagesManager.flushHistory(AppPeersManager.getInputPeerByID(-$scope.chatID)).then(function () { + $rootScope.$broadcast('history_focus', {peerString: $scope.chatFull.peerString}); + }); + + }; + }) .controller('SettingsModalController', function ($rootScope, $scope, $timeout, AppUsersManager, AppChatsManager, MtpApiManager, AppConfigManager, NotificationsManager) { @@ -639,12 +746,14 @@ angular.module('myApp.controllers', []) }); $scope.error = {}; - $scope.save = function () { + $scope.save = function (profileForm) { MtpApiManager.invokeApi('account.updateProfile', { first_name: $scope.profile.first_name || '', last_name: $scope.profile.last_name || '' }).then(function (user) { $scope.error = {}; + // console.log($scope.profileForm); + profileForm.$setPristine(); AppUsersManager.saveApiUser(user); }, function (error) { switch (error.type) { diff --git a/app/js/directives.js b/app/js/directives.js index e35fee9b..c9c50fed 100644 --- a/app/js/directives.js +++ b/app/js/directives.js @@ -421,7 +421,7 @@ angular.module('myApp.directives', ['myApp.filters']) if (e.type == 'drop') { scope.$apply(function () { scope.draftMessage.files = Array.prototype.slice.call(e.originalEvent.dataTransfer.files); - scope.draftMessage.isMedia = false; + scope.draftMessage.isMedia = true; }); } dragTimeout = setTimeout(function () { @@ -631,7 +631,7 @@ angular.module('myApp.directives', ['myApp.filters']) // } // } - MtpApiFileManager.downloadFile(scope.video.dc_id, inputLocation, scope.video.size).then(function (url) { + MtpApiFileManager.downloadFile(scope.video.dc_id, inputLocation, scope.video.size, null, {mime: 'video/mp4'}).then(function (url) { scope.progress.enabled = false; // scope.progress = {enabled: true, percent: 50}; scope.player.quicktime = hasQt; diff --git a/app/js/lib/mtproto.js b/app/js/lib/mtproto.js index 696d45c8..aba8fe16 100644 --- a/app/js/lib/mtproto.js +++ b/app/js/lib/mtproto.js @@ -2586,7 +2586,9 @@ factory('MtpApiFileManager', function (MtpApiManager, $q, $window) { return cachedDownloadPromises[fileName] = deferred.promise; } - function downloadFile (dcID, location, size, fileEntry) { + function downloadFile (dcID, location, size, fileEntry, options) { + options = options || {}; + console.log('dload file', dcID, location, size); var fileName = getFileName(location), cachedPromise = cachedSavePromises[fileName] || cachedDownloadPromises[fileName]; @@ -2638,7 +2640,7 @@ factory('MtpApiFileManager', function (MtpApiManager, $q, $window) { }, errorHandler).then(function () { if (isFinal) { - deferred.resolve(cachedDownloads[fileName] = fileEntry.toURL('image/jpeg')); + deferred.resolve(cachedDownloads[fileName] = fileEntry.toURL(options.mime || 'image/jpeg')); } else { // console.log('notify', {done: offset + limit, total: size}); deferred.notify({done: offset + limit, total: size}); @@ -2699,14 +2701,14 @@ factory('MtpApiFileManager', function (MtpApiManager, $q, $window) { if (isFinal) { try { - var blob = new Blob(blobParts, {type: 'image/jpeg'}); + var blob = new Blob(blobParts, {type: options.mime || 'image/jpeg'}); } catch (e) { window.BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder; var bb = new BlobBuilder; angular.forEach(blobParts, function(blobPart) { bb.append(blobPart); }); - var blob = bb.getBlob('image/jpeg'); + var blob = bb.getBlob(options.mime || 'image/jpeg'); } diff --git a/app/js/services.js b/app/js/services.js index 4777114a..6337588d 100644 --- a/app/js/services.js +++ b/app/js/services.js @@ -208,7 +208,7 @@ angular.module('myApp.services', []) scope.userID = userID; var modalInstance = $modal.open({ - templateUrl: 'partials/user_modal.html?1', + templateUrl: 'partials/user_modal.html?2', controller: 'UserModalController', scope: scope, windowClass: 'user_modal_window', @@ -330,7 +330,7 @@ angular.module('myApp.services', []) scope.chatID = chatID; var modalInstance = $modal.open({ - templateUrl: 'partials/chat_modal.html?3', + templateUrl: 'partials/chat_modal.html?4', controller: 'ChatModalController', windowClass: 'chat_modal_window', scope: scope @@ -688,7 +688,7 @@ angular.module('myApp.services', []) return deferred.promise; } - function processAffectedHistory (inputPeer, affectedHistory) { + function processAffectedHistory (inputPeer, affectedHistory, method) { if (!ApiUpdatesManager.saveSeq(affectedHistory.seq)) { return false; } @@ -696,12 +696,12 @@ angular.module('myApp.services', []) return $q.when(); } - return MtpApiManager.invokeApi('messages.readHistory', { + return MtpApiManager.invokeApi(method, { peer: inputPeer, offset: affectedHistory.offset, max_id: 0 }).then(function (affectedHistory) { - return processAffectedHistory(inputPeer, affectedHistory); + return processAffectedHistory(inputPeer, affectedHistory, method); }); } @@ -740,7 +740,7 @@ angular.module('myApp.services', []) offset: 0, max_id: 0 }).then(function (affectedHistory) { - return processAffectedHistory(inputPeer, affectedHistory); + return processAffectedHistory(inputPeer, affectedHistory, 'messages.readHistory'); }).then(function () { if (foundDialog[0]) { foundDialog[0].unread_count = 0; @@ -767,6 +767,26 @@ angular.module('myApp.services', []) return promise; } + function flushHistory (inputPeer) { + // console.log('start read'); + var peerID = AppPeersManager.getPeerID(inputPeer), + historyStorage = historiesStorage[peerID]; + + return MtpApiManager.invokeApi('messages.deleteHistory', { + peer: inputPeer, + offset: 0 + }).then(function (affectedHistory) { + return processAffectedHistory(inputPeer, affectedHistory, 'messages.deleteHistory'); + }).then(function () { + var foundDialog = getDialogByPeerID(peerID); + if (foundDialog[0]) { + dialogsStorage.dialogs.splice(foundDialog[1], 1); + } + delete historiesStorage[peerID]; + $rootScope.$broadcast('dialog_flush', {peerID: peerID}); + }); + } + function saveMessages (apiMessages) { angular.forEach(apiMessages, function (apiMessage) { messagesStorage[apiMessage.id] = apiMessage; @@ -1195,7 +1215,7 @@ angular.module('myApp.services', []) ' @ ' + (AppChatsManager.getChat(-peerID).title || 'Unknown chat'); - notificationPhoto = AppChatsManager.getChatPhoto(-peerID, 'Chat'); + notificationPhoto = AppChatsManager.getChatPhoto(-peerID, 'Group'); peerString = AppChatsManager.getChatString(-peerID); } @@ -1286,7 +1306,7 @@ angular.module('myApp.services', []) if ($rootScope.idle.isIDLE && !message.out && message.unread) { - NotificationsManager.getPeerSettings(peerID).then(function (muted) { + NotificationsManager.getPeerMuted(peerID).then(function (muted) { if (!message.unread || muted) { return; } @@ -1326,6 +1346,61 @@ angular.module('myApp.services', []) $rootScope.$broadcast('dialog_unread', {peerID: peerID, count: count}); }); break; + + case 'updateDeleteMessages': + var dialogsUpdated = {}, + historiesUpdated = {}, + messageID, message, i, peerID, foundDialog, dialog, history; + + for (i = 0; i < update.messages.length; i++) { + messageID = update.messages[i]; + message = messagesStorage[messageID]; + if (message) { + peerID = getMessagePeer(message); + history = historiesUpdated[peer] || (historiesUpdated[peer] = {count: 0, unread: 0, msgs: {}}); + + if (!message.out && message.unread) { + history.unread++; + NotificationsManager.cancel('msg' + messageID); + } + history.count++; + history.msgs[messageID] = true; + + if (messagesForHistory[messageID]) { + messagesForHistory[messageID].DELETED = true; + delete messagesForHistory[messageID]; + } + if (messagesForDialogs[messageID]) { + messagesForDialogs[messageID].DELETED = true; + delete messagesForDialogs[messageID]; + } + message.DELETED = true; + delete messagesStorage[messageID]; + } + } + + angular.forEach(historiesUpdated, function (updatedData, peerID) { + var foundDialog = getDialogByPeerID(peerID); + if (foundDialog) { + if (updatedData.unread) { + foundDialog[0].unread_count -= updatedData.unread; + + $rootScope.$broadcast('dialog_unread', {peerID: peerID, count: foundDialog[0].unread_count}); + } + } + + var historyStorage = historiesStorage[peerID]; + if (historyStorage !== undefined) { + var newHistory = []; + for (var i = 0; i < historyStorage.history.length; i++) { + if (!updatedData.msgs[historyStorage.history[i]]) { + newHistory.push(historyStorage.history[i]); + } + } + historyStorage.history = newHistory; + } + }); + break; } }); @@ -1333,6 +1408,7 @@ angular.module('myApp.services', []) getDialogs: getDialogs, getHistory: getHistory, readHistory: readHistory, + flushHistory: flushHistory, saveMessages: saveMessages, sendText: sendText, sendFile: sendFile, @@ -1516,15 +1592,20 @@ angular.module('myApp.services', []) height: fullHeight, }; - if (video.w > video.h) { + if (!video.w || !video.h) { + full.height = full.width = Math.min(fullWidth, fullHeight); + } + else if (video.w > video.h) { full.height = parseInt(video.h * fullWidth / video.w); - } else { + } + else { full.width = parseInt(video.w * fullHeight / video.h); if (full.width > fullWidth) { full.height = parseInt(full.height * fullWidth / full.width); full.width = fullWidth; } } + // console.log(222, video.w, video.h, full.width, full.height); video.full = full; video.fromUser = AppUsersManager.getUser(video.user_id); @@ -1625,17 +1706,17 @@ angular.module('myApp.services', []) if (window.chrome && chrome.fileSystem && chrome.fileSystem.chooseEntry) { - var ext = (doc.file_name.split('.', 2) || [])[1] || '', - mime = doc.mime_type; + var ext = (doc.file_name.split('.', 2) || [])[1] || ''; + chrome.fileSystem.chooseEntry({ type: 'saveFile', suggestedName: doc.file_name, accepts: [{ - mimeTypes: [mime], + mimeTypes: [doc.mime_type], extensions: [ext] }] }, function (writableFileEntry) { - MtpApiFileManager.downloadFile(doc.dc_id, inputFileLocation, doc.size, writableFileEntry).then(function (url) { + MtpApiFileManager.downloadFile(doc.dc_id, inputFileLocation, doc.size, writableFileEntry, {mime: doc.mime_type}).then(function (url) { delete historyDoc.progress; console.log('file save done'); }, function (e) { @@ -1644,7 +1725,7 @@ angular.module('myApp.services', []) }, updateDownloadProgress); }); } else { - MtpApiFileManager.downloadFile(doc.dc_id, inputFileLocation, doc.size).then(function (url) { + MtpApiFileManager.downloadFile(doc.dc_id, inputFileLocation, doc.size, null, {mime: doc.mime_type}).then(function (url) { delete historyDoc.progress; var a = $('Download').css({position: 'absolute', top: 1, left: 1}).attr('href', url).attr('target', '_blank').attr('download', doc.file_name).appendTo('body'); @@ -1705,7 +1786,7 @@ angular.module('myApp.services', []) $rootScope.$broadcast('history_update'); } - MtpApiFileManager.downloadFile(audio.dc_id, inputFileLocation, audio.size).then(function (url) { + MtpApiFileManager.downloadFile(audio.dc_id, inputFileLocation, audio.size, null, {mime: 'audio/mpeg'}).then(function (url) { delete historyAudio.progress; historyAudio.url = $sce.trustAsResourceUrl(url); historyAudio.autoplay = true; @@ -2172,7 +2253,7 @@ angular.module('myApp.services', []) }) -.service('NotificationsManager', function ($rootScope, $window, $timeout, $interval, MtpApiManager, AppPeersManager, IdleManager, AppConfigManager) { +.service('NotificationsManager', function ($rootScope, $window, $timeout, $interval, $q, MtpApiManager, AppPeersManager, IdleManager, AppConfigManager) { var notificationsUiSupport = 'Notification' in window; var notificationsShown = {}; @@ -2220,7 +2301,8 @@ angular.module('myApp.services', []) cancel: notificationCancel, clear: notificationsClear, getPeerSettings: getPeerSettings, - getPeerMuted: getPeerMuted + getPeerMuted: getPeerMuted, + savePeerSettings: savePeerSettings }; function getPeerSettings (peerID) { @@ -2236,6 +2318,21 @@ angular.module('myApp.services', []) }); } + function savePeerSettings (peerID, settings) { + var inputSettings = angular.copy(settings); + inputSettings._ = 'inputPeerNotifySettings'; + + peerSettings[peerID] = $q.when(settings); + + return MtpApiManager.invokeApi('account.updateNotifySettings', { + peer: { + _: 'inputNotifyPeer', + peer: AppPeersManager.getInputPeerByID(peerID) + }, + settings: inputSettings + }); + } + function getPeerMuted (peerID) { return getPeerSettings(peerID).then(function (peerNotifySettings) { return peerNotifySettings._ == 'peerNotifySettings' && diff --git a/app/partials/chat_modal.html b/app/partials/chat_modal.html index d8652f1d..e034bf81 100644 --- a/app/partials/chat_modal.html +++ b/app/partials/chat_modal.html @@ -22,7 +22,27 @@

- +
+
+ Notifications: + + {{settings.notifications ? 'ON' : 'OFF'}} + +
+ +
+ Clear History +
+ +
+ + +
+
Members
@@ -45,6 +65,10 @@ +
+ Group members list is unavailable. +
+ \ No newline at end of file diff --git a/app/partials/settings_modal.html b/app/partials/settings_modal.html index 67cb6ad6..e0476a1b 100644 --- a/app/partials/settings_modal.html +++ b/app/partials/settings_modal.html @@ -1,14 +1,12 @@