Browse Source

Added basic reply support

master
Igor Zhukov 10 years ago
parent
commit
4ef33ea806
  1. 64
      app/css/app.css
  2. 64
      app/css/desktop.css
  3. 15
      app/css/mobile.css
  4. 67
      app/js/controllers.js
  5. 82
      app/js/directives.js
  6. 1
      app/js/lib/utils.js
  7. 2
      app/js/locales/en-us.json
  8. 72
      app/js/services.js
  9. 2
      app/partials/desktop/head.html
  10. 16
      app/partials/desktop/im.html
  11. 2
      app/partials/desktop/message.html
  12. 55
      app/partials/desktop/reply_message.html
  13. 2
      app/partials/mobile/message.html

64
app/css/app.css

@ -1762,6 +1762,55 @@ div.im_message_body {
user-select: text; 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 { a.im_message_fwd_photo {
position: absolute; position: absolute;
margin-top: 1px; margin-top: 1px;
@ -2502,21 +2551,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 { .im_edit_panel_title {
text-align: center; text-align: center;
margin: 0; margin: 0;

64
app/css/desktop.css

@ -677,6 +677,17 @@ a.footer_link.active:active {
opacity: 1; 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 { .im_edit_panel_wrap {
padding: 0px 0 38px; padding: 0px 0 38px;
margin: 0 24px 0 12px; margin: 0 24px 0 12px;
@ -686,7 +697,6 @@ a.footer_link.active:active {
margin: 0 0 47px 3px; margin: 0 0 47px 3px;
border-bottom: 1px solid #EEE; border-bottom: 1px solid #EEE;
} }
.im_edit_flush_link,
.im_edit_cancel_link { .im_edit_cancel_link {
display: block; display: block;
padding: 7px 17px; padding: 7px 17px;
@ -695,14 +705,15 @@ a.footer_link.active:active {
margin: 6px 6px; margin: 6px 6px;
} }
.im_edit_cancel_link { .im_edit_cancel_link {
float: left;
}
.im_edit_flush_link {
float: right; float: right;
} }
.im_edit_selected_actions { .im_edit_selected_actions {
text-align: left;
text-transform: uppercase; text-transform: uppercase;
} }
.im_selected_count {
color: #b9cfe3;
}
.im_submit { .im_submit {
color: #499dd9; color: #499dd9;
@ -875,6 +886,7 @@ a.footer_link.active:active {
} }
} }
.im_message_fwd_author_wrap { .im_message_fwd_author_wrap {
margin: 1px 0 4px; margin: 1px 0 4px;
display: inline-block; display: inline-block;
@ -986,6 +998,7 @@ a.im_panel_peer_photo .peer_initials {
.im_send_field_wrap { .im_send_field_wrap {
margin-bottom: 15px; margin-bottom: 15px;
position: relative;
} }
.composer_rich_textarea, .composer_rich_textarea,
.composer_textarea { .composer_textarea {
@ -1076,6 +1089,49 @@ a.im_panel_peer_photo .peer_initials {
opacity: 1; 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 */ /* Peer modals */
.user_modal_window .modal-dialog { .user_modal_window .modal-dialog {
max-width: 480px; max-width: 480px;

15
app/css/mobile.css

@ -608,6 +608,14 @@ img.im_message_video_thumb,
margin-top: 0; margin-top: 0;
} }
.im_message_reply_wrap {
margin-top: 2px;
}
.im_message_reply_author {
font-weight: normal;
font-size: 13px;
}
.im_message_fwd_header { .im_message_fwd_header {
font-size: 12px; font-size: 12px;
} }
@ -624,7 +632,6 @@ img.im_message_video_thumb,
} }
.im_message_date { .im_message_date {
font-size: 10px; font-size: 10px;
/*font-size: 12px;*/
padding: 0; padding: 0;
} }
.im_message_out .im_message_meta { .im_message_out .im_message_meta {
@ -736,8 +743,14 @@ a.im_message_from_photo {
.contacts_modal_search_field { .contacts_modal_search_field {
font-size: 1.2em; font-size: 1.2em;
} }
.im_edit_selected_actions {
text-align: center;
}
.im_edit_delete_btn, .im_edit_delete_btn,
.im_edit_forward_btn { .im_edit_forward_btn {
border-radius: 2px;
font-weight: normal;
line-height: 18px;
background: none !important; background: none !important;
border: 0 !important; border: 0 !important;
width: 50%; width: 50%;

67
app/js/controllers.js

@ -349,7 +349,12 @@ angular.module('myApp.controllers', ['myApp.i18n'])
$scope.search = {}; $scope.search = {};
$scope.historyFilter = {mediaType: false}; $scope.historyFilter = {mediaType: false};
$scope.historyPeer = {}; $scope.historyPeer = {};
$scope.historyState = {selectActions: false, typing: [], missedCount: 0}; $scope.historyState = {
selectActions: false,
typing: [],
missedCount: 0,
skipped: false
};
$scope.openSettings = function () { $scope.openSettings = function () {
$modal.open({ $modal.open({
@ -862,16 +867,17 @@ angular.module('myApp.controllers', ['myApp.i18n'])
StatusManager.start(); StatusManager.start();
$scope.peerHistories = []; $scope.peerHistories = [];
$scope.skippedHistory = false;
$scope.selectedMsgs = {}; $scope.selectedMsgs = {};
$scope.selectedCount = 0; $scope.selectedCount = 0;
$scope.historyState.selectActions = false; $scope.historyState.selectActions = false;
$scope.historyState.missedCount = 0; $scope.historyState.missedCount = 0;
$scope.historyState.skipped = false;
$scope.state = {}; $scope.state = {};
$scope.toggleMessage = toggleMessage; $scope.toggleMessage = toggleMessage;
$scope.selectedDelete = selectedDelete; $scope.selectedDelete = selectedDelete;
$scope.selectedForward = selectedForward; $scope.selectedForward = selectedForward;
$scope.selectedReply = selectedReply;
$scope.selectedCancel = selectedCancel; $scope.selectedCancel = selectedCancel;
$scope.selectedFlush = selectedFlush; $scope.selectedFlush = selectedFlush;
@ -1066,7 +1072,7 @@ angular.module('myApp.controllers', ['myApp.i18n'])
} else { } else {
minID = 0; minID = 0;
} }
$scope.skippedHistory = hasLess = minID > 0; $scope.historyState.skipped = hasLess = minID > 0;
if (morePending) { if (morePending) {
showMoreHistory(); showMoreHistory();
@ -1123,7 +1129,7 @@ angular.module('myApp.controllers', ['myApp.i18n'])
$scope.historyState.missedCount = 0; $scope.historyState.missedCount = 0;
hasMore = false; hasMore = false;
$scope.skippedHistory = hasLess = false; $scope.historyState.skipped = hasLess = false;
maxID = 0; maxID = 0;
minID = 0; minID = 0;
peerHistory = historiesQueuePush(peerID); peerHistory = historiesQueuePush(peerID);
@ -1169,7 +1175,7 @@ angular.module('myApp.controllers', ['myApp.i18n'])
: 0; : 0;
maxID = historyResult.history[historyResult.history.length - 1]; maxID = historyResult.history[historyResult.history.length - 1];
$scope.skippedHistory = hasLess = minID > 0; $scope.historyState.skipped = hasLess = minID > 0;
hasMore = historyResult.count === null || hasMore = historyResult.count === null ||
fetchedLength && fetchedLength < historyResult.count; fetchedLength && fetchedLength < historyResult.count;
@ -1179,7 +1185,7 @@ angular.module('myApp.controllers', ['myApp.i18n'])
peerHistory.messages = []; peerHistory.messages = [];
angular.forEach(historyResult.history, function (id) { angular.forEach(historyResult.history, function (id) {
var message = AppMessagesManager.wrapForHistory(id); var message = AppMessagesManager.wrapForHistory(id);
if ($scope.skippedHistory) { if ($scope.historyState.skipped) {
delete message.unread; delete message.unread;
} }
if (historyResult.unreadOffset) { if (historyResult.unreadOffset) {
@ -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 () { function toggleEdit () {
if ($scope.historyState.selectActions) { if ($scope.historyState.selectActions) {
selectedCancel(); selectedCancel();
@ -1374,7 +1391,7 @@ angular.module('myApp.controllers', ['myApp.i18n'])
} }
var curPeer = addedMessage.peerID == $scope.curDialog.peerID; var curPeer = addedMessage.peerID == $scope.curDialog.peerID;
if (curPeer) { if (curPeer) {
if ($scope.historyFilter.mediaType || $scope.skippedHistory) { if ($scope.historyFilter.mediaType || $scope.historyState.skipped) {
if (addedMessage.my) { if (addedMessage.my) {
returnToRecent(); returnToRecent();
} else { } else {
@ -1488,7 +1505,7 @@ angular.module('myApp.controllers', ['myApp.i18n'])
$scope.$on('history_need_more', showMoreHistory); $scope.$on('history_need_more', showMoreHistory);
$rootScope.$watch('idle.isIDLE', function (newVal) { $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); AppMessagesManager.readHistory($scope.curDialog.inputPeer);
} }
if (!newVal) { if (!newVal) {
@ -1506,9 +1523,12 @@ angular.module('myApp.controllers', ['myApp.i18n'])
$scope.$watch('curDialog.peer', resetDraft); $scope.$watch('curDialog.peer', resetDraft);
$scope.$on('user_update', angular.noop); $scope.$on('user_update', angular.noop);
$scope.$on('reply_selected', function (e, messageID) {
replySelect(messageID);
});
$scope.$on('ui_typing', onTyping); $scope.$on('ui_typing', onTyping);
$scope.draftMessage = {text: '', send: sendMessage}; $scope.draftMessage = {text: '', send: sendMessage, replyClear: replyClear};
$scope.$watch('draftMessage.text', onMessageChange); $scope.$watch('draftMessage.text', onMessageChange);
$scope.$watch('draftMessage.files', onFilesSelected); $scope.$watch('draftMessage.files', onFilesSelected);
$scope.$watch('draftMessage.sticker', onStickerSelected); $scope.$watch('draftMessage.sticker', onStickerSelected);
@ -1529,11 +1549,14 @@ angular.module('myApp.controllers', ['myApp.i18n'])
}); });
var timeout = 0; var timeout = 0;
var options = {
replyToMsgID: $scope.draftMessage.replyToMessage && $scope.draftMessage.replyToMessage.id
};
do { do {
(function (peerID, curText, curTimeout) { (function (peerID, curText, curTimeout) {
setTimeout(function () { setTimeout(function () {
AppMessagesManager.sendText(peerID, curText); AppMessagesManager.sendText(peerID, curText, options);
}, curTimeout) }, curTimeout)
})($scope.curDialog.peerID, text.substr(0, 4096), timeout); })($scope.curDialog.peerID, text.substr(0, 4096), timeout);
@ -1552,6 +1575,8 @@ angular.module('myApp.controllers', ['myApp.i18n'])
function resetDraft (newPeer) { function resetDraft (newPeer) {
replyClear();
if (newPeer) { if (newPeer) {
Storage.get('draft' + $scope.curDialog.peerID).then(function (draftText) { Storage.get('draft' + $scope.curDialog.peerID).then(function (draftText) {
// console.log('Restore draft', 'draft' + $scope.curDialog.peerID, draftText); // console.log('Restore draft', 'draft' + $scope.curDialog.peerID, draftText);
@ -1566,12 +1591,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) { function onMessageChange(newVal) {
// console.log('ctrl text changed', newVal); // console.log('ctrl text changed', newVal);
// console.trace('ctrl text changed', newVal); // console.trace('ctrl text changed', newVal);
if (newVal && newVal.length) { if (newVal && newVal.length) {
if (!$scope.historyFilter.mediaType && !$scope.skippedHistory) { if (!$scope.historyFilter.mediaType && !$scope.historyState.skipped) {
AppMessagesManager.readHistory($scope.curDialog.inputPeer); AppMessagesManager.readHistory($scope.curDialog.inputPeer);
} }
@ -1596,11 +1631,15 @@ angular.module('myApp.controllers', ['myApp.i18n'])
if (!angular.isArray(newVal) || !newVal.length) { if (!angular.isArray(newVal) || !newVal.length) {
return; 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++) { for (var i = 0; i < newVal.length; i++) {
AppMessagesManager.sendFile($scope.curDialog.peerID, newVal[i], { AppMessagesManager.sendFile($scope.curDialog.peerID, newVal[i], options);
isMedia: $scope.draftMessage.isMedia
});
$scope.$broadcast('ui_message_send'); $scope.$broadcast('ui_message_send');
} }
} }

82
app/js/directives.js

@ -320,6 +320,78 @@ angular.module('myApp.directives', ['myApp.filters'])
templateUrl: templateUrl('message_service') 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() { .directive('myMessagePhoto', function() {
return { return {
templateUrl: templateUrl('message_attach_photo') templateUrl: templateUrl('message_attach_photo')
@ -1212,6 +1284,14 @@ angular.module('myApp.directives', ['myApp.filters'])
composer.focus(); composer.focus();
} }
}); });
$scope.$on('ui_peer_reply', function () {
onContentLoaded(function () {
$scope.$emit('ui_editor_resize');
if (!Config.Navigator.touch) {
composer.focus();
}
})
});
var sendAwaiting = false; var sendAwaiting = false;
$scope.$on('ui_message_before_send', function () { $scope.$on('ui_message_before_send', function () {
@ -2056,8 +2136,6 @@ angular.module('myApp.directives', ['myApp.filters'])
onContentLoaded(updateMargin); onContentLoaded(updateMargin);
}); });
}; };
}) })

1
app/js/lib/utils.js

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

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

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

72
app/js/services.js

@ -764,6 +764,9 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
var lastSearchFilter = {}, var lastSearchFilter = {},
lastSearchResults = []; lastSearchResults = [];
var needSingleMessages = [],
fetchSingleMessagesTimeout = false;
var serverTimeOffset = 0, var serverTimeOffset = 0,
timestampNow = tsNow(true), timestampNow = tsNow(true),
midnightNoOffset = timestampNow - (timestampNow % 86400), midnightNoOffset = timestampNow - (timestampNow % 86400),
@ -1345,15 +1348,20 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
}); });
} }
function sendText(peerID, text) { function sendText(peerID, text, options) {
console.log(peerID, text, options);
if (!angular.isString(text) || !text.length) { if (!angular.isString(text) || !text.length) {
return; return;
} }
options = options || {};
var messageID = tempID--, var messageID = tempID--,
randomID = [nextRandomInt(0xFFFFFFFF), nextRandomInt(0xFFFFFFFF)], randomID = [nextRandomInt(0xFFFFFFFF), nextRandomInt(0xFFFFFFFF)],
randomIDS = bigint(randomID[0]).shiftLeft(32).add(bigint(randomID[1])).toString(), randomIDS = bigint(randomID[0]).shiftLeft(32).add(bigint(randomID[1])).toString(),
historyStorage = historiesStorage[peerID], historyStorage = historiesStorage[peerID],
inputPeer = AppPeersManager.getInputPeerByID(peerID), inputPeer = AppPeersManager.getInputPeerByID(peerID),
flags = 0,
replyToMsgID = options.replyToMsgID,
message; message;
if (historyStorage === undefined) { if (historyStorage === undefined) {
@ -1361,15 +1369,22 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
} }
MtpApiManager.getUserID().then(function (fromID) { MtpApiManager.getUserID().then(function (fromID) {
if (peerID != fromID) {
flags |= 3;
}
if (replyToMsgID) {
flags |= 8;
}
message = { message = {
_: 'message', _: 'message',
id: messageID, id: messageID,
from_id: fromID, from_id: fromID,
to_id: AppPeersManager.getOutputPeer(peerID), to_id: AppPeersManager.getOutputPeer(peerID),
flags: peerID == fromID ? 0 : 3, flags: flags,
date: tsNow(true) + serverTimeOffset, date: tsNow(true) + serverTimeOffset,
message: text, message: text,
random_id: randomIDS, random_id: randomIDS,
reply_to_msg_id: replyToMsgID,
pending: true pending: true
}; };
@ -1399,7 +1414,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
peer: inputPeer, peer: inputPeer,
message: text, message: text,
random_id: randomID, random_id: randomID,
reply_to_msg_id: 0 reply_to_msg_id: replyToMsgID
}, sentRequestOptions).then(function (sentMessage) { }, sentRequestOptions).then(function (sentMessage) {
message.date = sentMessage.date; message.date = sentMessage.date;
message.id = sentMessage.id; message.id = sentMessage.id;
@ -1450,6 +1465,8 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
randomIDS = bigint(randomID[0]).shiftLeft(32).add(bigint(randomID[1])).toString(), randomIDS = bigint(randomID[0]).shiftLeft(32).add(bigint(randomID[1])).toString(),
historyStorage = historiesStorage[peerID], historyStorage = historiesStorage[peerID],
inputPeer = AppPeersManager.getInputPeerByID(peerID), inputPeer = AppPeersManager.getInputPeerByID(peerID),
flags = 0,
replyToMsgID = options.replyToMsgID,
attachType, apiFileName, realFileName; attachType, apiFileName, realFileName;
if (!options.isMedia) { if (!options.isMedia) {
@ -1474,6 +1491,12 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
} }
MtpApiManager.getUserID().then(function (fromID) { MtpApiManager.getUserID().then(function (fromID) {
if (peerID != fromID) {
flags |= 3;
}
if (replyToMsgID) {
flags |= 8;
}
var media = { var media = {
_: 'messageMediaPending', _: 'messageMediaPending',
type: attachType, type: attachType,
@ -1487,11 +1510,12 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
id: messageID, id: messageID,
from_id: fromID, from_id: fromID,
to_id: AppPeersManager.getOutputPeer(peerID), to_id: AppPeersManager.getOutputPeer(peerID),
flags: peerID == fromID ? 0 : 3, flags: flags,
date: tsNow(true) + serverTimeOffset, date: tsNow(true) + serverTimeOffset,
message: '', message: '',
media: media, media: media,
random_id: randomIDS, random_id: randomIDS,
reply_to_msg_id: replyToMsgID,
pending: true pending: true
}; };
@ -1545,7 +1569,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
peer: inputPeer, peer: inputPeer,
media: inputMedia, media: inputMedia,
random_id: randomID, random_id: randomID,
reply_to_msg_id: 0 reply_to_msg_id: replyToMsgID
}).then(function (statedMessage) { }).then(function (statedMessage) {
message.date = statedMessage.message.date; message.date = statedMessage.message.date;
message.id = statedMessage.message.id; message.id = statedMessage.message.id;
@ -1940,6 +1964,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) { if (message.message && message.message.length) {
var options = {}; var options = {};
if (!Config.Navigator.mobile) { if (!Config.Navigator.mobile) {
@ -1954,6 +1993,27 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
return messagesForHistory[msgID] = message; 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) { function regroupWrappedHistory (history, limit) {
if (!history || !history.length) { if (!history || !history.length) {
return false; return false;
@ -2006,7 +2066,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
!curMessage.action && !curMessage.action &&
curMessage.date < prevMessage.date + 900) { 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) { if (groupFwd && curMessage.fwd_from_id && curMessage.fwd_from_id == prevMessage.fwd_from_id) {
curMessage.grouped = singleLine ? 'im_grouped_fwd_short' : 'im_grouped_fwd'; curMessage.grouped = singleLine ? 'im_grouped_fwd_short' : 'im_grouped_fwd';
} else { } else {

2
app/partials/desktop/head.html

@ -52,7 +52,7 @@
</ul> </ul>
</div> </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-when="true" my-i18n="im_show_recent_messages"></span>
<span ng-switch-default my-i18n="im_show_all_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> <strong class="tg_head_peer_return_count" ng-show="historyState.missedCount > 0" ng-bind="'+' + historyState.missedCount"></strong>

16
app/partials/desktop/im.html

@ -142,14 +142,11 @@
<div class="im_edit_panel_wrap clearfix" ng-show="historyState.selectActions"> <div class="im_edit_panel_wrap clearfix" ng-show="historyState.selectActions">
<div class="im_edit_panel_border"></div> <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"> <a class="btn btn-md btn-md-primary im_edit_cancel_link" ng-click="selectedCancel()" my-i18n="modal_cancel"></a>
<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>
<div class="im_edit_selected_actions" my-i18n> <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-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> <my-i18n-param name="count"><strong class="im_selected_count" ng-show="selectedCount > 0" ng-bind="selectedCount"></strong></my-i18n-param>
</div> </div>
</div> </div>
@ -158,7 +155,7 @@
<div class="im_send_form_wrap1"> <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"> <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> <i class="icon im_panel_peer_online" ng-show="historyPeer.id > 0 &amp;&amp; historyPeer.data.status._ == 'userStatusOnline'"></i>
@ -167,6 +164,11 @@
<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" 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"> <div class="im_send_field_wrap">
<a class="composer_emoji_insert_btn"><i class="icon icon-emoji"></i></a> <a class="composer_emoji_insert_btn"><i class="icon icon-emoji"></i></a>

2
app/partials/desktop/message.html

@ -41,6 +41,8 @@
<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> <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>
<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"> <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> <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"> <div class="im_message_fwd_author_wrap">

55
app/partials/desktop/reply_message.html

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

2
app/partials/mobile/message.html

@ -33,6 +33,8 @@
<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> <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>
<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"> <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> <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> <span my-i18n-param="date" class="im_message_fwd_date" ng-bind="::historyMessage.fwd_date | dateOrTime"></span>

Loading…
Cancel
Save