Improved mobile UX

This commit is contained in:
Igor Zhukov 2015-07-10 19:36:24 +03:00
parent c43e20692d
commit 3c55bed2ce
17 changed files with 359 additions and 172 deletions

View File

@ -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,12 +1534,16 @@ angular.module('myApp.controllers', ['myApp.i18n'])
})
};
function selectedDelete () {
if ($scope.selectedCount > 0) {
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 () {
@ -1513,15 +1552,18 @@ angular.module('myApp.controllers', ['myApp.i18n'])
});
}
}
}
function selectedForward () {
if ($scope.selectedCount > 0) {
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));

View File

@ -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':

View File

@ -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';

View File

@ -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",

View File

@ -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('<li><a class="composer_emoji_option" data-code="' + encodeEntities(emoticonCode) + '"><i class="emoji emoji-w20 emoji-spritesheet-' + categoryIndex + '" style="background-position: -' + x + 'px -' + y + 'px;"></i><span class="composer_emoji_shortcut">:' + encodeEntities(emoticonData[1][0]) + ':</span></a></li>');
html.push('<li><a class="composer_emoji_option" data-code="' + encodeEntities(emoticonCode) + '"><i class="emoji emoji-w', iconSize, ' emoji-spritesheet-' + categoryIndex + '" style="background-position: -' + x + 'px -' + y + 'px;"></i><span class="composer_emoji_shortcut">:' + encodeEntities(emoticonData[1][0]) + ':</span></a></li>');
}
}
@ -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('<div class="' + classPrefix + '_scrollable_container"><div class="' + classPrefix + '_scrollable_wrap"><div class="' + classPrefix + '_scrollable"></div></div></div>');
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('<div class="scroller_scrollable_container"><div class="scroller_scrollable_wrap nano"><div class="scroller_scrollable nano-content "></div></div></div>');
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('<div class="scroller_native_scrollable"></div>');
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,6 +1220,7 @@ Scroller.prototype.reinit = function () {
Scroller.prototype.updateHeight = function () {
var height;
if (this.useNano) {
if (this.maxHeight || this.minHeight) {
height = this.content[0].offsetHeight;
if (this.maxHeight && height > this.maxHeight) {
@ -1225,8 +1233,9 @@ Scroller.prototype.updateHeight = function () {
} else {
height = this.scroller[0].offsetHeight;
}
if (this.useNano) {
$(this.scroller).nanoScroller();
} else {
height = this.scrollable[0].offsetHeight;
}
return height;
}

View File

@ -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,10 +2659,14 @@ a.composer_command_option.composer_autocomplete_option_active .composer_command_
font-size: 13px;
color: #444;
margin: 10px 0 3px;
&:first-child {
margin-top: 0;
}
.composer_stickerset_title:hover {
&:hover {
color: #444;
}
}
.composer_sticker_btn {
width: 78px;
height: 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 */

View File

@ -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;
}

View File

@ -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 {
@ -1685,3 +1726,38 @@ a.media_modal_date:hover {
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;
}

View File

@ -174,7 +174,7 @@
<div class="im_send_field_wrap" ng-class="historyState.replyKeyboard._ == 'replyKeyboardMarkup' ? 'im_send_field_wrap_2ndbtn' : ''">
<a class="composer_emoji_insert_btn"><i class="icon icon-emoji"></i></a>
<a class="composer_command_btn" ng-show="!historyState.replyKeyboard && commands.list.length > 0 && !draftMessage.text.length || draftMessage.text[0] == '/'" ng-mousedown="toggleSlash($event)" ng-class="draftMessage.text[0] == '/' ? 'active' : ''"><i class="icon icon-slash"></i></a>
<a class="composer_command_btn" ng-show="!historyState.replyKeyboard && commands.list.length > 0 && (!draftMessage.text.length || draftMessage.text[0] == '/')" ng-mousedown="toggleSlash($event)" ng-class="draftMessage.text[0] == '/' ? 'active' : ''"><i class="icon icon-slash"></i></a>
<a class="composer_keyboard_btn" ng-show="historyState.replyKeyboard._ == 'replyKeyboardMarkup'" ng-mousedown="replyKeyboardToggle($event)" ng-class="!historyState.replyKeyboard.pFlags.hidden ? 'active' : ''"><i class="icon icon-keyboard"></i></a>
<div class="im_send_dropbox_wrap" my-i18n="im_photos_drop_text"></div>

View File

@ -15,6 +15,7 @@
<span ng-switch-when="1" ng-bind="::audio.file_name"></span>
<span ng-switch-default my-i18n="message_attach_audio_message"></span>
</a>
<i ng-if="::message.media_unread || false" ng-show="message.media_unread" class="icon icon-audio-unread"></i>
<div class="audio_player_meta" ng-if="!audio.downloaded || !(mediaPlayer.player.duration || audio.duration)" ng-switch="audio.progress.enabled">
<span ng-switch-when="true" class="audio_player_size" ng-bind="audio.progress | formatSizeProgress"></span>
<span ng-switch-default class="audio_player_size" ng-bind="audio.size | formatSize"></span>

View File

@ -17,9 +17,6 @@
<li ng-if="chatFull.chat._ != 'chatForbidden' &amp;&amp; !chatFull.chat.left">
<a ng-click="editTitle()" my-i18n="group_modal_menu_edit_group"></a>
</li>
<li ng-if="chatFull.chat._ != 'chatForbidden' &amp;&amp; !chatFull.chat.left">
<a ng-click="leaveGroup()" my-i18n="group_modal_menu_leave"></a>
</li>
<li>
<a ng-click="flushHistory()" my-i18n="group_modal_menu_delete_chat"></a>
</li>
@ -110,13 +107,14 @@
<div class="chat_modal_participant_wrap clearfix" ng-repeat="participant in chatFull.participants.participants | orderBy:'-user.sortStatus'">
<a ng-if="participant.canKick" ng-click="kickFromGroup(participant.user_id)" class="chat_modal_participant_kick pull-right" my-i18n="group_modal_members_kick"></a>
<a ng-if="participant.canLeave" ng-click="leaveGroup()" class="chat_modal_participant_kick pull-right" my-i18n="group_modal_menu_leave"></a>
<a class="chat_modal_participant_photo pull-left" my-user-photolink="participant.user_id" img-class="chat_modal_participant_photo" status="true"></a>
<div class="chat_modal_participant_name">
<a my-user-link="participant.user_id"></a>
</div>
<div class="chat_modal_participant_status" my-user-status="::participant.user_id"></div>
<div class="chat_modal_participant_status" my-user-status="::participant.user_id" bot-chat-privacy="true"></div>
</div>
</div>

View File

@ -87,7 +87,7 @@
<div class="im_history_wrap im_history_scrollable_wrap mobile_scrollable_wrap">
<div class="im_history_scrollable">
<div class="im_history" ng-class="{im_history_selectable: historyState.selectActions}">
<div class="im_history" ng-class="{im_history_selectable: !historyState.startBot, im_history_select_active: historyState.selectActions}">
<div ng-if="state.empty" class="im_history_empty" ng-switch="state.mayBeHasMore" my-vertical-position="0.25" padding="true">
<span ng-switch-when="true">
<my-i18n msgid="im_loading_history"></my-i18n><span my-loading-dots></span>
@ -109,21 +109,33 @@
<div class="im_bottom_panel_wrap">
<div class="im_edit_panel_wrap clearfix" ng-show="historyState.selectActions">
<div class="im_edit_selected_actions" my-i18n>
<div class="im_edit_panel_wrap clearfix" ng-show="historyState.selectActions || historyState.startBot" ng-switch="historyState.startBot != false">
<div class="im_edit_start_actions" ng-switch-when="true">
<a class="btn btn-primary im_start_btn" ng-click="botStart()" my-i18n="im_start"></a>
</div>
<div class="im_edit_selected_actions" ng-switch-default my-i18n>
<a class="btn btn-primary im_edit_forward_btn" ng-click="selectedForward()" ng-class="{disabled: !selectedCount}" ng-disabled="!selectedCount" my-i18n-format="im_forward"></a><a class="btn btn-danger im_edit_delete_btn" ng-click="selectedDelete()" ng-class="{disabled: !selectedCount}" ng-disabled="!selectedCount" my-i18n-format="im_delete"></a>
<my-i18n-param name="count"><strong class="im_selected_count" ng-show="selectedCount > 0" ng-bind="selectedCount"></strong></my-i18n-param>
</div>
</div>
<div class="im_send_panel_wrap" ng-hide="historyState.selectActions">
<div class="im_send_panel_wrap" ng-show="!historyState.selectActions &amp;&amp; !historyState.startBot">
<div class="im_send_form_wrap1">
<div class="im_send_form_wrap clearfix" ng-controller="AppImSendController">
<form my-send-form draft-message="draftMessage" class="im_send_form" ng-class="{im_send_form_empty: !draftMessage.text.length}">
<form my-send-form draft-message="draftMessage" mentions="mentions" commands="commands" class="im_send_form" ng-class="{im_send_form_empty: !draftMessage.text.length}">
<div class="im_send_reply_wrap" ng-if="draftMessage.replyToMessage != null">
<a class="im_send_reply_cancel" ng-mousedown="draftMessage.replyClear()"><i class="icon icon-reply-bar"></i><i class="icon icon-reply-bar"></i></a>
<a class="im_message_reply_wrap" my-reply-message="draftMessage.replyToMessage" watch="true"></a>
</div>
<div class="im_send_field_panel">
<div class="im_send_field_wrap" ng-class="historyState.replyKeyboard._ == 'replyKeyboardMarkup' ? 'im_send_field_wrap_2ndbtn' : ''">
<a class="composer_command_btn" ng-show="!historyState.replyKeyboard && commands.list.length > 0 && (!draftMessage.text.length || draftMessage.text[0] == '/')" ng-mousedown="toggleSlash($event)" ng-class="draftMessage.text[0] == '/' ? 'active' : ''"><i class="icon icon-slash"></i></a>
<a class="composer_keyboard_btn" ng-show="historyState.replyKeyboard._ == 'replyKeyboardMarkup'" ng-mousedown="replyKeyboardToggle($event)" ng-class="!historyState.replyKeyboard.pFlags.hidden ? 'active' : ''"><i class="icon icon-keyboard"></i></a>
<div class="im_send_field_wrap">
<div class="im_send_dropbox_wrap" my-i18n="im_photos_drop_text"></div>
<textarea ng-model="draftMessage.text" placeholder="{{'im_message_field_placeholder' | i18n}}" class="form-control im_message_field no_outline" dir="auto"></textarea>
</div>
@ -136,6 +148,13 @@
<a class="composer_emoji_insert_btn pull-right"><i class="icon icon-emoji"></i></a>
<button type="submit" class="btn btn-success im_submit"></button>
</div>
<div class="im_send_keyboard_wrap" ng-if="historyState.replyKeyboard._ == 'replyKeyboardMarkup'" ng-show="!historyState.replyKeyboard.pFlags.hidden">
<div my-reply-markup="historyState.replyKeyboard"></div>
</div>
</form>
</div>

View File

@ -3,8 +3,12 @@
<div class="im_message_wrap clearfix" ng-switch="::historyMessage._ == 'messageService'">
<div class="im_service_message_wrap" ng-switch-when="true">
<div class="im_service_message">
<div class="im_service_message_wrap" ng-switch-when="true" ng-switch="historyMessage.action._ == 'messageActionBotIntro'">
<div ng-switch-when="true" class="im_bot_intro_message_wrap">
<div class="im_bot_intro_message_header" my-i18n="message_service_bot_intro_header"></div>
<div class="im_bot_intro_message" ng-bind-html="::historyMessage.action.rDescription"></div>
</div>
<div ng-switch-default class="im_service_message">
<a class="im_message_author" my-user-link="historyMessage.from_id" short="!historyMessage.to_id.chat_id" color="historyMessage.to_id.chat_id > 0" no-watch="true"></a>
<span class="im_message_service" my-service-message></span>
</div>
@ -45,7 +49,7 @@
<div ng-switch-when="messageMediaPhoto" my-message-photo="historyMessage.media" message-id="historyMessage.id"></div>
<div ng-switch-when="messageMediaVideo" my-message-video="historyMessage.media" message-id="historyMessage.id"></div>
<div ng-switch-when="messageMediaDocument" my-message-document="historyMessage.media.document" message-id="historyMessage.id"></div>
<div ng-switch-when="messageMediaAudio" class="im_message_audio" my-audio-player audio="historyMessage.media.audio"></div>
<div ng-switch-when="messageMediaAudio" class="im_message_audio" my-audio-player audio="historyMessage.media.audio" message="historyMessage"></div>
<div ng-switch-when="messageMediaGeo" my-message-geo="historyMessage.media"></div>
<div ng-switch-when="messageMediaVenue" my-message-geo="historyMessage.media"></div>
<div ng-switch-when="messageMediaContact" class="im_message_contact" my-message-contact></div>

View File

@ -0,0 +1,11 @@
<div class="message_actions_modal_wrap" my-modal-position>
<div class="message_actions_wrap">
<button class="btn btn-md btn-md-primary btn-block" my-i18n="message_action_reply" ng-click="$close('reply')"></button>
<button class="btn btn-md btn-md-primary btn-block" my-i18n="message_action_forward" ng-click="$close('forward')"></button>
<button class="btn btn-md btn-md-primary btn-block" my-i18n="message_action_delete" ng-click="$close('delete')"></button>
<button class="btn btn-md btn-md-primary btn-block" my-i18n="message_action_select" ng-click="$close('select')"></button>
<button class="btn btn-md btn-block" my-i18n="message_action_cancel" ng-click="$dismiss()"></button>
</div>
</div>

View File

@ -38,17 +38,14 @@
<ul ng-switch-default class="sessions_modal_sessions_list nav nav-pills nav-stacked">
<li class="sessions_modal_session_wrap clearfix" ng-repeat="authorization in authorizations | limitTo: slice.limit track by authorization.hash">
<div ng-if="authorization.current">
<h5 class="sessions_modal_sessions_header" my-i18n="sessions_modal_current_session"></h5>
</div>
<div class="sessions_modal_session">
<div class="session_meta_wrap pull-right">
<div class="session_active_date" ng-switch="authorization.current">
<span ng-switch-when="true" class="session_active_date_online" my-i18n="sessions_modal_session_online"></span>
<span ng-switch-when="true" class="session_active_date_online" my-i18n="sessions_modal_current_session"></span>
<span ng-switch-default ng-bind="authorization.date_active | dateOrTime"></span>
</div>
<a ng-if="!authorization.current" class="session_terminate_btn btn btn-md" ng-click="terminateSession(authorization.hash)" my-i18n="sessions_modal_terminate_one"></a>
<a ng-if="!authorization.current" class="session_terminate_btn" ng-click="terminateSession(authorization.hash)" my-i18n="sessions_modal_terminate_one"></a>
</div>
<div class="sessions_modal_session_app" ng-bind="authorization.app_name + ' ' + authorization.app_version"></div>
@ -57,10 +54,10 @@
</div>
<div ng-if="authorization.current && authorizations.length > 1">
<div class="sessions_modal_terminate_all_wrap">
<button class="btn btn-md btn-md-danger" ng-click="terminateAllSessions()" my-i18n="sessions_modal_terminate_all"></button>
<a ng-click="terminateAllSessions()" my-i18n="sessions_modal_terminate_all"></a>
</div>
<h5 class="sessions_modal_sessions_header" my-i18n="sessions_modal_active_sessions"></h5>
<hr class="sessions_modal_other_splitter"></hr>
</div>
</li>

View File

@ -59,7 +59,7 @@
<div class="mobile_user_modal_photo_profile_wrap">
<a href="" ng-click="openPhoto(user.photo.photo_id, {u: profile.id})" class="mobile_user_modal_image_wrap pull-left" ng-class="{disabled: !user.photo.id}">
<a href="" ng-click="openPhoto(profile.photo.photo_id, {p: profile.id})" class="mobile_user_modal_image_wrap pull-left" ng-class="{disabled: !user.photo.id}">
<img
class="mobile_user_modal_image"
my-load-thumb

View File

@ -64,6 +64,11 @@
</div>
<div class="mobile_modal_section" ng-if="user.pFlags.bot &amp;&amp; bot_info.rAbout">
<h4 class="mobile_modal_section_header" my-i18n="user_modal_about"></h4>
<div class="mobile_modal_section_value" ng-bind-html="bot_info.rAbout"></div>
</div>
<div class="mobile_modal_action_wrap">
<a class="mobile_modal_action" ng-click="goToHistory()" my-i18n="user_modal_send_message"></a>
</div>
@ -72,6 +77,19 @@
<a class="mobile_modal_action" ng-click="shareContact()" my-i18n="user_modal_share_contact"></a>
</div>
<div class="mobile_modal_action_wrap" ng-if="user.pFlags.bot &amp;&amp; !user.pFlags.botNoGroups">
<a class="mobile_modal_action" ng-click="inviteToGroup()" my-i18n="user_modal_add_to_group"></a>
</div>
<div class="mobile_modal_action_wrap" ng-if="bot_info.commands.settings != null">
<a class="mobile_modal_action" ng-click="sendCommand('settings')" my-i18n="user_modal_bot_settings"></a>
</div>
<div class="mobile_modal_action_wrap" ng-if="bot_info.commands.help != null">
<a class="mobile_modal_action" ng-click="sendCommand('help')" my-i18n="user_modal_bot_help"></a>
</div>
<div class="mobile_modal_action_wrap">
<a class="mobile_modal_action tg_checkbox clearfix" ng-click="settings.notifications = !settings.notifications" ng-class="settings.notifications ? 'tg_checkbox_on' : ''">
<span class="icon icon-checkbox-outer"><i class="icon-checkbox-inner"></i></span>