From 3c55bed2ce15321439b717c77e1b7782cb616d44 Mon Sep 17 00:00:00 2001 From: Igor Zhukov Date: Fri, 10 Jul 2015 19:36:24 +0300 Subject: [PATCH] Improved mobile UX --- app/js/controllers.js | 84 +++++++++++---- app/js/filters.js | 4 +- app/js/lib/utils.js | 3 +- app/js/locales/en-us.json | 6 ++ app/js/message_composer.js | 77 +++++++------ app/less/app.less | 81 +++++++++++++- app/less/desktop.less | 66 ------------ app/less/mobile.less | 102 +++++++++++++++--- app/partials/desktop/im.html | 2 +- app/partials/mobile/audio_player.html | 1 + app/partials/mobile/chat_modal.html | 8 +- app/partials/mobile/im.html | 45 +++++--- app/partials/mobile/message.html | 10 +- .../mobile/message_actions_modal.html | 11 ++ app/partials/mobile/sessions_list_modal.html | 11 +- app/partials/mobile/settings_modal.html | 2 +- app/partials/mobile/user_modal.html | 18 ++++ 17 files changed, 359 insertions(+), 172 deletions(-) create mode 100644 app/partials/mobile/message_actions_modal.html diff --git a/app/js/controllers.js b/app/js/controllers.js index c726dc7c..7d628c2f 100644 --- a/app/js/controllers.js +++ b/app/js/controllers.js @@ -971,7 +971,7 @@ angular.module('myApp.controllers', ['myApp.i18n']) }) - .controller('AppImHistoryController', function ($scope, $location, $timeout, $rootScope, MtpApiManager, AppUsersManager, AppChatsManager, AppMessagesManager, AppPeersManager, ApiUpdatesManager, PeersSelectService, IdleManager, StatusManager, ErrorService) { + .controller('AppImHistoryController', function ($scope, $location, $timeout, $modal, $rootScope, MtpApiManager, AppUsersManager, AppChatsManager, AppMessagesManager, AppPeersManager, ApiUpdatesManager, PeersSelectService, IdleManager, StatusManager, ErrorService) { $scope.$watchCollection('curDialog', applyDialogSelect); @@ -1411,6 +1411,13 @@ angular.module('myApp.controllers', ['myApp.i18n']) var target = $event.target; while (target) { if (target.className.indexOf('im_message_outer_wrap') != -1) { + if (Config.Mobile) { + return false; + } + break; + } + if (Config.Mobile && + target.className.indexOf('im_message_body') != -1) { break; } if (target.tagName == 'A' || @@ -1424,9 +1431,37 @@ angular.module('myApp.controllers', ['myApp.i18n']) } target = target.parentNode; } + + if (Config.Mobile) { + $modal.open({ + templateUrl: templateUrl('message_actions_modal'), + windowClass: 'message_actions_modal_window' + }).result.then(function (action) { + switch (action) { + case 'reply': + selectedReply(messageID); + break; + + case 'delete': + selectedDelete(messageID); + break; + + case 'forward': + selectedForward(messageID); + break; + + case 'select': + $scope.historyState.selectActions = true; + $scope.$broadcast('ui_panel_update'); + toggleMessage(messageID); + break; + } + }); + return false; + } } - var shiftClick = $event.shiftKey; + var shiftClick = $event && $event.shiftKey; if (shiftClick) { $scope.$broadcast('ui_selection_clear'); } @@ -1499,29 +1534,36 @@ angular.module('myApp.controllers', ['myApp.i18n']) }) }; - function selectedDelete () { - if ($scope.selectedCount > 0) { - var selectedMessageIDs = []; + function selectedDelete (selectedMessageID) { + var selectedMessageIDs = []; + if (selectedMessageID) { + selectedMessageIDs.push(selectedMessageID); + } + else if ($scope.selectedCount > 0) { angular.forEach($scope.selectedMsgs, function (t, messageID) { selectedMessageIDs.push(messageID); }); - if (selectedMessageIDs.length) { - ErrorService.confirm({type: 'MESSAGES_DELETE', count: selectedMessageIDs.length}).then(function () { - AppMessagesManager.deleteMessages(selectedMessageIDs).then(function () { - selectedCancel(); - }); + } + if (selectedMessageIDs.length) { + ErrorService.confirm({type: 'MESSAGES_DELETE', count: selectedMessageIDs.length}).then(function () { + AppMessagesManager.deleteMessages(selectedMessageIDs).then(function () { + selectedCancel(); }); - } + }); } } - function selectedForward () { - if ($scope.selectedCount > 0) { - var selectedMessageIDs = []; + function selectedForward (selectedMessageID) { + var selectedMessageIDs = []; + if (selectedMessageID) { + selectedMessageIDs.push(selectedMessageID); + } + else if ($scope.selectedCount > 0) { angular.forEach($scope.selectedMsgs, function (t, messageID) { selectedMessageIDs.push(messageID); }); - + } + if (selectedMessageIDs.length) { PeersSelectService.selectPeers({confirm_type: 'FORWARD_PEER'}).then(function (peerStrings) { angular.forEach(peerStrings, function (peerString) { var peerID = AppPeersManager.getPeerID(peerString); @@ -1533,16 +1575,16 @@ angular.module('myApp.controllers', ['myApp.i18n']) }); }); }); - } } - function selectedReply () { - if ($scope.selectedCount == 1) { - var selectedMessageID; + function selectedReply (selectedMessageID) { + if (!selectedMessageID && $scope.selectedCount == 1) { angular.forEach($scope.selectedMsgs, function (t, messageID) { selectedMessageID = messageID; }); + } + if (selectedMessageID) { selectedCancel(); $scope.$broadcast('reply_selected', selectedMessageID); } @@ -2051,12 +2093,12 @@ angular.module('myApp.controllers', ['myApp.i18n']) if (replyKeyboard) { replyKeyboard = AppMessagesManager.wrapReplyMarkup(replyKeyboard); } - // console.log('update reply markup', peerID, replyKeyboard); + console.log('update reply markup', peerID, replyKeyboard); $scope.historyState.replyKeyboard = replyKeyboard; var addReplyMessage = replyKeyboard && - !replyKeyboard.hidden && + !replyKeyboard.pFlags.hidden && (replyKeyboard._ == 'replyKeyboardForceReply' || (replyKeyboard._ == 'replyKeyboardMarkup' && peerID < 0)); diff --git a/app/js/filters.js b/app/js/filters.js index a76121a7..ca598361 100644 --- a/app/js/filters.js +++ b/app/js/filters.js @@ -16,7 +16,7 @@ angular.module('myApp.filters', ['myApp.i18n']) if (!user || !user.first_name && !user.last_name) { return _('user_name_deleted'); } - return user.first_name + ' ' + user.last_name; + return user.first_name + (user.last_name ? ' ' + user.last_name : ''); } }) @@ -34,7 +34,7 @@ angular.module('myApp.filters', ['myApp.i18n']) return function (user, botChatPrivacy) { var statusType = user && user.status && user.status._; if (!statusType) { - statusType = user.pFlags.bot ? 'userStatusBot' : 'userStatusEmpty'; + statusType = user && user.pFlags && user.pFlags.bot ? 'userStatusBot' : 'userStatusEmpty'; } switch (statusType) { case 'userStatusOnline': diff --git a/app/js/lib/utils.js b/app/js/lib/utils.js index 22cb9fa5..87c3b700 100644 --- a/app/js/lib/utils.js +++ b/app/js/lib/utils.js @@ -312,7 +312,8 @@ function templateUrl (tplName) { media_modal_layout: 'desktop', slider: 'desktop', reply_message: 'desktop', - chat_invite_link_modal: 'desktop' + chat_invite_link_modal: 'desktop', + reply_markup: 'desktop' }; var layout = forceLayout[tplName] || (Config.Mobile ? 'mobile' : 'desktop'); return 'partials/' + layout + '/' + tplName + '.html'; diff --git a/app/js/locales/en-us.json b/app/js/locales/en-us.json index 52250f93..443f7ef7 100644 --- a/app/js/locales/en-us.json +++ b/app/js/locales/en-us.json @@ -284,6 +284,12 @@ "message_service_unsupported_action": "unsupported action {action}", "message_service_bot_intro_header": "What can this bot do?", + "message_action_reply": "Reply", + "message_action_delete": "Delete", + "message_action_forward": "Forward", + "message_action_select": "Select", + "message_action_cancel": "Cancel", + "error_modal_warning_title": "Warning", "error_modal_bad_request_title": "Error", "error_modal_unauthorized_title": "Unauthorized", diff --git a/app/js/message_composer.js b/app/js/message_composer.js index 2a5dd80b..705b40f6 100644 --- a/app/js/message_composer.js +++ b/app/js/message_composer.js @@ -684,9 +684,6 @@ MessageComposer.prototype.restoreSelection = function () { MessageComposer.prototype.checkAutocomplete = function (forceFull) { - if (Config.Mobile) { - return false; - } var pos, value; if (this.richTextareaEl) { var textarea = this.richTextareaEl[0]; @@ -1091,7 +1088,7 @@ MessageComposer.prototype.showEmojiSuggestions = function (codes) { pos = spritesheet[1]; x = iconSize * spritesheet[3]; y = iconSize * spritesheet[2]; - html.push('
  • :' + encodeEntities(emoticonData[1][0]) + ':
  • '); + html.push('
  • :' + encodeEntities(emoticonData[1][0]) + ':
  • '); } } @@ -1144,12 +1141,13 @@ MessageComposer.prototype.showCommandsSuggestions = function (commands) { MessageComposer.prototype.updatePosition = function () { var offset = (this.richTextareaEl || this.textareaEl).offset(); - var width = (this.richTextareaEl || this.textareaEl).outerWidth(); var height = this.scroller.updateHeight(); + var width = $((this.richTextareaEl || this.textareaEl)[0].parentNode).outerWidth(); + console.log(width); this.autoCompleteWrapEl.css({ top: offset.top - height, - left: offset.left, - width: width - 2 + left: Config.Mobile ? 0 : offset.left, + width: Config.Mobile ? '100%' : width - 2 }); this.scroller.update(); } @@ -1171,30 +1169,39 @@ function Scroller(content, options) { var classPrefix = options.classPrefix || 'scroller'; this.content = $(content); - this.content.wrap('
    '); - - this.scrollable = $(this.content[0].parentNode); - this.scroller = $(this.scrollable[0].parentNode); - this.wrap = $(this.scroller[0].parentNode); - this.useNano = options.nano !== undefined ? options.nano : !Config.Mobile; this.maxHeight = options.maxHeight; this.minHeight = options.minHeight; if (this.useNano) { - this.scrollable.addClass('nano-content'); - this.scroller.addClass('nano'); - this.scroller.nanoScroller({preventPageScrolling: true, tabIndex: -1}); + this.setUpNano(); } else { - if (this.maxHeight) { - this.wrap.css({maxHeight: this.maxHeight}); - } - if (this.minHeight) { - this.wrap.css({minHeight: this.minHeight}); - } + this.setUpNative(); } this.updateHeight(); } +Scroller.prototype.setUpNano = function () { + this.content.wrap('
    '); + + this.scrollable = $(this.content[0].parentNode); + this.scroller = $(this.scrollable[0].parentNode); + this.wrap = $(this.scroller[0].parentNode); + + this.scroller.nanoScroller({preventPageScrolling: true, tabIndex: -1}); +} + +Scroller.prototype.setUpNative = function () { + this.content.wrap('
    '); + this.scrollable = $(this.content[0].parentNode); + + this.scrollable.css({overflow: 'auto'}); + if (this.maxHeight) { + this.scrollable.css({maxHeight: this.maxHeight}); + } + if (this.minHeight) { + this.scrollable.css({minHeight: this.minHeight}); + } +} Scroller.prototype.update = function () { if (this.useNano) { @@ -1213,20 +1220,22 @@ Scroller.prototype.reinit = function () { Scroller.prototype.updateHeight = function () { var height; - if (this.maxHeight || this.minHeight) { - height = this.content[0].offsetHeight; - if (this.maxHeight && height > this.maxHeight) { - height = this.maxHeight; - } - if (this.minHeight && height < this.minHeight) { - height = this.minHeight; - } - this.wrap.css({height: height}); - } else { - height = this.scroller[0].offsetHeight; - } if (this.useNano) { + if (this.maxHeight || this.minHeight) { + height = this.content[0].offsetHeight; + if (this.maxHeight && height > this.maxHeight) { + height = this.maxHeight; + } + if (this.minHeight && height < this.minHeight) { + height = this.minHeight; + } + this.wrap.css({height: height}); + } else { + height = this.scroller[0].offsetHeight; + } $(this.scroller).nanoScroller(); + } else { + height = this.scrollable[0].offsetHeight; } return height; } diff --git a/app/less/app.less b/app/less/app.less index 385df0fc..5d692d94 100644 --- a/app/less/app.less +++ b/app/less/app.less @@ -2176,6 +2176,9 @@ a.im_message_fwd_photo { color: #999; position: absolute; } +.im_send_field_wrap { + position: relative; +} textarea.im_message_field { font-size: 12px; margin-bottom: 10px; @@ -2198,6 +2201,56 @@ textarea.im_message_field { margin-left: 33px; } +.im_send_reply { + &_wrap { + margin-bottom: 5px; + } + + &_form_wrap { + a.im_panel_own_photo, + a.im_panel_peer_photo { + margin-top: 41px; + } + } + + &_cancel { + float: right; + display: block; + width: 18px; + height: 18px; + margin-right: 6px; + margin-top: 5px; + -webkit-transform: translate3d(0,0,0); + padding-top: 7px; + + .icon-reply-bar { + display: block; + background: #999; + width: 18px; + height: 2px; + transform-origin: 50% 50%; + } + + &:hover { + .icon-reply-bar { + background: #44a1e8; + } + } + } +} + +.icon-reply-bar { + &:first-child { + .transform(rotate(-45deg)); + + transform-origin: 50% 50%; + } + + &:last-child { + .transform(translate3d(0,-2px,0) rotate(45deg)); + } +} + a.img_fullsize, .img_fullsize_wrap { display: block; @@ -2391,6 +2444,7 @@ img.img_fullsize { height: 174px; position: relative; } + .composer_emoji_tooltip_content { padding-right: 8px; outline: 0!important; @@ -2484,8 +2538,6 @@ a.composer_emoji_btn { margin-top: -5px; margin-left: -1px; } -.composer_dropdown_scroller { -} .composer_dropdown { position: static; @@ -2607,9 +2659,13 @@ a.composer_command_option.composer_autocomplete_option_active .composer_command_ font-size: 13px; color: #444; margin: 10px 0 3px; -} -.composer_stickerset_title:hover { - color: #444; + + &:first-child { + margin-top: 0; + } + &:hover { + color: #444; + } } .composer_sticker_btn { width: 78px; @@ -3176,6 +3232,21 @@ a.contacts_modal_contact:hover .md_modal_list_peer_description, .im_message_fwd_date { color: #899daf; } + + .im_message_outer_wrap { + background-color: rgba(242, 246, 250, 1.0); + animation-name: im_message_focus_fade; + animation-duration: 5s; + } +} + +@keyframes im_message_focus_fade { + from { + background-color: rgba(242, 246, 250, 1.0); + } + to { + background-color: rgba(242, 246, 250, 0); + } } /* Colorized user names */ diff --git a/app/less/desktop.less b/app/less/desktop.less index 46aebdd1..c2c1e70e 100644 --- a/app/less/desktop.less +++ b/app/less/desktop.less @@ -1180,7 +1180,6 @@ a.im_panel_peer_photo .peer_initials { .im_send_field_wrap { margin-bottom: 13px; - position: relative; padding-bottom: 2px; overflow-x: hidden; } @@ -1283,56 +1282,6 @@ a.im_panel_peer_photo .peer_initials { } } -.im_send_reply { - &_wrap { - margin-bottom: 5px; - } - - &_form_wrap { - a.im_panel_own_photo, - a.im_panel_peer_photo { - margin-top: 41px; - } - } - - &_cancel { - float: right; - display: block; - width: 18px; - height: 18px; - margin-right: 6px; - margin-top: 5px; - -webkit-transform: translate3d(0,0,0); - padding-top: 7px; - - .icon-reply-bar { - display: block; - background: #999; - width: 18px; - height: 2px; - transform-origin: 50% 50%; - } - - &:hover { - .icon-reply-bar { - background: #44a1e8; - } - } - } -} - -.icon-reply-bar { - &:first-child { - .transform(rotate(-45deg)); - - transform-origin: 50% 50%; - } - - &:last-child { - .transform(translate3d(0,-2px,0) rotate(45deg)); - } -} - @media (max-height: 600px) { a { &.im_panel_peer_photo, @@ -1798,21 +1747,6 @@ a.im_panel_peer_photo .peer_initials { background: #f2f6fa; } -.im_message_focus .im_message_outer_wrap { - background-color: rgba(242, 246, 250, 1.0); - animation-name: im_message_focus_fade; - animation-duration: 5s; -} - -@keyframes im_message_focus_fade { - from { - background-color: rgba(242, 246, 250, 1.0); - } - to { - background-color: rgba(242, 246, 250, 0); - } -} - .im_history_selectable .im_message_outer_wrap { cursor: pointer; } diff --git a/app/less/mobile.less b/app/less/mobile.less index 2ce1f3ac..9f4ee298 100644 --- a/app/less/mobile.less +++ b/app/less/mobile.less @@ -92,7 +92,6 @@ html { } .navbar-toggle { - &:hover, &:active, &:focus { background-color: rgba(255,255,255,0.1); @@ -136,7 +135,9 @@ html { .tg_page_head { .navbar-inverse { - .navbar-toggle:hover, + .navbar-toggle:hover { + background-color: rgba(0,0,0,0.0); + } .open .navbar-toggle { background-color: rgba(0,0,0,0.1); } @@ -205,8 +206,6 @@ html { border: 1px solid transparent; border-radius: 4px; - &:hover, - &:link, &:active { background-color: rgba(0,0,0,0.1); } @@ -524,12 +523,6 @@ html { background: #e1e9f0; } -.im_message_focus { - .im_message_outer_wrap { - background: rgba(225, 233, 240, 0.35); - } -} - .im_message_body { padding: 7px 10px; border-radius: 3px; @@ -921,11 +914,15 @@ a.im_message_from_photo { .contacts_modal_search_field { font-size: 1.2em; } +.im_edit_start_actions { + text-align: center; +} .im_edit_selected_actions { text-align: center; } .im_edit_delete_btn, -.im_edit_forward_btn { +.im_edit_forward_btn, +.im_start_btn { border-radius: 2px; font-weight: normal; line-height: 18px; @@ -942,6 +939,9 @@ a.im_message_from_photo { .im_edit_delete_btn { color: #c3584d !important; } +.im_start_btn { + color: #497495 !important; +} .im_edit_delete_btn strong, .im_edit_forward_btn strong { font-weight: normal; @@ -1333,7 +1333,6 @@ a.im_message_fwd_author { height: 32px; padding: 3px 13px 4px 16px; - &:hover, &:active { .icon-paperclip { background-position: -12px -100px; @@ -1445,6 +1444,10 @@ a.im_message_fwd_author { } } +.composer_emoji_tooltip .scroller_native_scrollable { + height: 106px; +} + .icon-tooltip-tail { display: none; } @@ -1461,6 +1464,37 @@ a.im_message_fwd_author { } } +.composer_dropdown_wrap { + box-shadow: none; +} +.composer_dropdown a.composer_command_option, +.composer_dropdown a.composer_mention_option { + padding: 5px 12px; + font-size: 12px; + line-height: 25px; +} +.composer_user_name, +.composer_user_mention { + line-height: 25px; +} +span.composer_user_photo, +img.composer_user_photo { + width: 25px; + height: 25px; +} + +.composer_dropdown a.composer_emoji_option { + padding: 5px 12px; + font-size: 13px; + line-height: 26px; +} +.composer_dropdown a.composer_emoji_option .emoji { + vertical-align: top; +} +.composer_emoji_shortcut { + line-height: 26px; +} + .contacts_modal { &_search { padding: 3px 0 12px; @@ -1490,9 +1524,16 @@ a.im_message_fwd_author { } } } -.sessions_modal_wrap .mobile_modal_body { +.modal-content .sessions_modal_wrap .mobile_modal_body { padding: 0; } +.sessions_modal_session { + padding: 8px 15px; +} +.sessions_modal_terminate_all_wrap { + margin-left: 15px; +} + .mobile_modal { .peer_select_modal_wrap { @@ -1684,4 +1725,39 @@ a.media_modal_date:hover { .stickerset_modal_sticker_wrap img { width: 64px; height: 64px; +} + +.message_actions_modal_window { + max-width: 220px; + margin: 0 auto; +} +.message_actions_wrap { + padding: 10px 20px; +} + + + +.im_send_reply_wrap { + margin-left: 38px; + margin-right: 10px; +} +.composer_rich_textarea, +.composer_textarea { + .im_send_field_wrap_2ndbtn & { + padding-right: 35px; + } +} +.im_send_field_panel { + position: relative; +} +.composer_command_btn { + right: 10px; + top: 6px; +} +.composer_keyboard_btn { + right: 10px; + top: 6px; +} +.im_send_keyboard_wrap { + padding: 0 5px; } \ No newline at end of file diff --git a/app/partials/desktop/im.html b/app/partials/desktop/im.html index d31ea971..04d76673 100644 --- a/app/partials/desktop/im.html +++ b/app/partials/desktop/im.html @@ -174,7 +174,7 @@
    - +
    diff --git a/app/partials/mobile/audio_player.html b/app/partials/mobile/audio_player.html index 0755082f..5f32ec95 100644 --- a/app/partials/mobile/audio_player.html +++ b/app/partials/mobile/audio_player.html @@ -15,6 +15,7 @@ +
    diff --git a/app/partials/mobile/chat_modal.html b/app/partials/mobile/chat_modal.html index a2a99ce0..07254242 100644 --- a/app/partials/mobile/chat_modal.html +++ b/app/partials/mobile/chat_modal.html @@ -17,9 +17,6 @@
  • -
  • - -
  • @@ -109,14 +106,15 @@
    - + +
    -
    +
    diff --git a/app/partials/mobile/im.html b/app/partials/mobile/im.html index b7fc3386..30b9469c 100644 --- a/app/partials/mobile/im.html +++ b/app/partials/mobile/im.html @@ -87,7 +87,7 @@
    -
    +
    @@ -109,33 +109,52 @@
    -
    -
    +
    +
    + +
    +
    -
    +
    -
    + -
    -
    - +
    + +
    -
    - - +
    +
    + + + +
    + +
    + +
    + + +
    + + + +
    - - +
    +
    +
    +
    diff --git a/app/partials/mobile/message.html b/app/partials/mobile/message.html index 1c853e2b..c3815529 100644 --- a/app/partials/mobile/message.html +++ b/app/partials/mobile/message.html @@ -3,8 +3,12 @@
    -
    -
    +
    +
    +
    +
    +
    +
    @@ -45,7 +49,7 @@
    -
    +
    diff --git a/app/partials/mobile/message_actions_modal.html b/app/partials/mobile/message_actions_modal.html new file mode 100644 index 00000000..d53c804e --- /dev/null +++ b/app/partials/mobile/message_actions_modal.html @@ -0,0 +1,11 @@ +
    + +
    + + + + + +
    + +
    diff --git a/app/partials/mobile/sessions_list_modal.html b/app/partials/mobile/sessions_list_modal.html index 6c0b8cdb..13f14d27 100644 --- a/app/partials/mobile/sessions_list_modal.html +++ b/app/partials/mobile/sessions_list_modal.html @@ -38,17 +38,14 @@