Browse Source

Merge branch '15th-layer-support'

master
Igor Zhukov 9 years ago
parent
commit
2d0e863ed8
  1. 79
      app/css/app.css
  2. 70
      app/css/desktop.css
  3. 18
      app/css/mobile.css
  4. 118
      app/js/controllers.js
  5. 101
      app/js/directives.js
  6. 4
      app/js/lib/config.js
  7. 82
      app/js/lib/schema.tl.txt
  8. 13
      app/js/lib/tl_utils.js
  9. 1
      app/js/lib/utils.js
  10. 2
      app/js/locales/en-us.json
  11. 181
      app/js/message_composer.js
  12. 513
      app/js/services.js
  13. 2
      app/partials/desktop/head.html
  14. 18
      app/partials/desktop/im.html
  15. 6
      app/partials/desktop/message.html
  16. 55
      app/partials/desktop/reply_message.html
  17. 4
      app/partials/mobile/message.html
  18. 2
      webogram.sublime-project

79
app/css/app.css

@ -1762,6 +1762,55 @@ div.im_message_body { @@ -1762,6 +1762,55 @@ div.im_message_body {
user-select: text;
}
.im_message_reply_wrap {
display: block;
color: inherit;
text-decoration: none;
margin-bottom: 5px;
height: 42px;
overflow: hidden;
}
.im_message_reply_wrap:hover {
text-decoration: none;
color: inherit;
background: rgba(242, 246, 250, 0.5);
}
.im_message_reply {
border: 0 #77b7e4 solid;
border-left-width: 2px;
padding-left: 8px;
}
.im_message_reply_thumb_wrap {
display: block;
float: left;
width: 42px;
height: 42px;
text-align: center;
position: absolute;
}
.im_message_reply_author {
font-weight: bold;
color: #3a6d99;
margin-top: 2px;
margin-bottom: 3px;
}
.im_message_reply_loading {
padding: 12px 0;
}
.im_reply_message_service {
color: #999;
}
.im_message_reply_body {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.im_message_reply_thumbed .im_message_reply_author,
.im_message_reply_thumbed .im_message_reply_body {
margin-left: 52px;
}
a.im_message_fwd_photo {
position: absolute;
margin-top: 1px;
@ -2141,7 +2190,7 @@ a.composer_emoji_btn:hover { @@ -2141,7 +2190,7 @@ a.composer_emoji_btn:hover {
color: #52719a;
}
.composer_dropdown li a:hover,
.composer_dropdown li a.composer_emoji_option_active {
.composer_dropdown li a.composer_autocomplete_option_active {
color: #52719a;
background: #f2f6fa;
}
@ -2151,6 +2200,19 @@ a.composer_emoji_btn:hover { @@ -2151,6 +2200,19 @@ a.composer_emoji_btn:hover {
margin-left: 15px;
line-height: 20px;
}
.composer_mention_option {
line-height: 20px;
}
.composer_user_mention {
color: #808080;
margin-left: 7px;
}
.composer_dropdown li a:hover .composer_user_mention,
.composer_dropdown li a.composer_autocomplete_option_active .composer_user_mention {
color: #698192;
}
.composer_sticker_btn {
width: 67px;
height: 67px;
@ -2502,21 +2564,6 @@ img.chat_modal_participant_photo { @@ -2502,21 +2564,6 @@ img.chat_modal_participant_photo {
}
/* Messages edit panel */
.im_edit_selected_actions {
text-align: center;
}
.im_edit_delete_btn,
.im_edit_forward_btn {
border-radius: 2px;
padding: 7px 17px;
font-weight: normal;
font-size: 12px;
line-height: 18px;
margin: 6px 6px;
}
.im_edit_panel_title {
text-align: center;
margin: 0;

70
app/css/desktop.css

@ -96,10 +96,13 @@ @@ -96,10 +96,13 @@
-ms-transform: translateY(-1px);
-o-transform: translateY(-1px);
transform: translateY(-1px);
}
.tg_head_logo_dropdown:not(.open) .dropdown-toggle:hover .icon-bar:nth-child(3) {
-webkit-transform: translateY(1px);
-moz-transform: translateY(1px);
-ms-transform: translateY(1px);
-o-transform: translateY(1px);
transform: translateY(1px);
}
.tg_head_logo_dropdown.open .icon-bar:nth-child(1) {
@ -677,6 +680,17 @@ a.footer_link.active:active { @@ -677,6 +680,17 @@ a.footer_link.active:active {
opacity: 1;
}
/* Messages edit panel */
.im_edit_delete_btn,
.im_edit_forward_btn,
.im_edit_reply_btn {
border-radius: 2px;
padding: 7px 17px;
font-weight: normal;
font-size: 12px;
line-height: 18px;
margin: 6px 0 6px 14px;
}
.im_edit_panel_wrap {
padding: 0px 0 38px;
margin: 0 24px 0 12px;
@ -686,7 +700,6 @@ a.footer_link.active:active { @@ -686,7 +700,6 @@ a.footer_link.active:active {
margin: 0 0 47px 3px;
border-bottom: 1px solid #EEE;
}
.im_edit_flush_link,
.im_edit_cancel_link {
display: block;
padding: 7px 17px;
@ -695,14 +708,15 @@ a.footer_link.active:active { @@ -695,14 +708,15 @@ a.footer_link.active:active {
margin: 6px 6px;
}
.im_edit_cancel_link {
float: left;
}
.im_edit_flush_link {
float: right;
}
.im_edit_selected_actions {
text-align: left;
text-transform: uppercase;
}
.im_selected_count {
color: #b9cfe3;
}
.im_submit {
color: #499dd9;
@ -876,6 +890,7 @@ a.footer_link.active:active { @@ -876,6 +890,7 @@ a.footer_link.active:active {
}
}
.im_message_fwd_author_wrap {
margin: 1px 0 4px;
display: inline-block;
@ -987,6 +1002,7 @@ a.im_panel_peer_photo .peer_initials { @@ -987,6 +1002,7 @@ a.im_panel_peer_photo .peer_initials {
.im_send_field_wrap {
margin-bottom: 15px;
position: relative;
}
.composer_rich_textarea,
.composer_textarea {
@ -1077,6 +1093,49 @@ a.im_panel_peer_photo .peer_initials { @@ -1077,6 +1093,49 @@ a.im_panel_peer_photo .peer_initials {
opacity: 1;
}
.im_send_reply_wrap {
margin-bottom: 5px;
}
.im_send_reply_form_wrap a.im_panel_own_photo,
.im_send_reply_form_wrap a.im_panel_peer_photo {
margin-top: 47px;
}
.im_send_reply_cancel {
float: right;
display: block;
width: 18px;
height: 18px;
margin-right: 6px;
margin-top: 5px;
-webkit-transform: translate3d(0,0,0);
padding-top: 7px;
}
.im_send_reply_cancel .icon-reply-bar {
display: block;
background: #999;
width: 18px;
height: 2px;
transform-origin: 50% 50%;
}
.im_send_reply_cancel:hover .icon-reply-bar {
background: #44a1e8;
}
.icon-reply-bar:first-child {
-webkit-transform: rotate(-45deg);
-moz-transform: rotate(-45deg);
-ms-transform: rotate(-45deg);
-o-transform: rotate(-45deg);
transform: rotate(-45deg);
transform-origin: 50% 50%;
}
.icon-reply-bar:last-child {
-webkit-transform: translate3d(0,-2px,0) rotate(45deg);
-moz-transform: translate3d(0,-2px,0) rotate(45deg);
-ms-transform: translate3d(0,-2px,0) rotate(45deg);
-o-transform: translate3d(0,-2px,0) rotate(45deg);
transform: translate3d(0,-2px,0) rotate(45deg);
}
/* Peer modals */
.user_modal_window .modal-dialog {
max-width: 480px;
@ -1497,7 +1556,6 @@ a.im_panel_peer_photo .peer_initials { @@ -1497,7 +1556,6 @@ a.im_panel_peer_photo .peer_initials {
.im_dialog_photo {
width: 48px;
height: 48px;
line-height: 48px;
border-radius: 50%;
overflow: hidden;
}

18
app/css/mobile.css

@ -412,6 +412,9 @@ html { @@ -412,6 +412,9 @@ html {
.im_message_selected {
background: #e1e9f0;
}
.im_message_focus .im_message_outer_wrap {
background: rgba(225, 233, 240, 0.35);
}
.im_message_body {
@ -608,6 +611,14 @@ img.im_message_video_thumb, @@ -608,6 +611,14 @@ img.im_message_video_thumb,
margin-top: 0;
}
.im_message_reply_wrap {
margin-top: 2px;
}
.im_message_reply_author {
font-weight: normal;
font-size: 13px;
}
.im_message_fwd_header {
font-size: 12px;
}
@ -624,7 +635,6 @@ img.im_message_video_thumb, @@ -624,7 +635,6 @@ img.im_message_video_thumb,
}
.im_message_date {
font-size: 10px;
/*font-size: 12px;*/
padding: 0;
}
.im_message_out .im_message_meta {
@ -736,8 +746,14 @@ a.im_message_from_photo { @@ -736,8 +746,14 @@ a.im_message_from_photo {
.contacts_modal_search_field {
font-size: 1.2em;
}
.im_edit_selected_actions {
text-align: center;
}
.im_edit_delete_btn,
.im_edit_forward_btn {
border-radius: 2px;
font-weight: normal;
line-height: 18px;
background: none !important;
border: 0 !important;
width: 50%;

118
app/js/controllers.js

@ -349,7 +349,12 @@ angular.module('myApp.controllers', ['myApp.i18n']) @@ -349,7 +349,12 @@ angular.module('myApp.controllers', ['myApp.i18n'])
$scope.search = {};
$scope.historyFilter = {mediaType: false};
$scope.historyPeer = {};
$scope.historyState = {selectActions: false, typing: [], missedCount: 0};
$scope.historyState = {
selectActions: false,
typing: [],
missedCount: 0,
skipped: false
};
$scope.openSettings = function () {
$modal.open({
@ -862,16 +867,17 @@ angular.module('myApp.controllers', ['myApp.i18n']) @@ -862,16 +867,17 @@ angular.module('myApp.controllers', ['myApp.i18n'])
StatusManager.start();
$scope.peerHistories = [];
$scope.skippedHistory = false;
$scope.selectedMsgs = {};
$scope.selectedCount = 0;
$scope.historyState.selectActions = false;
$scope.historyState.missedCount = 0;
$scope.historyState.skipped = false;
$scope.state = {};
$scope.toggleMessage = toggleMessage;
$scope.selectedDelete = selectedDelete;
$scope.selectedForward = selectedForward;
$scope.selectedReply = selectedReply;
$scope.selectedCancel = selectedCancel;
$scope.selectedFlush = selectedFlush;
@ -1066,7 +1072,7 @@ angular.module('myApp.controllers', ['myApp.i18n']) @@ -1066,7 +1072,7 @@ angular.module('myApp.controllers', ['myApp.i18n'])
} else {
minID = 0;
}
$scope.skippedHistory = hasLess = minID > 0;
$scope.historyState.skipped = hasLess = minID > 0;
if (morePending) {
showMoreHistory();
@ -1123,7 +1129,7 @@ angular.module('myApp.controllers', ['myApp.i18n']) @@ -1123,7 +1129,7 @@ angular.module('myApp.controllers', ['myApp.i18n'])
$scope.historyState.missedCount = 0;
hasMore = false;
$scope.skippedHistory = hasLess = false;
$scope.historyState.skipped = hasLess = false;
maxID = 0;
minID = 0;
peerHistory = historiesQueuePush(peerID);
@ -1169,7 +1175,7 @@ angular.module('myApp.controllers', ['myApp.i18n']) @@ -1169,7 +1175,7 @@ angular.module('myApp.controllers', ['myApp.i18n'])
: 0;
maxID = historyResult.history[historyResult.history.length - 1];
$scope.skippedHistory = hasLess = minID > 0;
$scope.historyState.skipped = hasLess = minID > 0;
hasMore = historyResult.count === null ||
fetchedLength && fetchedLength < historyResult.count;
@ -1179,7 +1185,7 @@ angular.module('myApp.controllers', ['myApp.i18n']) @@ -1179,7 +1185,7 @@ angular.module('myApp.controllers', ['myApp.i18n'])
peerHistory.messages = [];
angular.forEach(historyResult.history, function (id) {
var message = AppMessagesManager.wrapForHistory(id);
if ($scope.skippedHistory) {
if ($scope.historyState.skipped) {
delete message.unread;
}
if (historyResult.unreadOffset) {
@ -1336,6 +1342,17 @@ angular.module('myApp.controllers', ['myApp.i18n']) @@ -1336,6 +1342,17 @@ angular.module('myApp.controllers', ['myApp.i18n'])
}
}
function selectedReply () {
if ($scope.selectedCount == 1) {
var selectedMessageID;
angular.forEach($scope.selectedMsgs, function (t, messageID) {
selectedMessageID = messageID;
});
selectedCancel();
$scope.$broadcast('reply_selected', selectedMessageID);
}
}
function toggleEdit () {
if ($scope.historyState.selectActions) {
selectedCancel();
@ -1374,7 +1391,7 @@ angular.module('myApp.controllers', ['myApp.i18n']) @@ -1374,7 +1391,7 @@ angular.module('myApp.controllers', ['myApp.i18n'])
}
var curPeer = addedMessage.peerID == $scope.curDialog.peerID;
if (curPeer) {
if ($scope.historyFilter.mediaType || $scope.skippedHistory) {
if ($scope.historyFilter.mediaType || $scope.historyState.skipped) {
if (addedMessage.my) {
returnToRecent();
} else {
@ -1396,7 +1413,7 @@ angular.module('myApp.controllers', ['myApp.i18n']) @@ -1396,7 +1413,7 @@ angular.module('myApp.controllers', ['myApp.i18n'])
$scope.historyState.typing.splice(0, $scope.historyState.typing.length);
$scope.$broadcast('ui_history_append_new', {
my: addedMessage.my,
noScroll: unreadAfterIdle && !historyMessage.out && $rootScope.idle.isIDLE
idleScroll: unreadAfterIdle && !historyMessage.out && $rootScope.idle.isIDLE
});
if (addedMessage.my && $scope.historyUnreadAfter) {
delete $scope.historyUnreadAfter;
@ -1488,7 +1505,7 @@ angular.module('myApp.controllers', ['myApp.i18n']) @@ -1488,7 +1505,7 @@ angular.module('myApp.controllers', ['myApp.i18n'])
$scope.$on('history_need_more', showMoreHistory);
$rootScope.$watch('idle.isIDLE', function (newVal) {
if (!newVal && $scope.curDialog && $scope.curDialog.peerID && !$scope.historyFilter.mediaType && !$scope.skippedHistory) {
if (!newVal && $scope.curDialog && $scope.curDialog.peerID && !$scope.historyFilter.mediaType && !$scope.historyState.skipped) {
AppMessagesManager.readHistory($scope.curDialog.inputPeer);
}
if (!newVal) {
@ -1502,13 +1519,17 @@ angular.module('myApp.controllers', ['myApp.i18n']) @@ -1502,13 +1519,17 @@ angular.module('myApp.controllers', ['myApp.i18n'])
$scope.$on('user_update', angular.noop);
})
.controller('AppImSendController', function ($scope, $timeout, MtpApiManager, Storage, AppPeersManager, AppDocsManager, AppMessagesManager, ApiUpdatesManager, MtpApiFileManager) {
.controller('AppImSendController', function ($scope, $timeout, MtpApiManager, Storage, AppChatsManager, AppUsersManager, AppPeersManager, AppDocsManager, AppMessagesManager, ApiUpdatesManager, MtpApiFileManager) {
$scope.$watch('curDialog.peer', resetDraft);
$scope.$on('user_update', angular.noop);
$scope.$on('reply_selected', function (e, messageID) {
replySelect(messageID);
});
$scope.$on('ui_typing', onTyping);
$scope.draftMessage = {text: '', send: sendMessage};
$scope.draftMessage = {text: '', send: sendMessage, replyClear: replyClear};
$scope.mentions = {};
$scope.$watch('draftMessage.text', onMessageChange);
$scope.$watch('draftMessage.files', onFilesSelected);
$scope.$watch('draftMessage.sticker', onStickerSelected);
@ -1529,11 +1550,14 @@ angular.module('myApp.controllers', ['myApp.i18n']) @@ -1529,11 +1550,14 @@ angular.module('myApp.controllers', ['myApp.i18n'])
});
var timeout = 0;
var options = {
replyToMsgID: $scope.draftMessage.replyToMessage && $scope.draftMessage.replyToMessage.id
};
do {
(function (peerID, curText, curTimeout) {
setTimeout(function () {
AppMessagesManager.sendText(peerID, curText);
AppMessagesManager.sendText(peerID, curText, options);
}, curTimeout)
})($scope.curDialog.peerID, text.substr(0, 4096), timeout);
@ -1550,8 +1574,40 @@ angular.module('myApp.controllers', ['myApp.i18n']) @@ -1550,8 +1574,40 @@ angular.module('myApp.controllers', ['myApp.i18n'])
return cancelEvent(e);
}
function updateMentions () {
var peerID = $scope.curDialog.peerID;
if (!peerID || peerID > 0) {
safeReplaceObject($scope.mentions, {});
$scope.$broadcast('mentions_update');
return;
}
AppChatsManager.getChatFull(-peerID).then(function (chatFull) {
var participantsVector = (chatFull.participants || {}).participants || [];
var mentionUsers = [];
var mentionIndex = SearchIndexManager.createIndex();
angular.forEach(participantsVector, function (participant) {
var user = AppUsersManager.getUser(participant.user_id);
if (user.username) {
mentionUsers.push(user);
SearchIndexManager.indexObject(user.id, AppUsersManager.getUserSearchText(user.id), mentionIndex);
}
});
safeReplaceObject($scope.mentions, {
users: mentionUsers,
index: mentionIndex
});
$scope.$broadcast('mentions_update');
});
}
function resetDraft (newPeer) {
updateMentions();
replyClear();
if (newPeer) {
Storage.get('draft' + $scope.curDialog.peerID).then(function (draftText) {
// console.log('Restore draft', 'draft' + $scope.curDialog.peerID, draftText);
@ -1566,12 +1622,22 @@ angular.module('myApp.controllers', ['myApp.i18n']) @@ -1566,12 +1622,22 @@ angular.module('myApp.controllers', ['myApp.i18n'])
}
}
function replySelect(messageID) {
$scope.draftMessage.replyToMessage = AppMessagesManager.wrapForHistory(messageID);
$scope.$broadcast('ui_peer_reply');
}
function replyClear() {
delete $scope.draftMessage.replyToMessage;
$scope.$broadcast('ui_peer_reply');
}
function onMessageChange(newVal) {
// console.log('ctrl text changed', newVal);
// console.trace('ctrl text changed', newVal);
if (newVal && newVal.length) {
if (!$scope.historyFilter.mediaType && !$scope.skippedHistory) {
if (!$scope.historyFilter.mediaType && !$scope.historyState.skipped) {
AppMessagesManager.readHistory($scope.curDialog.inputPeer);
}
@ -1596,11 +1662,15 @@ angular.module('myApp.controllers', ['myApp.i18n']) @@ -1596,11 +1662,15 @@ angular.module('myApp.controllers', ['myApp.i18n'])
if (!angular.isArray(newVal) || !newVal.length) {
return;
}
var options = {
replyToMsgID: $scope.draftMessage.replyToMessage && $scope.draftMessage.replyToMessage.id,
isMedia: $scope.draftMessage.isMedia
};
delete $scope.draftMessage.replyToMessage;
for (var i = 0; i < newVal.length; i++) {
AppMessagesManager.sendFile($scope.curDialog.peerID, newVal[i], {
isMedia: $scope.draftMessage.isMedia
});
AppMessagesManager.sendFile($scope.curDialog.peerID, newVal[i], options);
$scope.$broadcast('ui_message_send');
}
}
@ -1612,7 +1682,6 @@ angular.module('myApp.controllers', ['myApp.i18n']) @@ -1612,7 +1682,6 @@ angular.module('myApp.controllers', ['myApp.i18n'])
var doc = AppDocsManager.getDoc(newVal);
if (doc.id && doc.access_hash) {
console.log('sticker', doc);
var inputMedia = {
_: 'inputMediaDocument',
id: {
@ -2360,13 +2429,14 @@ angular.module('myApp.controllers', ['myApp.i18n']) @@ -2360,13 +2429,14 @@ angular.module('myApp.controllers', ['myApp.i18n'])
}).then(function (addResult) {
ApiUpdatesManager.processUpdateMessage({
_: 'updates',
seq: addResult.seq,
users: addResult.users,
chats: addResult.chats,
seq: 0,
updates: [{
_: 'updateNewMessage',
message: addResult.message,
pts: addResult.pts
pts: addResult.pts,
pts_count: addResult.pts_count
}]
});
});
@ -2997,13 +3067,14 @@ angular.module('myApp.controllers', ['myApp.i18n']) @@ -2997,13 +3067,14 @@ angular.module('myApp.controllers', ['myApp.i18n'])
}).then(function (createdResult) {
ApiUpdatesManager.processUpdateMessage({
_: 'updates',
seq: createdResult.seq,
users: createdResult.users,
chats: createdResult.chats,
seq: 0,
updates: [{
_: 'updateNewMessage',
message: createdResult.message,
pts: createdResult.pts
pts: createdResult.pts,
pts_count: createdResult.pts_count
}]
});
@ -3037,13 +3108,14 @@ angular.module('myApp.controllers', ['myApp.i18n']) @@ -3037,13 +3108,14 @@ angular.module('myApp.controllers', ['myApp.i18n'])
}).then(function (editResult) {
ApiUpdatesManager.processUpdateMessage({
_: 'updates',
seq: editResult.seq,
users: editResult.users,
chats: editResult.chats,
seq: 0,
updates: [{
_: 'updateNewMessage',
message: editResult.message,
pts: editResult.pts
pts: editResult.pts,
pts_count: editResult.pts_count
}]
});

101
app/js/directives.js

@ -320,6 +320,78 @@ angular.module('myApp.directives', ['myApp.filters']) @@ -320,6 +320,78 @@ angular.module('myApp.directives', ['myApp.filters'])
templateUrl: templateUrl('message_service')
};
})
.directive('myReplyMessage', function(AppPhotosManager, AppMessagesManager, AppPeersManager, $rootScope) {
return {
templateUrl: templateUrl('reply_message'),
scope: {
'replyMessage': '=myReplyMessage'
},
link: link
};
function link ($scope, element, attrs) {
var message = $scope.replyMessage;
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.wrapForHistory(messageID);
updateMessage($scope, element);
stopWaiting();
}
});
}
}
function updateMessage($scope, element) {
var message = $scope.replyMessage;
var thumbWidth = 42;
var thumbHeight = 42;
var thumbPhotoSize;
if (message.media) {
switch (message.media._) {
case 'messageMediaPhoto':
thumbPhotoSize = AppPhotosManager.choosePhotoSize(message.media.photo, thumbWidth, thumbHeight);
break;
case 'messageMediaDocument':
thumbPhotoSize = message.media.document.thumb;
break;
case 'messageMediaVideo':
thumbPhotoSize = message.media.video.thumb;
break;
}
}
if (thumbPhotoSize && thumbPhotoSize._ != 'photoSizeEmpty') {
var dim = calcImageInBox(thumbPhotoSize.w, thumbPhotoSize.h, thumbWidth, thumbHeight, true);
$scope.thumb = {
width: dim.w,
height: dim.h,
location: thumbPhotoSize.location,
size: thumbPhotoSize.size
};
}
if (element[0].tagName == 'A') {
element.on('click', function () {
var peerID = AppMessagesManager.getMessagePeer(message);
var peerString = AppPeersManager.getPeerString(peerID);
$rootScope.$broadcast('history_focus', {peerString: peerString, messageID: message.id});
})
}
}
})
.directive('myMessagePhoto', function() {
return {
templateUrl: templateUrl('message_attach_photo')
@ -800,10 +872,17 @@ angular.module('myApp.directives', ['myApp.filters']) @@ -800,10 +872,17 @@ angular.module('myApp.directives', ['myApp.filters'])
curAnimation = false;
$scope.$on('ui_history_append_new', function (e, options) {
if (!atBottom && !options.my || options.noScroll) {
if (!atBottom && !options.my) {
onContentLoaded(function () {
$(historyWrap).nanoScroller();
})
});
return;
}
if (options.idleScroll) {
onContentLoaded(function () {
$(historyWrap).nanoScroller();
changeScroll();
});
return;
}
var curAnimated = animated &&
@ -1071,7 +1150,8 @@ angular.module('myApp.directives', ['myApp.filters']) @@ -1071,7 +1150,8 @@ angular.module('myApp.directives', ['myApp.filters'])
return {
link: link,
scope: {
draftMessage: '='
draftMessage: '=',
mentions: '='
}
};
@ -1122,6 +1202,7 @@ angular.module('myApp.directives', ['myApp.filters']) @@ -1122,6 +1202,7 @@ angular.module('myApp.directives', ['myApp.filters'])
getSendOnEnter: function () {
return sendOnEnter;
},
mentions: $scope.mentions,
onMessageSubmit: onMessageSubmit,
onFilePaste: onFilePaste
});
@ -1212,6 +1293,18 @@ angular.module('myApp.directives', ['myApp.filters']) @@ -1212,6 +1293,18 @@ angular.module('myApp.directives', ['myApp.filters'])
composer.focus();
}
});
$scope.$on('ui_peer_reply', function () {
onContentLoaded(function () {
$scope.$emit('ui_editor_resize');
if (!Config.Navigator.touch) {
composer.focus();
}
})
});
$scope.$on('mentions_update', function () {
composer.onMentionsUpdated();
});
var sendAwaiting = false;
$scope.$on('ui_message_before_send', function () {
@ -2056,8 +2149,6 @@ angular.module('myApp.directives', ['myApp.filters']) @@ -2056,8 +2149,6 @@ angular.module('myApp.directives', ['myApp.filters'])
onContentLoaded(updateMargin);
});
};
})

4
app/js/lib/config.js

File diff suppressed because one or more lines are too long

82
app/js/lib/schema.tl.txt

@ -70,7 +70,7 @@ fileLocationUnavailable#7c596b46 volume_id:long local_id:int secret:long = FileL @@ -70,7 +70,7 @@ fileLocationUnavailable#7c596b46 volume_id:long local_id:int secret:long = FileL
fileLocation#53d69076 dc_id:int volume_id:long local_id:int secret:long = FileLocation;
userEmpty#200250ba id:int = User;
userSelf#7007b451 id:int first_name:string last_name:string username:string phone:string photo:UserProfilePhoto status:UserStatus inactive:Bool = User;
userSelf#1c60e608 id:int first_name:string last_name:string username:string phone:string photo:UserProfilePhoto status:UserStatus = User;
userContact#cab35e18 id:int first_name:string last_name:string username:string access_hash:long phone:string photo:UserProfilePhoto status:UserStatus = User;
userRequest#d9ccc4ef id:int first_name:string last_name:string username:string access_hash:long phone:string photo:UserProfilePhoto status:UserStatus = User;
userForeign#75cf7a8 id:int first_name:string last_name:string username:string access_hash:long photo:UserProfilePhoto status:UserStatus = User;
@ -98,8 +98,7 @@ chatPhotoEmpty#37c1011c = ChatPhoto; @@ -98,8 +98,7 @@ chatPhotoEmpty#37c1011c = ChatPhoto;
chatPhoto#6153276a photo_small:FileLocation photo_big:FileLocation = ChatPhoto;
messageEmpty#83e5de54 id:int = Message;
message#567699b3 flags:int id:int from_id:int to_id:Peer date:int message:string media:MessageMedia = Message;
messageForwarded#a367e716 flags:int id:int fwd_from_id:int fwd_date:int from_id:int to_id:Peer date:int message:string media:MessageMedia = Message;
message#a7ab1991 flags:# id:int from_id:int to_id:Peer fwd_from_id:flags.2?int fwd_date:flags.2?int reply_to_msg_id:flags.3?int date:int message:string media:MessageMedia = Message;
messageService#1d86f70e flags:int id:int from_id:int to_id:Peer date:int action:MessageAction = Message;
messageMediaEmpty#3ded6320 = MessageMedia;
@ -107,7 +106,7 @@ messageMediaPhoto#c8c45a2a photo:Photo = MessageMedia; @@ -107,7 +106,7 @@ messageMediaPhoto#c8c45a2a photo:Photo = MessageMedia;
messageMediaVideo#a2d24290 video:Video = MessageMedia;
messageMediaGeo#56e0d474 geo:GeoPoint = MessageMedia;
messageMediaContact#5e7d2f39 phone_number:string first_name:string last_name:string user_id:int = MessageMedia;
messageMediaUnsupported#29632a36 bytes:bytes = MessageMedia;
messageMediaUnsupported#9f84f49e = MessageMedia;
messageActionEmpty#b6aef7b0 = MessageAction;
messageActionChatCreate#a6638b9a title:string users:Vector<int> = MessageAction;
@ -117,7 +116,7 @@ messageActionChatDeletePhoto#95e3fbef = MessageAction; @@ -117,7 +116,7 @@ messageActionChatDeletePhoto#95e3fbef = MessageAction;
messageActionChatAddUser#5e3cfc4b user_id:int = MessageAction;
messageActionChatDeleteUser#b2ae9b0c user_id:int = MessageAction;
dialog#ab3a99ac peer:Peer top_message:int unread_count:int notify_settings:PeerNotifySettings = Dialog;
dialog#c1dd804a peer:Peer top_message:int read_inbox_max_id:int unread_count:int notify_settings:PeerNotifySettings = Dialog;
photoEmpty#2331b22d id:long = Photo;
photo#22b56751 id:long access_hash:long user_id:int date:int caption:string geo:GeoPoint sizes:Vector<PhotoSize> = Photo;
@ -132,7 +131,7 @@ video#388fa391 id:long access_hash:long user_id:int date:int caption:string dura @@ -132,7 +131,7 @@ video#388fa391 id:long access_hash:long user_id:int date:int caption:string dura
geoPointEmpty#1117dd5f = GeoPoint;
geoPoint#2049d70c long:double lat:double = GeoPoint;
auth.checkedPhone#e300cc3b phone_registered:Bool phone_invited:Bool = auth.CheckedPhone;
auth.checkedPhone#811ea28e phone_registered:Bool = auth.CheckedPhone;
auth.sentCode#efed51d9 phone_registered:Bool phone_code_hash:string send_call_timeout:int is_password:Bool = auth.SentCode;
@ -172,15 +171,7 @@ contactStatus#d3680c61 user_id:int status:UserStatus = ContactStatus; @@ -172,15 +171,7 @@ contactStatus#d3680c61 user_id:int status:UserStatus = ContactStatus;
chatLocated#3631cf4c chat_id:int distance:int = ChatLocated;
contacts.foreignLinkUnknown#133421f8 = contacts.ForeignLink;
contacts.foreignLinkRequested#a7801f47 has_phone:Bool = contacts.ForeignLink;
contacts.foreignLinkMutual#1bea8ce1 = contacts.ForeignLink;
contacts.myLinkEmpty#d22a1c60 = contacts.MyLink;
contacts.myLinkRequested#6c69efee contact:Bool = contacts.MyLink;
contacts.myLinkContact#c240ebd9 = contacts.MyLink;
contacts.link#eccea3f5 my_link:contacts.MyLink foreign_link:contacts.ForeignLink user:User = contacts.Link;
contacts.link#3ace484c my_link:ContactLink foreign_link:ContactLink user:User = contacts.Link;
contacts.contactsNotModified#b74ba9d2 = contacts.Contacts;
contacts.contacts#6f8b8cb2 contacts:Vector<Contact> users:Vector<User> = contacts.Contacts;
@ -199,19 +190,18 @@ messages.messages#8c718e87 messages:Vector<Message> chats:Vector<Chat> users:Vec @@ -199,19 +190,18 @@ messages.messages#8c718e87 messages:Vector<Message> chats:Vector<Chat> users:Vec
messages.messagesSlice#b446ae3 count:int messages:Vector<Message> chats:Vector<Chat> users:Vector<User> = messages.Messages;
messages.messageEmpty#3f4e0648 = messages.Message;
messages.message#ff90c417 message:Message chats:Vector<Chat> users:Vector<User> = messages.Message;
messages.statedMessages#969478bb messages:Vector<Message> chats:Vector<Chat> users:Vector<User> pts:int seq:int = messages.StatedMessages;
messages.statedMessages#7d84b48 messages:Vector<Message> chats:Vector<Chat> users:Vector<User> pts:int pts_count:int = messages.StatedMessages;
messages.statedMessage#d07ae726 message:Message chats:Vector<Chat> users:Vector<User> pts:int seq:int = messages.StatedMessage;
messages.statedMessage#96240c6a message:Message chats:Vector<Chat> users:Vector<User> pts:int pts_count:int = messages.StatedMessage;
messages.sentMessage#d1f4d35c id:int date:int pts:int seq:int = messages.SentMessage;
messages.sentMessage#900eac40 id:int date:int pts:int pts_count:int = messages.SentMessage;
messages.chats#8150cbd8 chats:Vector<Chat> users:Vector<User> = messages.Chats;
messages.chats#64ff9fd5 chats:Vector<Chat> = messages.Chats;
messages.chatFull#e5d7d19c full_chat:ChatFull chats:Vector<Chat> users:Vector<User> = messages.ChatFull;
messages.affectedHistory#b7de36f2 pts:int seq:int offset:int = messages.AffectedHistory;
messages.affectedHistory#b45c69d1 pts:int pts_count:int offset:int = messages.AffectedHistory;
inputMessagesFilterEmpty#57e2f66c = MessagesFilter;
inputMessagesFilterPhotos#9609a51c = MessagesFilter;
@ -221,10 +211,10 @@ inputMessagesFilterPhotoVideoDocuments#d95e73bb = MessagesFilter; @@ -221,10 +211,10 @@ inputMessagesFilterPhotoVideoDocuments#d95e73bb = MessagesFilter;
inputMessagesFilterDocument#9eddf188 = MessagesFilter;
inputMessagesFilterAudio#cfc87522 = MessagesFilter;
updateNewMessage#13abdb3 message:Message pts:int = Update;
updateNewMessage#1f2b0afd message:Message pts:int pts_count:int = Update;
updateMessageID#4e90bfd6 id:int random_id:long = Update;
updateReadMessages#c6649e31 messages:Vector<int> pts:int = Update;
updateDeleteMessages#a92bfe26 messages:Vector<int> pts:int = Update;
updateReadMessages#2e5ab668 messages:Vector<int> pts:int pts_count:int = Update;
updateDeleteMessages#a20db0e5 messages:Vector<int> pts:int pts_count:int = Update;
updateUserTyping#5c486927 user_id:int action:SendMessageAction = Update;
updateChatUserTyping#9a65ea1f chat_id:int user_id:int action:SendMessageAction = Update;
updateChatParticipants#7761198 participants:ChatParticipants = Update;
@ -232,7 +222,7 @@ updateUserStatus#1bfbd823 user_id:int status:UserStatus = Update; @@ -232,7 +222,7 @@ updateUserStatus#1bfbd823 user_id:int status:UserStatus = Update;
updateUserName#a7332b73 user_id:int first_name:string last_name:string username:string = Update;
updateUserPhoto#95313b0c user_id:int date:int photo:UserProfilePhoto previous:Bool = Update;
updateContactRegistered#2575bbb9 user_id:int date:int = Update;
updateContactLink#51a48a9a user_id:int my_link:contacts.MyLink foreign_link:contacts.ForeignLink = Update;
updateContactLink#9d2e67c5 user_id:int my_link:ContactLink foreign_link:ContactLink = Update;
updateNewAuthorization#8f06529a auth_key_id:long date:int device:string location:string = Update;
updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State;
@ -242,8 +232,8 @@ updates.difference#f49ca0 new_messages:Vector<Message> new_encrypted_messages:Ve @@ -242,8 +232,8 @@ updates.difference#f49ca0 new_messages:Vector<Message> new_encrypted_messages:Ve
updates.differenceSlice#a8fb1981 new_messages:Vector<Message> new_encrypted_messages:Vector<EncryptedMessage> other_updates:Vector<Update> chats:Vector<Chat> users:Vector<User> intermediate_state:updates.State = updates.Difference;
updatesTooLong#e317af7e = Updates;
updateShortMessage#d3f45784 id:int from_id:int message:string pts:int date:int seq:int = Updates;
updateShortChatMessage#2b2fbd4e id:int from_id:int chat_id:int message:string pts:int date:int seq:int = Updates;
updateShortMessage#ed5c2127 flags:# id:int user_id:int message:string pts:int pts_count:int date:int fwd_from_id:flags.2?int fwd_date:flags.2?int reply_to_msg_id:flags.3?int = Updates;
updateShortChatMessage#52238b3c flags:# id:int from_id:int chat_id:int message:string pts:int pts_count:int date:int fwd_from_id:flags.2?int fwd_date:flags.2?int reply_to_msg_id:flags.3?int = Updates;
updateShort#78d4dec1 update:Update date:int = Updates;
updatesCombined#725b04c3 updates:Vector<Update> users:Vector<User> chats:Vector<Chat> date:int seq_start:int seq:int = Updates;
updates#74ae4240 updates:Vector<Update> users:Vector<User> chats:Vector<Chat> date:int seq:int = Updates;
@ -257,7 +247,7 @@ upload.file#96a18d5 type:storage.FileType mtime:int bytes:bytes = upload.File; @@ -257,7 +247,7 @@ upload.file#96a18d5 type:storage.FileType mtime:int bytes:bytes = upload.File;
dcOption#2ec2a43c id:int hostname:string ip_address:string port:int = DcOption;
config#2e54dd74 date:int test_mode:Bool this_dc:int dc_options:Vector<DcOption> chat_size_max:int broadcast_size_max:int = Config;
config#3e6f732a date:int expires:int test_mode:Bool this_dc:int dc_options:Vector<DcOption> chat_size_max:int broadcast_size_max:int online_update_period_ms:int offline_blur_timeout_ms:int offline_idle_timeout_ms:int online_cloud_timeout_ms:int notify_cloud_delay_ms:int notify_default_delay_ms:int chat_big_size:int disabled_features:Vector<DisabledFeature> = Config;
nearestDc#8e1a1775 country:string this_dc:int nearest_dc:int = NearestDc;
@ -266,11 +256,11 @@ help.noAppUpdate#c45a6536 = help.AppUpdate; @@ -266,11 +256,11 @@ help.noAppUpdate#c45a6536 = help.AppUpdate;
help.inviteText#18cb9f78 message:string = help.InviteText;
messages.statedMessagesLinks#3e74f5c6 messages:Vector<Message> chats:Vector<Chat> users:Vector<User> links:Vector<contacts.Link> pts:int seq:int = messages.StatedMessages;
messages.statedMessagesLinks#51be5d19 messages:Vector<Message> chats:Vector<Chat> users:Vector<User> pts:int pts_count:int links:Vector<contacts.Link> seq:int = messages.StatedMessages;
messages.statedMessageLink#a9af2881 message:Message chats:Vector<Chat> users:Vector<User> links:Vector<contacts.Link> pts:int seq:int = messages.StatedMessage;
messages.statedMessageLink#948a288 message:Message chats:Vector<Chat> users:Vector<User> pts:int pts_count:int links:Vector<contacts.Link> seq:int = messages.StatedMessage;
messages.sentMessageLink#e9db4a3f id:int date:int pts:int seq:int links:Vector<contacts.Link> = messages.SentMessage;
messages.sentMessageLink#e923400d id:int date:int pts:int pts_count:int links:Vector<contacts.Link> seq:int = messages.SentMessage;
inputGeoChat#74d456fa chat_id:int access_hash:long = InputGeoChat;
@ -425,7 +415,7 @@ account.password#739e5f72 current_salt:bytes new_salt:bytes hint:string = accoun @@ -425,7 +415,7 @@ account.password#739e5f72 current_salt:bytes new_salt:bytes hint:string = accoun
documentAttributeImageSize#6c37c15c w:int h:int = DocumentAttribute;
documentAttributeAnimated#11b58939 = DocumentAttribute;
documentAttributeSticker#fb0a5727 = DocumentAttribute;
documentAttributeSticker#994c9882 alt:string = DocumentAttribute;
documentAttributeVideo#5910cccb duration:int w:int h:int = DocumentAttribute;
documentAttributeAudio#51448e5 duration:int = DocumentAttribute;
documentAttributeFilename#15590068 file_name:string = DocumentAttribute;
@ -438,6 +428,18 @@ stickerPack#12b299d4 emoticon:string documents:Vector<long> = StickerPack; @@ -438,6 +428,18 @@ stickerPack#12b299d4 emoticon:string documents:Vector<long> = StickerPack;
messages.allStickersNotModified#e86602c3 = messages.AllStickers;
messages.allStickers#dcef3102 hash:string packs:Vector<StickerPack> documents:Vector<Document> = messages.AllStickers;
disabledFeature#ae636f24 feature:string description:string = DisabledFeature;
updateReadHistoryInbox#9961fd5c peer:Peer max_id:int pts:int pts_count:int = Update;
updateReadHistoryOutbox#2f2f21bf peer:Peer max_id:int pts:int pts_count:int = Update;
messages.affectedMessages#84d19185 pts:int pts_count:int = messages.AffectedMessages;
contactLinkUnknown#5f4f9247 = ContactLink;
contactLinkNone#feedd3ad = ContactLink;
contactLinkHasPhone#268f3f59 = ContactLink;
contactLinkContact#d502c2d0 = ContactLink;
---functions---
invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
@ -484,14 +486,14 @@ messages.getMessages#4222fa74 id:Vector<int> = messages.Messages; @@ -484,14 +486,14 @@ messages.getMessages#4222fa74 id:Vector<int> = messages.Messages;
messages.getDialogs#eccf1df6 offset:int max_id:int limit:int = messages.Dialogs;
messages.getHistory#92a1df2f peer:InputPeer offset:int max_id:int limit:int = messages.Messages;
messages.search#7e9f2ab peer:InputPeer q:string filter:MessagesFilter min_date:int max_date:int offset:int max_id:int limit:int = messages.Messages;
messages.readHistory#eed884c6 peer:InputPeer max_id:int offset:int read_contents:Bool = messages.AffectedHistory;
messages.readHistory#b04f2510 peer:InputPeer max_id:int offset:int = messages.AffectedHistory;
messages.deleteHistory#f4f8fb61 peer:InputPeer offset:int = messages.AffectedHistory;
messages.deleteMessages#14f2dd0a id:Vector<int> = Vector<int>;
messages.deleteMessages#a5f18925 id:Vector<int> = messages.AffectedMessages;
messages.receivedMessages#28abcb68 max_id:int = Vector<int>;
messages.setTyping#a3825e50 peer:InputPeer action:SendMessageAction = Bool;
messages.sendMessage#4cde0aab peer:InputPeer message:string random_id:long = messages.SentMessage;
messages.sendMedia#a3c85d76 peer:InputPeer media:InputMedia random_id:long = messages.StatedMessage;
messages.forwardMessages#514cd10f peer:InputPeer id:Vector<int> = messages.StatedMessages;
messages.sendMessage#1ca852a1 peer:InputPeer reply_to_msg_id:int message:string random_id:long = messages.SentMessage;
messages.sendMedia#fcee7fc0 peer:InputPeer reply_to_msg_id:int media:InputMedia random_id:long = messages.StatedMessage;
messages.forwardMessages#ded42045 peer:InputPeer id:Vector<int> random_id:Vector<long> = messages.StatedMessages;
messages.getChats#3c6aa187 id:Vector<int> = messages.Chats;
messages.getFullChat#3b831c66 chat_id:int = messages.ChatFull;
messages.editChatTitle#b4bc68b5 chat_id:int title:string = messages.StatedMessage;
@ -553,7 +555,7 @@ help.getSupport#9cdf08cd = help.Support; @@ -553,7 +555,7 @@ help.getSupport#9cdf08cd = help.Support;
auth.sendSms#da9f3e8 phone_number:string phone_code_hash:string = Bool;
messages.readMessageContents#354b5bc2 id:Vector<int> = Vector<int>;
messages.readMessageContents#36a73f77 id:Vector<int> = messages.AffectedMessages;
account.checkUsername#2714d86c username:string = Bool;
account.updateUsername#3e0bdd7c username:string = User;
@ -578,4 +580,6 @@ account.setPassword#dd2a4d8f current_password_hash:bytes new_salt:bytes new_pass @@ -578,4 +580,6 @@ account.setPassword#dd2a4d8f current_password_hash:bytes new_salt:bytes new_pass
auth.checkPassword#a63011e password_hash:bytes = auth.Authorization;
messages.getStickers#ae22e045 emoticon:string hash:string = messages.Stickers;
messages.getAllStickers#aa3bc868 hash:string = messages.AllStickers;
messages.getAllStickers#aa3bc868 hash:string = messages.AllStickers;
account.updateDeviceLocked#38df3532 period:int = Bool;

13
app/js/lib/tl_utils.js

@ -466,6 +466,7 @@ TLDeserialization.prototype.fetchRawBytes = function (len, typed, field) { @@ -466,6 +466,7 @@ TLDeserialization.prototype.fetchRawBytes = function (len, typed, field) {
TLDeserialization.prototype.fetchObject = function (type, field) {
switch (type) {
case '#':
case 'int': return this.fetchInt(field);
case 'long': return this.fetchLong(field);
case 'int128': return this.fetchIntBytes(128, false, field);
@ -574,7 +575,17 @@ TLDeserialization.prototype.fetchObject = function (type, field) { @@ -574,7 +575,17 @@ TLDeserialization.prototype.fetchObject = function (type, field) {
this.override[overrideKey].apply(this, [result, field + '[' + predicate + ']']);
} else {
angular.forEach(constructorData.params, function (param) {
result[param.name] = self.fetchObject(param.type, field + '[' + predicate + '][' + param.name + ']');
var type = param.type;
if (type.indexOf('?') !== -1) {
var condType = type.split('?');
var fieldBit = condType[0].split('.');
if (!(result[fieldBit[0]] & (1 << fieldBit[1]))) {
return;
}
type = condType[1];
}
result[param.name] = self.fetchObject(type, field + '[' + predicate + '][' + param.name + ']');
});
}

1
app/js/lib/utils.js

@ -280,6 +280,7 @@ function templateUrl (tplName) { @@ -280,6 +280,7 @@ function templateUrl (tplName) {
error_modal: 'desktop',
media_modal_layout: 'desktop',
slider: 'desktop',
reply_message: 'desktop'
};
var layout = forceLayout[tplName] || (Config.Mobile ? 'mobile' : 'desktop');
return 'partials/' + layout + '/' + tplName + '.html';

2
app/js/locales/en-us.json

@ -325,6 +325,8 @@ @@ -325,6 +325,8 @@
"im_clear_history": "Clear History",
"im_delete": "Delete {count}",
"im_forward": "Forward {count}",
"im_reply": "Reply",
"im_reply_loading": "Loading{dots}",
"im_photos_drop_text": "Drop photos here to send",
"im_message_field_placeholder": "Write a message...",
"im_media_attach_title": "Send media",

181
app/js/message_composer.js

@ -234,7 +234,7 @@ EmojiTooltip.prototype.createTooltip = function () { @@ -234,7 +234,7 @@ EmojiTooltip.prototype.createTooltip = function () {
this.contentEl.on('mousedown', function (e) {
e = e.originalEvent || e;
var target = $(e.target), code, sticker;
if (target.hasClass('emoji') || target.hasClass('composer_sticker_image')) {
if (target[0].tagName != 'A') {
target = $(target[0].parentNode);
}
if (code = target.attr('data-code')) {
@ -382,7 +382,7 @@ function EmojiPanel (containerEl, options) { @@ -382,7 +382,7 @@ function EmojiPanel (containerEl, options) {
this.containerEl.on('mousedown', function (e) {
e = e.originalEvent || e;
var target = $(e.target), code;
if (target.hasClass('emoji')) {
if (target[0].tagName != 'A') {
target = $(target[0].parentNode);
}
if (code = target.attr('data-code')) {
@ -436,8 +436,8 @@ function MessageComposer (textarea, options) { @@ -436,8 +436,8 @@ function MessageComposer (textarea, options) {
var self = this;
this.autoCompleteEl.on('mousedown', function (e) {
e = e.originalEvent || e;
var target = $(e.target), code;
if (target.hasClass('emoji') || target.hasClass('composer_emoji_shortcut')) {
var target = $(e.target), mention, code;
if (target[0].tagName != 'A') {
target = $(target[0].parentNode);
}
if (code = target.attr('data-code')) {
@ -446,6 +446,11 @@ function MessageComposer (textarea, options) { @@ -446,6 +446,11 @@ function MessageComposer (textarea, options) {
}
EmojiHelper.pushPopularEmoji(code);
}
if (mention = target.attr('data-mention')) {
if (self.onMentionSelected) {
self.onMentionSelected(mention);
}
}
return cancelEvent(e);
});
@ -455,6 +460,7 @@ function MessageComposer (textarea, options) { @@ -455,6 +460,7 @@ function MessageComposer (textarea, options) {
this.onMessageSubmit = options.onMessageSubmit;
this.getSendOnEnter = options.getSendOnEnter;
this.onFilePaste = options.onFilePaste;
this.mentions = options.mentions;
}
MessageComposer.prototype.setUpInput = function () {
@ -463,6 +469,7 @@ MessageComposer.prototype.setUpInput = function () { @@ -463,6 +469,7 @@ MessageComposer.prototype.setUpInput = function () {
} else {
this.setUpPlaintext();
}
this.autoCompleteRegEx = /(?:\s|^)(:|@)([A-Za-z0-9\-\+\*_]*)$/;
}
MessageComposer.prototype.setUpRich = function () {
@ -530,34 +537,39 @@ MessageComposer.prototype.onKeyEvent = function (e) { @@ -530,34 +537,39 @@ MessageComposer.prototype.onKeyEvent = function (e) {
if (this.autocompleteShown) {
if (e.keyCode == 38 || e.keyCode == 40) { // UP / DOWN
var next = e.keyCode == 40;
var currentSelected = $(this.autoCompleteEl).find('.composer_emoji_option_active');
var currentSelected = $(this.autoCompleteEl).find('.composer_autocomplete_option_active');
if (currentSelected.length) {
var currentSelectedWrap = currentSelected[0].parentNode;
var nextWrap = currentSelectedWrap[next ? 'nextSibling' : 'previousSibling'];
currentSelected.removeClass('composer_emoji_option_active');
currentSelected.removeClass('composer_autocomplete_option_active');
if (nextWrap) {
$(nextWrap).find('a').addClass('composer_emoji_option_active');
$(nextWrap).find('a').addClass('composer_autocomplete_option_active');
return cancelEvent(e);
}
}
var childNodes = this.autoCompleteEl[0].childNodes;
var nextWrap = childNodes[next ? 0 : childNodes.length - 1];
$(nextWrap).find('a').addClass('composer_emoji_option_active');
$(nextWrap).find('a').addClass('composer_autocomplete_option_active');
return cancelEvent(e);
}
if (e.keyCode == 13) { // ENTER
var currentSelected = $(this.autoCompleteEl).find('.composer_emoji_option_active')/* ||
$(this.autoCompleteEl).childNodes[0].find('a')*/;
var code = currentSelected.attr('data-code');
if (code) {
var currentSelected = $(this.autoCompleteEl).find('.composer_autocomplete_option_active');
var code, mention;
if (code = currentSelected.attr('data-code')) {
this.onEmojiSelected(code, true);
EmojiHelper.pushPopularEmoji(code);
return cancelEvent(e);
}
if (mention = currentSelected.attr('data-mention')) {
if (this.onMentionSelected) {
this.onMentionSelected(mention);
return cancelEvent(e);
}
}
checkSubmit = true;
}
}
@ -638,38 +650,65 @@ MessageComposer.prototype.checkAutocomplete = function () { @@ -638,38 +650,65 @@ MessageComposer.prototype.checkAutocomplete = function () {
value = value.substr(0, pos);
var matches = value.match(/(?:\s|^):([A-Za-z0-9\-\+\*_]*)$/);
var matches = value.match(this.autoCompleteRegEx);
if (matches) {
if (this.previousQuery == matches[0]) {
return;
}
this.previousQuery = matches[0];
var query = SearchIndexManager.cleanSearchText(matches[1]);
EmojiHelper.getPopularEmoji((function (popular) {
if (query.length) {
var found = EmojiHelper.searchEmojis(query);
if (found.length) {
var popularFound = [],
code, pos;
for (var i = 0, len = popular.length; i < len; i++) {
code = popular[i].code;
pos = found.indexOf(code);
if (pos >= 0) {
popularFound.push(code);
found.splice(pos, 1);
if (!found.length) {
break;
}
var query = SearchIndexManager.cleanSearchText(matches[2]);
if (matches[1] == '@') { // mentions
if (this.mentions && this.mentions.index) {
if (query.length) {
var foundObject = SearchIndexManager.search(query, this.mentions.index);
var foundUsers = [];
var user;
for (var i = 0, length = this.mentions.users.length; i < length; i++) {
user = this.mentions.users[i];
if (foundObject[user.id]) {
foundUsers.push(user);
}
}
this.showEmojiSuggestions(popularFound.concat(found));
} else {
var foundUsers = this.mentions.users;
}
if (foundUsers.length) {
this.showMentionSuggestions(foundUsers);
} else {
this.hideSuggestions();
}
} else {
this.showEmojiSuggestions(popular);
this.hideSuggestions();
}
}).bind(this));
}
else { // emoji
EmojiHelper.getPopularEmoji((function (popular) {
if (query.length) {
var found = EmojiHelper.searchEmojis(query);
if (found.length) {
var popularFound = [],
code, pos;
for (var i = 0, len = popular.length; i < len; i++) {
code = popular[i].code;
pos = found.indexOf(code);
if (pos >= 0) {
popularFound.push(code);
found.splice(pos, 1);
if (!found.length) {
break;
}
}
}
this.showEmojiSuggestions(popularFound.concat(found));
} else {
this.hideSuggestions();
}
} else {
this.showEmojiSuggestions(popular);
}
}).bind(this));
}
}
else {
delete this.previousQuery;
@ -819,6 +858,65 @@ MessageComposer.prototype.onEmojiSelected = function (code, autocomplete) { @@ -819,6 +858,65 @@ MessageComposer.prototype.onEmojiSelected = function (code, autocomplete) {
this.onChange();
}
MessageComposer.prototype.onMentionsUpdated = function (username) {
delete this.previousQuery;
if (this.isActive) {
this.checkAutocomplete();
}
}
MessageComposer.prototype.onMentionSelected = function (username) {
if (this.richTextareaEl) {
var textarea = this.richTextareaEl[0];
if (!this.isActive) {
if (!this.restoreSelection()) {
setRichFocus(textarea);
}
}
var valueCaret = getRichValueWithCaret(textarea);
var fullValue = valueCaret[0];
var pos = valueCaret[1] >= 0 ? valueCaret[1] : fullValue.length;
var suffix = fullValue.substr(pos);
var prefix = fullValue.substr(0, pos);
var matches = prefix.match(/@([A-Za-z0-9\-\+\*_]*)$/);
var newValuePrefix;
if (matches && matches[0]) {
newValuePrefix = prefix.substr(0, matches.index) + '@' + username;
} else {
newValuePrefix = prefix + '@' + username;
}
textarea.value = newValue;
this.selId = (this.selId || 0) + 1;
var html = this.getRichHtml(newValuePrefix) + '&nbsp;<span id="composer_sel' + this.selId + '"></span>' + this.getRichHtml(suffix);
this.richTextareaEl.html(html);
setRichFocus(textarea, $('#composer_sel' + this.selId)[0]);
}
else {
var textarea = this.textareaEl[0];
var fullValue = textarea.value;
var pos = this.isActive ? getFieldSelection(textarea) : fullValue.length;
var suffix = fullValue.substr(pos);
var prefix = fullValue.substr(0, pos);
var matches = prefix.match(/@([A-Za-z0-9\-\+\*_]*)$/);
if (matches && matches[0]) {
var newValue = prefix.substr(0, matches.index) + '@' + username + ' ' + suffix;
var newPos = matches.index + username.length + 2;
} else {
var newValue = prefix + ':' + username + ': ' + suffix;
var newPos = prefix.length + username.length + 2;
}
textarea.value = newValue;
setFieldSelection(textarea, newPos);
}
this.hideSuggestions();
this.onChange();
}
MessageComposer.prototype.onChange = function (e) {
if (this.richTextareaEl) {
delete this.keyupStarted;
@ -850,7 +948,7 @@ MessageComposer.prototype.setValue = function (text) { @@ -850,7 +948,7 @@ MessageComposer.prototype.setValue = function (text) {
}
MessageComposer.prototype.getRichHtml = function (text) {
return $('<div>').text(text).html().replace(/:([A-Za-z0-9\-\+\*_]+?):/gi, (function (all, shortcut) {
return $('<div>').text(text).html().replace(/\n/g, '<br/>').replace(/:([A-Za-z0-9\-\+\*_]+?):/gi, (function (all, shortcut) {
var code = EmojiHelper.shortcuts[shortcut];
if (code !== undefined) {
return this.getEmojiHtml(code);
@ -900,6 +998,23 @@ MessageComposer.prototype.showEmojiSuggestions = function (codes) { @@ -900,6 +998,23 @@ MessageComposer.prototype.showEmojiSuggestions = function (codes) {
this.autocompleteShown = true;
}
MessageComposer.prototype.showMentionSuggestions = function (users) {
var html = [];
var user;
var count = Math.min(5, users.length);
var i;
for (i = 0; i < count; i++) {
user = users[i];
html.push('<li><a class="composer_mention_option" data-mention="' + user.username + '"><span class="composer_user_name">' + user.rFullName + '</span><span class="composer_user_mention">@' + user.username + '</span></a></li>');
}
this.autoCompleteEl.html(html.join(''));
this.autoCompleteEl.show();
this.updatePosition();
this.autocompleteShown = true;
}
MessageComposer.prototype.updatePosition = function () {
var offset = (this.richTextareaEl || this.textareaEl).offset();
var height = this.autoCompleteEl.outerHeight();

513
app/js/services.js

@ -111,7 +111,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) @@ -111,7 +111,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
if (apiUser.first_name) {
apiUser.rFirstName = RichTextProcessor.wrapRichText(apiUser.first_name, {noLinks: true, noLinebreaks: true});
apiUser.rFullName = RichTextProcessor.wrapRichText(apiUser.first_name + ' ' + (apiUser.last_name || ''), {noLinks: true, noLinebreaks: true});
apiUser.rFullName = apiUser.last_name ? RichTextProcessor.wrapRichText(apiUser.first_name + ' ' + (apiUser.last_name || ''), {noLinks: true, noLinebreaks: true}) : apiUser.rFirstName;
} else {
apiUser.rFirstName = RichTextProcessor.wrapRichText(apiUser.last_name, {noLinks: true, noLinebreaks: true}) || apiUser.rPhone || _('user_first_name_deleted');
apiUser.rFullName = RichTextProcessor.wrapRichText(apiUser.last_name, {noLinks: true, noLinebreaks: true}) || apiUser.rPhone || _('user_name_deleted');
@ -411,7 +411,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) @@ -411,7 +411,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
break;
case 'updateContactLink':
onContactUpdated(update.user_id, update.my_link._ == 'contacts.myLinkContact');
onContactUpdated(update.user_id, update.my_link._ == 'contactLinkContact');
break;
}
});
@ -567,7 +567,9 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) @@ -567,7 +567,9 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
function getChatFull(id) {
if (chatsFull[id] !== undefined) {
return $q.when(chatsFull[id]);
if (chats[id].version == chatsFull[id].participants.version) {
return $q.when(chatsFull[id]);
}
}
if (chatFullPromises[id] !== undefined) {
return chatFullPromises[id];
@ -653,13 +655,48 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) @@ -653,13 +655,48 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
// console.log('on apiUpdate', update);
switch (update._) {
case 'updateChatParticipants':
var participants = update.participants;
var chatFull = chatsFull[participants.id];
var participants = update.participants;
var chatFull = chatsFull[participants.id];
if (chatFull !== undefined) {
chatFull.participants = update.participants;
$rootScope.$broadcast('chat_full_update', chatID);
}
break;
case 'updateChatParticipantAdd':
var chatFull = chatsFull[update.chat_id];
if (chatFull !== undefined) {
var participants = chatFull.participants.participants || [];
for (var i = 0, length = participants.length; i < length; i++) {
if (participants[i].user_id == update.user_id) {
return;
}
}
participants.push({
_: 'chatParticipant',
user_id: update.user_id,
inviter_id: update.inviter_id,
date: tsNow(true)
});
chatFull.participants.version = update.version;
$rootScope.$broadcast('chat_full_update', update.chat_id);
}
break;
case 'updateChatParticipantDelete':
var chatFull = chatsFull[update.chat_id];
if (chatFull !== undefined) {
var participants = chatFull.participants.participants || [];
for (var i = 0, length = participants.length; i < length; i++) {
if (participants[i].user_id == update.user_id) {
participants.splice(i, 1);
chatFull.participants.version = update.version;
$rootScope.$broadcast('chat_full_update', update.chat_id);
return;
}
}
}
break;
}
});
@ -764,6 +801,9 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) @@ -764,6 +801,9 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
var lastSearchFilter = {},
lastSearchResults = [];
var needSingleMessages = [],
fetchSingleMessagesTimeout = false;
var serverTimeOffset = 0,
timestampNow = tsNow(true),
midnightNoOffset = timestampNow - (timestampNow % 86400),
@ -886,8 +926,9 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) @@ -886,8 +926,9 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
dialog.top_message > maxSeenID
) {
var message = getMessage(dialog.top_message);
var notifyPeer = message.flags & 16 ? message.from_id : peerID;
if (message.unread && !message.out) {
NotificationsManager.getPeerMuted(peerID).then(function (muted) {
NotificationsManager.getPeerMuted(notifyPeer).then(function (muted) {
if (!muted) {
Storage.get('notify_nopreview').then(function (no_preview) {
notifyAboutMessage(message, no_preview);
@ -1183,23 +1224,28 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) @@ -1183,23 +1224,28 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
function deleteMessages (messageIDs) {
return MtpApiManager.invokeApi('messages.deleteMessages', {
id: messageIDs
}).then(function (deletedMessageIDs) {
}).then(function (affectedMessages) {
ApiUpdatesManager.processUpdateMessage({
_: 'updateShort',
update: {
_: 'updateDeleteMessages',
messages: deletedMessageIDs
messages: messageIDs,
pts: affectedMessages.pts,
pts_count: affectedMessages.pts_count
}
});
return deletedMessageIDs;
return messageIDs;
});
}
function processAffectedHistory (inputPeer, affectedHistory, method) {
if (!ApiUpdatesManager.processUpdateMessage({
_: 'updates',
seq: affectedHistory.seq,
updates: []
_: 'updateShort',
update: {
_: 'updatePts',
pts: affectedHistory.pts,
pts_count: affectedHistory.pts_count
}
})) {
return false;
}
@ -1210,8 +1256,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) @@ -1210,8 +1256,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
return MtpApiManager.invokeApi(method, {
peer: inputPeer,
offset: affectedHistory.offset,
max_id: 0,
read_contents: true
max_id: 0
}).then(function (affectedHistory) {
return processAffectedHistory(inputPeer, affectedHistory, method);
});
@ -1253,8 +1298,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) @@ -1253,8 +1298,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
historyStorage.readPromise = MtpApiManager.invokeApi('messages.readHistory', {
peer: inputPeer,
offset: 0,
max_id: 0,
read_contents: true
max_id: 0
}).then(function (affectedHistory) {
return processAffectedHistory(inputPeer, affectedHistory, 'messages.readHistory');
}).then(function () {
@ -1334,9 +1378,6 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) @@ -1334,9 +1378,6 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
case 'messageMediaAudio':
AppAudioManager.saveAudio(apiMessage.media.audio);
break;
case 'messageMediaUnsupported':
delete apiMessage.media.bytes;
break;
}
}
if (apiMessage.action && apiMessage.action._ == 'messageActionChatEditPhoto') {
@ -1345,15 +1386,18 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) @@ -1345,15 +1386,18 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
});
}
function sendText(peerID, text) {
function sendText(peerID, text, options) {
if (!angular.isString(text) || !text.length) {
return;
}
options = options || {};
var messageID = tempID--,
randomID = [nextRandomInt(0xFFFFFFFF), nextRandomInt(0xFFFFFFFF)],
randomIDS = bigint(randomID[0]).shiftLeft(32).add(bigint(randomID[1])).toString(),
historyStorage = historiesStorage[peerID],
inputPeer = AppPeersManager.getInputPeerByID(peerID),
flags = 0,
replyToMsgID = options.replyToMsgID,
message;
if (historyStorage === undefined) {
@ -1361,15 +1405,22 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) @@ -1361,15 +1405,22 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
}
MtpApiManager.getUserID().then(function (fromID) {
if (peerID != fromID) {
flags |= 3;
}
if (replyToMsgID) {
flags |= 8;
}
message = {
_: 'message',
id: messageID,
from_id: fromID,
to_id: AppPeersManager.getOutputPeer(peerID),
flags: peerID == fromID ? 0 : 3,
flags: flags,
date: tsNow(true) + serverTimeOffset,
message: text,
random_id: randomIDS,
reply_to_msg_id: replyToMsgID,
pending: true
};
@ -1398,7 +1449,8 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) @@ -1398,7 +1449,8 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
MtpApiManager.invokeApi('messages.sendMessage', {
peer: inputPeer,
message: text,
random_id: randomID
random_id: randomID,
reply_to_msg_id: replyToMsgID
}, sentRequestOptions).then(function (sentMessage) {
message.date = sentMessage.date;
message.id = sentMessage.id;
@ -1407,7 +1459,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) @@ -1407,7 +1459,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
_: 'updates',
users: [],
chats: [],
seq: sentMessage.seq,
seq: 0,
updates: [{
_: 'updateMessageID',
random_id: randomIDS,
@ -1415,7 +1467,8 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) @@ -1415,7 +1467,8 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
}, {
_: 'updateNewMessage',
message: message,
pts: sentMessage.pts
pts: sentMessage.pts,
pts_count: sentMessage.pts_count
}]
});
}, function (error) {
@ -1448,6 +1501,8 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) @@ -1448,6 +1501,8 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
randomIDS = bigint(randomID[0]).shiftLeft(32).add(bigint(randomID[1])).toString(),
historyStorage = historiesStorage[peerID],
inputPeer = AppPeersManager.getInputPeerByID(peerID),
flags = 0,
replyToMsgID = options.replyToMsgID,
attachType, apiFileName, realFileName;
if (!options.isMedia) {
@ -1472,6 +1527,12 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) @@ -1472,6 +1527,12 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
}
MtpApiManager.getUserID().then(function (fromID) {
if (peerID != fromID) {
flags |= 3;
}
if (replyToMsgID) {
flags |= 8;
}
var media = {
_: 'messageMediaPending',
type: attachType,
@ -1485,11 +1546,12 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) @@ -1485,11 +1546,12 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
id: messageID,
from_id: fromID,
to_id: AppPeersManager.getOutputPeer(peerID),
flags: peerID == fromID ? 0 : 3,
flags: flags,
date: tsNow(true) + serverTimeOffset,
message: '',
media: media,
random_id: randomIDS,
reply_to_msg_id: replyToMsgID,
pending: true
};
@ -1542,7 +1604,8 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) @@ -1542,7 +1604,8 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
MtpApiManager.invokeApi('messages.sendMedia', {
peer: inputPeer,
media: inputMedia,
random_id: randomID
random_id: randomID,
reply_to_msg_id: replyToMsgID
}).then(function (statedMessage) {
message.date = statedMessage.message.date;
message.id = statedMessage.message.id;
@ -1552,7 +1615,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) @@ -1552,7 +1615,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
_: 'updates',
users: statedMessage.users,
chats: statedMessage.chats,
seq: statedMessage.seq,
seq: 0,
updates: [{
_: 'updateMessageID',
random_id: randomIDS,
@ -1560,7 +1623,8 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) @@ -1560,7 +1623,8 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
}, {
_: 'updateNewMessage',
message: message,
pts: statedMessage.pts
pts: statedMessage.pts,
pts_count: statedMessage.pts_count
}]
});
}, function (error) {
@ -1667,7 +1731,8 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) @@ -1667,7 +1731,8 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
MtpApiManager.invokeApi('messages.sendMedia', {
peer: inputPeer,
media: inputMedia,
random_id: randomID
random_id: randomID,
reply_to_msg_id: 0
}).then(function (statedMessage) {
message.date = statedMessage.message.date;
message.id = statedMessage.message.id;
@ -1677,7 +1742,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) @@ -1677,7 +1742,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
_: 'updates',
users: statedMessage.users,
chats: statedMessage.chats,
seq: statedMessage.seq,
seq: 0,
updates: [{
_: 'updateMessageID',
random_id: randomIDS,
@ -1685,7 +1750,8 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) @@ -1685,7 +1750,8 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
}, {
_: 'updateNewMessage',
message: message,
pts: statedMessage.pts
pts: statedMessage.pts,
pts_count: statedMessage.pts_count
}]
});
}, function (error) {
@ -1706,16 +1772,25 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) @@ -1706,16 +1772,25 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
function forwardMessages (peerID, msgIDs) {
msgIDs = msgIDs.sort();
var randomIDs = [];
var i;
var len = msgIDs.length;
for (var i = 0; i < msgIDs.length; i++) {
randomIDs.push([nextRandomInt(0xFFFFFFFF), nextRandomInt(0xFFFFFFFF)]);
}
return MtpApiManager.invokeApi('messages.forwardMessages', {
peer: AppPeersManager.getInputPeerByID(peerID),
id: msgIDs
id: msgIDs,
random_id: randomIDs
}).then(function (statedMessages) {
var updates = [];
angular.forEach(statedMessages.messages, function(apiMessage) {
updates.push({
_: 'updateNewMessage',
message: apiMessage,
pts: statedMessages.pts
pts: statedMessages.pts,
pts_count: statedMessages.pts_count
});
});
@ -1723,7 +1798,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) @@ -1723,7 +1798,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
_: 'updates',
users: statedMessages.users,
chats: statedMessages.chats,
seq: statedMessages.seq,
seq: 0,
updates: updates
});
});
@ -1817,11 +1892,12 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) @@ -1817,11 +1892,12 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
_: 'updates',
users: statedMessage.users,
chats: statedMessage.chats,
seq: statedMessage.seq,
seq: 0,
updates: [{
_: 'updateNewMessage',
message: statedMessage.message,
pts: statedMessage.pts
pts: statedMessage.pts,
pts_count: statedMessage.pts_count
}]
});
}
@ -1924,6 +2000,21 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) @@ -1924,6 +2000,21 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
}
}
var replyToMsgID = message.reply_to_msg_id;
if (replyToMsgID) {
if (messagesStorage[replyToMsgID]) {
message.reply_to_msg = wrapForHistory(replyToMsgID);
} else {
message.reply_to_msg = {id: replyToMsgID, loading: true};
if (needSingleMessages.indexOf(replyToMsgID) == -1) {
needSingleMessages.push(replyToMsgID);
if (fetchSingleMessagesTimeout === false) {
fetchSingleMessagesTimeout = setTimeout(fetchSingleMessages, 100);
}
}
}
}
if (message.message && message.message.length) {
var options = {};
if (!Config.Navigator.mobile) {
@ -1938,6 +2029,27 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) @@ -1938,6 +2029,27 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
return messagesForHistory[msgID] = message;
}
function fetchSingleMessages () {
if (fetchSingleMessagesTimeout !== false) {
clearTimeout(fetchSingleMessagesTimeout);
fetchSingleMessagesTimeout = false;
}
if (!needSingleMessages.length) {
return;
}
var msgIDs = 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);
})
}
function regroupWrappedHistory (history, limit) {
if (!history || !history.length) {
return false;
@ -1990,7 +2102,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) @@ -1990,7 +2102,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
!curMessage.action &&
curMessage.date < prevMessage.date + 900) {
var singleLine = curMessage.message && curMessage.message.length < 70 && curMessage.message.indexOf("\n") == -1;
var singleLine = curMessage.message && curMessage.message.length < 70 && curMessage.message.indexOf("\n") == -1 && !curMessage.reply_to_msg_id;
if (groupFwd && curMessage.fwd_from_id && curMessage.fwd_from_id == prevMessage.fwd_from_id) {
curMessage.grouped = singleLine ? 'im_grouped_fwd_short' : 'im_grouped_fwd';
} else {
@ -2298,6 +2410,9 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) @@ -2298,6 +2410,9 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
}
break;
// case 'updateReadHistoryInbox':
// case 'updateReadHistoryOutbox':
case 'updateDeleteMessages':
var dialogsUpdated = {},
historiesUpdated = {},
@ -2858,7 +2973,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) @@ -2858,7 +2973,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
break;
case 'documentAttributeSticker':
apiDoc.sticker = 1;
var stickerEmoji = EmojiHelper.stickers[apiDoc.id];
var stickerEmoji = attribute.alt || EmojiHelper.stickers[apiDoc.id];
if (stickerEmoji !== undefined) {
apiDoc.sticker = 2;
apiDoc.stickerEmoji = RichTextProcessor.wrapRichText(stickerEmoji, {noLinks: true, noLinebreaks: true});
@ -3270,39 +3385,106 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) @@ -3270,39 +3385,106 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
.service('ApiUpdatesManager', function ($rootScope, MtpNetworkerFactory, AppUsersManager, AppChatsManager, AppPeersManager, MtpApiManager) {
var isSynchronizing = true,
getDifferencePending = false,
curState = {},
pendingUpdates = {};
var curState = {};
var myID = 0;
MtpApiManager.getUserID().then(function (id) {
myID = id;
});
var syncPending = false;
var syncLoading = true;
var pendingSeqUpdates = {};
var pendingPtsUpdates = [];
function popPendingUpdate () {
function popPendingSeqUpdate () {
var nextSeq = curState.seq + 1,
updateMessage = pendingUpdates[nextSeq];
if (updateMessage) {
console.log(dT(), 'pop pending update', nextSeq, updateMessage);
if (processUpdateMessage(updateMessage)) {
delete pendingUpdates[nextSeq];
pendingUpdatesData = pendingSeqUpdates[nextSeq];
if (!pendingUpdatesData) {
return false;
}
var updates = pendingUpdatesData.updates;
var i, length;
for (var i = 0, length = updates.length; i < length; i++) {
saveUpdate(updates[i]);
}
curState.seq = pendingUpdatesData.seq;
if (pendingUpdatesData.date && curState.date < pendingUpdatesData.date) {
curState.date = pendingUpdatesData.date;
}
delete pendingSeqUpdates[nextSeq];
if (!popPendingSeqUpdate() &&
syncPending &&
syncPending.seqAwaiting &&
curState.seq >= syncPending.seqAwaiting) {
if (!syncPending.ptsAwaiting) {
clearTimeout(syncPending.timeout);
syncPending = false;
} else {
delete syncPending.seqAwaiting;
}
}
return true;
}
function forceGetDifference () {
if (!isSynchronizing) {
getDifference();
function popPendingPtsUpdate () {
if (!pendingPtsUpdates.length) {
return false;
}
}
pendingPtsUpdates.sort(function (a, b) {
return a.pts - b.pts;
});
function processUpdateMessage (updateMessage) {
if (updateMessage.seq) {
if (!saveSeq(updateMessage.seq, updateMessage.seq_start)) {
pendingUpdates[updateMessage.seq_start || updateMessage.seq] = updateMessage;
return false;
var curPts = curState.pts;
var goodPts = false;
var goodIndex = false;
var update;
for (var i = 0, length = pendingPtsUpdates.length; i < length; i++) {
update = pendingPtsUpdates[i];
curPts += update.pts_count;
if (curPts >= update.pts) {
goodPts = update.pts;
goodIndex = i;
}
if (updateMessage.date) {
curState.date = updateMessage.date;
}
if (!goodPts) {
return false;
}
curState.pts = goodPts;
for (i = 0; i <= goodIndex; i++) {
update = pendingPtsUpdates[i];
saveUpdate(update);
}
pendingPtsUpdates.splice(goodIndex, length - goodIndex);
if (!pendingPtsUpdates.length && syncPending) {
if (!syncPending.seqAwaiting) {
clearTimeout(syncPending.timeout);
syncPending = false;
} else {
delete syncPending.ptsAwaiting;
}
}
return true;
}
function forceGetDifference () {
if (!syncLoading) {
getDifference();
}
}
function processUpdateMessage (updateMessage) {
var processOpts = {
date: updateMessage.date,
seq: updateMessage.seq,
seqStart: updateMessage.seq_start
};
switch (updateMessage._) {
case 'updatesTooLong':
@ -3311,51 +3493,35 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) @@ -3311,51 +3493,35 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
break;
case 'updateShort':
saveUpdate(updateMessage.update);
processUpdate(updateMessage.update, processOpts);
break;
case 'updateShortMessage':
if (!AppUsersManager.hasUser(updateMessage.from_id)) {
console.log('User not found', updateMessage.from_id, 'getDiff');
forceGetDifference();
break;
}
saveUpdate({
_: 'updateNewMessage',
message: {
_: 'message',
id: updateMessage.id,
from_id: updateMessage.from_id,
to_id: AppPeersManager.getOutputPeer(MtpApiManager.getUserID()),
flags: 1,
date: updateMessage.date,
message: updateMessage.message
},
pts: updateMessage.pts
});
break;
case 'updateShortChatMessage':
if (!AppUsersManager.hasUser(updateMessage.from_id) ||
!AppChatsManager.hasChat(updateMessage.chat_id)) {
console.log('User or chat not found', updateMessage.from_id, updateMessage.chat_id, 'getDiff');
forceGetDifference();
break;
}
saveUpdate({
var isOut = updateMessage.flags & 2;
var fromID = updateMessage.from_id || (isOut ? myID : updateMessage.user_id);
var toID = updateMessage.chat_id
? -updateMessage.chat_id
: (isOut ? updateMessage.user_id : myID);
processUpdate({
_: 'updateNewMessage',
message: {
_: 'message',
flags: updateMessage.flags,
id: updateMessage.id,
from_id: updateMessage.from_id,
to_id: AppPeersManager.getOutputPeer(-updateMessage.chat_id),
flags: 1,
from_id: fromID,
to_id: AppPeersManager.getOutputPeer(toID),
date: updateMessage.date,
message: updateMessage.message
message: updateMessage.message,
fwd_from_id: updateMessage.fwd_from_id,
fwd_date: updateMessage.fwd_date,
reply_to_msg_id: updateMessage.reply_to_msg_id,
},
pts: updateMessage.pts
});
pts: updateMessage.pts,
pts_count: updateMessage.pts_count
}, processOpts);
break;
case 'updatesCombined':
@ -3363,52 +3529,26 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) @@ -3363,52 +3529,26 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
AppUsersManager.saveApiUsers(updateMessage.users);
AppChatsManager.saveApiChats(updateMessage.chats);
var i, update, message;
for (var i = 0; i < updateMessage.updates.length; i++) {
update = updateMessage.updates[i];
switch (update._) {
case 'updateNewMessage':
message = update.message;
if (message.from_id && !AppUsersManager.hasUser(message.from_id)) {
console.log('User not found', message.from_id, 'getDiff');
forceGetDifference();
return false;
}
if (message.to_id.chat_id && !AppChatsManager.hasChat(message.to_id.chat_id)) {
console.log('Chat not found', message.to_id.chat_id, 'getDiff');
forceGetDifference();
return false;
}
break;
}
}
angular.forEach(updateMessage.updates, function (update) {
saveUpdate(update);
processUpdate(update, processOpts);
});
break;
default:
console.warn(dT(), 'Unknown update message', updateMessage);
}
popPendingUpdate();
if (getDifferencePending && curState.seq >= getDifferencePending.seqAwaiting) {
console.log(dT(), 'cancel pending getDiff', getDifferencePending.seqAwaiting);
clearTimeout(getDifferencePending.timeout);
getDifferencePending = false;
}
return true;
}
function getDifference () {
isSynchronizing = true;
if (!syncLoading) {
syncLoading = true;
pendingSeqUpdates = {};
pendingPtsUpdates = [];
}
if (getDifferencePending) {
clearTimeout(getDifferencePending.timeout);
getDifferencePending = false;
if (syncPending) {
clearTimeout(syncPending.timeout);
syncPending = false;
}
MtpApiManager.invokeApi('updates.getDifference', {pts: curState.pts, date: curState.date, qts: 0}).then(function (differenceResult) {
@ -3416,8 +3556,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) @@ -3416,8 +3556,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
console.log(dT(), 'apply empty diff', differenceResult.seq);
curState.date = differenceResult.date;
curState.seq = differenceResult.seq;
isSynchronizing = false;
popPendingUpdate();
syncLoading = false;
return false;
}
@ -3433,7 +3572,8 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) @@ -3433,7 +3572,8 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
saveUpdate({
_: 'updateNewMessage',
message: apiMessage,
pts: curState.pts
pts: curState.pts,
pts_count: 0
});
});
@ -3445,55 +3585,100 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) @@ -3445,55 +3585,100 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
console.log(dT(), 'apply diff', curState.seq, curState.pts);
if (differenceResult._ == 'updates.differenceSlice') {
getDifference(true);
getDifference();
} else {
isSynchronizing = false;
syncLoading = false;
}
});
}
function saveUpdate (update) {
if (update.pts) {
curState.pts = update.pts;
function processUpdate (update, options) {
if (syncLoading) {
return false;
}
$rootScope.$broadcast('apiUpdate', update);
}
function saveSeq (seq, seqStart) {
seqStart = seqStart || seq;
if (!seqStart) {
return true;
if (update._ == 'updateNewMessage') {
var message = update.message;
if (message.from_id && !AppUsersManager.hasUser(message.from_id) ||
message.fwd_from_id && !AppUsersManager.hasUser(message.fwd_from_id) ||
message.to_id.user_id && !AppUsersManager.hasUser(message.to_id.user_id) ||
message.to_id.chat_id && !AppChatsManager.hasChat(message.to_id.chat_id)) {
console.warn(dT(), 'Short update not enough data', message);
forceGetDifference();
return false;
}
}
if (isSynchronizing) {
console.log(dT(), 'Seq decline', seqStart);
return false;
}
var popPts, popSeq;
if (seqStart != curState.seq + 1) {
if (seqStart > curState.seq) {
console.warn(dT(), 'Seq hole', seqStart, getDifferencePending && getDifferencePending.seqAwaiting);
if (!getDifferencePending) {
getDifferencePending = {
seqAwaiting: seqStart,
if (update.pts) {
var newPts = curState.pts + (update.pts_count || 0);
if (newPts < update.pts) {
console.log(dT(), 'Pts hole', curState, update);
pendingPtsUpdates.push(update);
if (!syncPending) {
syncPending = {
timeout: setTimeout(function () {
getDifference();
}, 5000)
};
}
syncPending.ptsAwaiting = true;
return false;
}
return false;
} else {
// console.log(dT(), 'Seq apply', seqStart);
curState.pts = update.pts;
popPts = true;
}
else if (options.seq > 0) {
var seq = options.seq;
var seqStart = options.seqStart || seq;
curState.seq = seq;
if (seqStart != curState.seq + 1) {
if (seqStart > curState.seq) {
console.warn(dT(), 'Seq hole', curState, syncPending && syncPending.seqAwaiting);
return true;
if (pendingSeqUpdates[seqStart] === undefined) {
pendingSeqUpdates[seqStart] = {seq: seq, date: options.date, updates: []};
}
pendingSeqUpdates[seqStart].updates.push(update);
if (!syncPending) {
syncPending = {
timeout: setTimeout(function () {
getDifference();
}, 5000)
};
}
if (!syncPending.seqAwaiting ||
syncPending.seqAwaiting < seqStart) {
syncPending.seqAwaiting = seqStart;
}
return false;
}
}
if (curState.seq != seq) {
curState.seq = seq;
if (options.date && curState.date < options.date) {
curState.date = options.date;
}
popSeq = true;
}
}
saveUpdate (update);
if (popPts) {
popPendingPtsUpdate();
}
else if (popSeq) {
popPendingSeqUpdate();
}
}
function saveUpdate (update) {
$rootScope.$broadcast('apiUpdate', update);
}
function attach () {
@ -3503,7 +3688,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) @@ -3503,7 +3688,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
curState.pts = stateResult.pts;
curState.date = stateResult.date;
setTimeout(function () {
isSynchronizing = false;
syncLoading = false;
}, 1000);
})
}
@ -3734,6 +3919,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) @@ -3734,6 +3919,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
else if (match[10]) {
if (!options.noLinks) {
html.push(
encodeEntities(match[9]),
'<a href="#/im?q=',
encodeURIComponent(match[10]),
'">',
@ -3742,6 +3928,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) @@ -3742,6 +3928,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
);
} else {
html.push(
encodeEntities(match[9]),
encodeEntities(match[10])
);
}

2
app/partials/desktop/head.html

@ -52,7 +52,7 @@ @@ -52,7 +52,7 @@
</ul>
</div>
<a class="tg_head_btn tg_head_peer_return_btn" ng-show="historyFilter.mediaType.length || skippedHistory" ng-click="returnToRecent()" ng-switch="skippedHistory">
<a class="tg_head_btn tg_head_peer_return_btn" ng-show="historyFilter.mediaType.length || historyState.skipped" ng-click="returnToRecent()" ng-switch="historyState.skipped">
<span ng-switch-when="true" my-i18n="im_show_recent_messages"></span>
<span ng-switch-default my-i18n="im_show_all_messages"></span>
<strong class="tg_head_peer_return_count" ng-show="historyState.missedCount > 0" ng-bind="'+' + historyState.missedCount"></strong>

18
app/partials/desktop/im.html

@ -142,14 +142,11 @@ @@ -142,14 +142,11 @@
<div class="im_edit_panel_wrap clearfix" ng-show="historyState.selectActions">
<div class="im_edit_panel_border"></div>
<a class="btn btn-md btn-md-primary im_edit_flush_link" ng-click="selectedFlush()" ng-switch="historyPeer.id > 0">
<span ng-switch-when="true" my-i18n="im_delete_chat"></span>
<span ng-switch-default my-i18n="im_clear_history"></span>
</a>
<a class="btn btn-md im_edit_cancel_link" ng-click="selectedCancel()" my-i18n="modal_cancel"></a>
<a class="btn btn-md btn-md-primary im_edit_cancel_link" ng-click="selectedCancel()" my-i18n="modal_cancel"></a>
<div class="im_edit_selected_actions" 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>
<a class="btn btn-primary im_edit_delete_btn" ng-click="selectedDelete()" ng-class="{disabled: !selectedCount}" ng-disabled="!selectedCount" my-i18n-format="im_delete"></a>
<a class="btn btn-primary im_edit_reply_btn" ng-click="selectedReply()" ng-show="selectedCount == 1"my-i18n="im_reply"></a>
<my-i18n-param name="count"><strong class="im_selected_count" ng-show="selectedCount > 0" ng-bind="selectedCount"></strong></my-i18n-param>
</div>
</div>
@ -158,14 +155,19 @@ @@ -158,14 +155,19 @@
<div class="im_send_form_wrap1">
<div class="im_send_form_wrap clearfix" ng-controller="AppImSendController">
<div class="im_send_form_wrap clearfix" ng-controller="AppImSendController" ng-class="{im_send_reply_form_wrap: draftMessage.replyToMessage != null}">
<a class="pull-right im_panel_peer_photo" my-peer-photolink="historyPeer.id" img-class="im_panel_peer_photo" watch="true">
<i class="icon im_panel_peer_online" ng-show="historyPeer.id > 0 &amp;&amp; historyPeer.data.status._ == 'userStatusOnline'"></i>
</a>
<a class="pull-left im_panel_own_photo" my-peer-photolink="ownID" img-class="im_panel_own_photo" watch="true" ng-click="openSettings()" no-open="true"></a>
<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" 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-click="draftMessage.replyClear()"><i class="icon icon-reply-bar"></i><i class="icon icon-reply-bar"></i></a>
<div my-reply-message="draftMessage.replyToMessage"></div>
</div>
<div class="im_send_field_wrap">
<a class="composer_emoji_insert_btn"><i class="icon icon-emoji"></i></a>

6
app/partials/desktop/message.html

@ -19,7 +19,7 @@ @@ -19,7 +19,7 @@
</div>
<div ng-switch-default class="im_content_message_wrap" ng-class="::[historyMessage.out ? 'im_message_out' : 'im_message_in', historyMessage._ == 'messageForwarded' ? 'im_message_fwd' : '']">
<div ng-switch-default class="im_content_message_wrap" ng-class="::[historyMessage.out ? 'im_message_out' : 'im_message_in', historyMessage.fwd_from_id > 0 ? 'im_message_fwd' : '']">
<div class="im_content_message_select_area">
<i class="icon icon-select-tick"></i>
</div>
@ -41,7 +41,9 @@ @@ -41,7 +41,9 @@
<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>
<div ng-if="::historyMessage._ == 'messageForwarded' || false" class="im_message_fwd_from">
<a class="im_message_reply_wrap" my-reply-message="historyMessage.reply_to_msg" ng-if="::historyMessage.reply_to_msg_id"></a>
<div ng-if="::historyMessage.fwd_from_id > 0" class="im_message_fwd_from">
<a class="im_message_fwd_photo pull-left" my-user-photolink="historyMessage.fwd_from_id" img-class="im_message_fwd_photo"></a>
<div class="im_message_fwd_author_wrap">
<a class="im_message_fwd_author" my-user-link="historyMessage.fwd_from_id" short="true" no-watch="true"></a><span class="im_message_fwd_date" ng-bind="::historyMessage.fwd_date | dateOrTime"></span>

55
app/partials/desktop/reply_message.html

@ -0,0 +1,55 @@ @@ -0,0 +1,55 @@
<div class="im_message_reply clearfix" ng-class="{im_message_reply_thumbed: thumb != null}" ng-switch="replyMessage.loading">
<div class="im_message_reply_loading" ng-switch-when="true" my-i18n="im_reply_loading">
<my-i18n-param name="dots"><span my-loading-dots></span></my-i18n-param>
</div>
<div class="im_message_reply_thumb_wrap pull-left" ng-if="thumb != null">
<img
class="im_message_reply_thumb"
my-load-thumb
thumb="thumb"
/>
</div>
<div class="im_message_reply_author" ng-switch-default>
<span my-user-link="replyMessage.from_id"></span>
</div>
<div class="im_message_reply_body" ng-switch-default>
<span class="im_reply_message_media" ng-if="replyMessage.media" ng-switch="replyMessage.media._">
<span ng-switch-when="messageMediaPhoto" my-i18n="conversation_media_photo"></span>
<span ng-switch-when="messageMediaVideo" my-i18n="conversation_media_video"></span>
<span ng-switch-when="messageMediaDocument" ng-switch="::replyMessage.media.document.sticker || false">
<span ng-switch-when="1" my-i18n="conversation_media_sticker"></span>
<span ng-switch-when="2">
<span ng-bind-html="replyMessage.media.document.stickerEmoji"></span>
(<my-i18n msgid="conversation_media_sticker"></my-i18n>)
</span>
<span ng-switch-default ng-bind="replyMessage.media.document.file_name"></span>
</span>
<span ng-switch-when="messageMediaAudio" my-i18n="conversation_media_audio"></span>
<span ng-switch-when="messageMediaGeo" my-i18n="conversation_media_location"></span>
<span ng-switch-when="messageMediaContact" my-i18n="conversation_media_contact"></span>
</span>
<span class="im_reply_message_service" ng-if="replyMessage._ == 'messageService'" ng-switch="replyMessage.action._">
<span ng-switch-when="messageActionChatCreate" my-i18n="conversation_group_created"></span>
<span ng-switch-when="messageActionChatEditTitle" my-i18n="conversation_group_renamed"></span>
<span ng-switch-when="messageActionChatEditPhoto" my-i18n="conversation_group_photo_updated"></span>
<span ng-switch-when="messageActionChatDeletePhoto" my-i18n="conversation_group_photo_removed"></span>
<span ng-switch-when="messageActionChatAddUser" ng-switch="replyMessage.from_id == replyMessage.action.user_id">
<span ng-switch-when="true" my-i18n="conversation_returned_to_group"></span>
<span ng-switch-default my-i18n="conversation_invited_user">
<my-i18n-param name="user"><span my-user-link="replyMessage.action.user_id"></span></my-i18n-param>
</span>
</span>
<span ng-switch-when="messageActionChatDeleteUser" ng-switch="replyMessage.from_id == replyMessage.action.user_id">
<span ng-switch-when="true" my-i18n="conversation_left_group"></span>
<span ng-switch-default my-i18n="conversation_kicked_user">
<my-i18n-param name="user"><span my-user-link="replyMessage.action.user_id"></span></my-i18n-param>
</span>
</span>
</span>
<span class="im_reply_message_text" ng-if="replyMessage.message.length" ng-bind-html="replyMessage.richMessage"></span>
</div>
</div>

4
app/partials/mobile/message.html

@ -33,7 +33,9 @@ @@ -33,7 +33,9 @@
<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>
<div ng-if="::historyMessage._ == 'messageForwarded' &amp;&amp; !historyMessage.media" class="im_message_fwd_header" my-i18n="message_forwarded_message_mobile">
<a class="im_message_reply_wrap" my-reply-message="historyMessage.reply_to_msg" ng-if="::historyMessage.reply_to_msg_id"></a>
<div ng-if="::historyMessage.fwd_from_id > 0 &amp;&amp; !historyMessage.media" class="im_message_fwd_header" my-i18n="message_forwarded_message_mobile">
<a my-i18n-param="from" class="im_message_fwd_author" my-user-link="historyMessage.fwd_from_id" no-watch="true"></a>
<span my-i18n-param="date" class="im_message_fwd_date" ng-bind="::historyMessage.fwd_date | dateOrTime"></span>
</div>

2
webogram.sublime-project vendored

@ -4,7 +4,7 @@ @@ -4,7 +4,7 @@
{
"follow_symlinks": true,
"path": ".",
"folder_exclude_patterns": ["*dist", "node_modules", "releases"],
"folder_exclude_patterns": ["*dist", "node_modules", "releases", ".tx"],
"file_exclude_patterns": ["*.zip", "templates.js"]
}
]

Loading…
Cancel
Save