diff --git a/app/js/controllers.js b/app/js/controllers.js index e6919d1e..ff34028c 100644 --- a/app/js/controllers.js +++ b/app/js/controllers.js @@ -753,6 +753,21 @@ angular.module('myApp.controllers', ['myApp.i18n']) deleteDialog(dialog.peerID); }); + $scope.$on('draft_updated', function (e, draftUpdate) { + var curDialog, i; + for (i = 0; i < $scope.dialogs.length; i++) { + curDialog = $scope.dialogs[i]; + if (curDialog.peerID == draftUpdate.peerID) { + curDialog.draft = draftUpdate.draft; + if (i > 0 && draftUpdate.draft) { + $scope.dialogs.splice(i, 1); + $scope.dialogs.unshift(curDialog); + } + break; + } + } + }); + $scope.$on('history_delete', function (e, historyUpdate) { for (var i = 0; i < $scope.dialogs.length; i++) { if ($scope.dialogs[i].peerID == historyUpdate.peerID) { @@ -2147,13 +2162,13 @@ angular.module('myApp.controllers', ['myApp.i18n']) $scope.$on('user_update', angular.noop); }) - .controller('AppImSendController', function ($q, $scope, $timeout, MtpApiManager, Storage, AppProfileManager, AppChatsManager, AppUsersManager, AppPeersManager, AppDocsManager, AppMessagesManager, AppInlineBotsManager, MtpApiFileManager, RichTextProcessor) { + .controller('AppImSendController', function ($rootScope, $q, $scope, $timeout, MtpApiManager, Storage, AppProfileManager, AppChatsManager, AppUsersManager, AppPeersManager, AppDocsManager, AppMessagesManager, AppInlineBotsManager, MtpApiFileManager, DraftsManager, RichTextProcessor) { $scope.$watch('curDialog.peer', resetDraft); $scope.$on('user_update', angular.noop); $scope.$on('peer_draft_attachment', applyDraftAttachment); $scope.$on('reply_selected', function (e, messageID) { - replySelect(messageID); + replySelect(messageID, true); }); $scope.$on('ui_typing', onTyping); @@ -2188,6 +2203,18 @@ angular.module('myApp.controllers', ['myApp.i18n']) $scope.replyKeyboardToggle = replyKeyboardToggle; $scope.toggleSlash = toggleSlash; + $rootScope.$watch('idle.isIDLE', function (newVal) { + if (newVal && $scope.curDialog.peerID) { + DraftsManager.syncDraft($scope.curDialog.peerID); + } + }); + + $scope.$on('draft_updated', function (e, draftUpdate) { + if (draftUpdate.peerID == $scope.curDialog.peerID) { + getDraft(); + } + }); + var replyToMarkup = false; var forceDraft = false; @@ -2200,9 +2227,9 @@ angular.module('myApp.controllers', ['myApp.i18n']) if (angular.isString(text) && text.length > 0) { text = RichTextProcessor.parseEmojis(text); - var timeout = 0; var options = { - replyToMsgID: $scope.draftMessage.replyToMessage && $scope.draftMessage.replyToMessage.mid + replyToMsgID: $scope.draftMessage.replyToMessage && $scope.draftMessage.replyToMessage.mid, + clearDraft: true }; do { AppMessagesManager.sendText($scope.curDialog.peerID, text.substr(0, 4096), options); @@ -2213,6 +2240,8 @@ angular.module('myApp.controllers', ['myApp.i18n']) if (forceDraft == $scope.curDialog.peer) { forceDraft = false; + } else { + DraftsManager.changeDraft($scope.curDialog.peerID); } resetDraft(); @@ -2326,7 +2355,14 @@ angular.module('myApp.controllers', ['myApp.i18n']) }); } - function resetDraft (newPeer) { + function resetDraft (newPeer, prevPeer) { + if (prevPeer) { + var prevPeerID = AppPeersManager.getPeerID(prevPeer); + if (prevPeerID) { + DraftsManager.syncDraft(prevPeerID); + } + } + updateMentions(); updateCommands(); replyClear(); @@ -2347,13 +2383,20 @@ angular.module('myApp.controllers', ['myApp.i18n']) } fwdsClear(); + getDraft(); + } - if (newPeer) { - Storage.get('draft' + $scope.curDialog.peerID).then(function (draftText) { - // console.log('Restore draft', 'draft' + $scope.curDialog.peerID, draftText); - $scope.draftMessage.text = draftText || ''; + function getDraft() { + if ($scope.curDialog.peerID) { + DraftsManager.getDraft($scope.curDialog.peerID).then(function (draftData) { + $scope.draftMessage.text = draftData ? draftData.text : ''; $scope.draftMessage.isBroadcast = AppPeersManager.isChannel($scope.curDialog.peerID) && !AppPeersManager.isMegagroup($scope.curDialog.peerID); - // console.log('send broadcast', $scope.draftMessage); + if (draftData.replyToMsgID) { + var replyToMsgID = draftData.replyToMsgID; + replySelect(replyToMsgID); + } else { + replyClear(); + } $scope.$broadcast('ui_peer_draft'); }); } else { @@ -2364,7 +2407,7 @@ angular.module('myApp.controllers', ['myApp.i18n']) } function applyDraftAttachment (e, attachment) { - // console.log('apply draft attach', attachment); + console.log('apply draft attach', attachment); if (!attachment || !attachment._) { return; } @@ -2386,14 +2429,10 @@ angular.module('myApp.controllers', ['myApp.i18n']) }, 1000); } else if (attachment._ == 'fwd_messages') { - forceDraft = $scope.curDialog.peer; - $scope.draftMessage.fwdMessages = attachment.id; - $scope.$broadcast('ui_peer_reply'); - var peerID = AppPeersManager.getPeerID($scope.curDialog.peer); - Storage.get('draft' + peerID).then(function (draftText) { - $scope.draftMessage.text = draftText || ''; - $scope.$broadcast('ui_peer_draft'); - }); + $timeout(function () { + $scope.draftMessage.fwdMessages = attachment.id; + $scope.$broadcast('ui_peer_reply'); + }, 100); } else if (attachment._ == 'inline_query') { var mention = attachment.mention; @@ -2413,13 +2452,20 @@ angular.module('myApp.controllers', ['myApp.i18n']) } } - function replySelect(messageID) { - $scope.draftMessage.replyToMessage = AppMessagesManager.wrapForDialog(messageID); + function replySelect(messageID, byUser) { + $scope.draftMessage.replyToMessage = AppMessagesManager.wrapSingleMessage(messageID); $scope.$broadcast('ui_peer_reply'); replyToMarkup = false; + + if (byUser) { + DraftsManager.changeDraft($scope.curDialog.peerID, { + text: $scope.draftMessage.text, + replyToMsgID: messageID + }); + } } - function replyClear() { + function replyClear(byUser) { var message = $scope.draftMessage.replyToMessage; if (message && $scope.historyState.replyKeyboard && @@ -2430,6 +2476,12 @@ angular.module('myApp.controllers', ['myApp.i18n']) } delete $scope.draftMessage.replyToMessage; $scope.$broadcast('ui_peer_reply'); + + if (byUser) { + DraftsManager.changeDraft($scope.curDialog.peerID, { + text: $scope.draftMessage.text + }); + } } function fwdsClear () { @@ -2512,16 +2564,15 @@ angular.module('myApp.controllers', ['myApp.i18n']) if (!$scope.historyFilter.mediaType && !$scope.historyState.skipped) { AppMessagesManager.readHistory($scope.curDialog.peerID); } - - var backupDraftObj = {}; - backupDraftObj['draft' + $scope.curDialog.peerID] = newVal; - Storage.set(backupDraftObj); - // console.log(dT(), 'draft save', backupDraftObj); - } else { - Storage.remove('draft' + $scope.curDialog.peerID); - // console.log(dT(), 'draft delete', 'draft' + $scope.curDialog.peerID); } - checkInlinePattern(newVal); + if ($scope.curDialog.peerID) { + var replyToMessage = $scope.draftMessage.replyToMessage; + DraftsManager.changeDraft($scope.curDialog.peerID, { + text: newVal, + replyToMsgID: replyToMessage && replyToMessage.mid + }); + checkInlinePattern(newVal); + } } var inlineUsernameRegex = /^@([a-zA-Z\d_]{1,32})( | )([\s\S]*)$/; diff --git a/app/js/lib/ng_utils.js b/app/js/lib/ng_utils.js index 94e97d14..37dcb4fb 100644 --- a/app/js/lib/ng_utils.js +++ b/app/js/lib/ng_utils.js @@ -1258,6 +1258,7 @@ angular.module('izhukov.utils', []) return { wrapRichText: wrapRichText, wrapPlainText: wrapPlainText, + wrapDraftText: wrapDraftText, wrapUrl: wrapUrl, parseEntities: parseEntities, parseMarkdown: parseMarkdown, @@ -1731,6 +1732,72 @@ angular.module('izhukov.utils', []) return $sce.trustAs('html', text); } + function wrapDraftText (text, options) { + if (!text || !text.length) { + return ''; + } + + options = options || {}; + + var entities = options.entities; + + if (entities === undefined) { + entities = parseEntities(text, options); + } + + var i = 0; + var len = entities.length; + var entity; + var entityText; + var skipEntity; + var code = []; + var lastOffset = 0; + for (i = 0; i < len; i++) { + entity = entities[i]; + if (entity.offset > lastOffset) { + code.push( + text.substr(lastOffset, entity.offset - lastOffset) + ); + } + else if (entity.offset < lastOffset) { + continue; + } + skipEntity = false; + entityText = text.substr(entity.offset, entity.length); + switch (entity._) { + case 'messageEntityEmoji': + code.push( + ':', + entity.title, + ':' + ); + break; + + case 'messageEntityCode': + code.push( + '`', entityText, '`' + ); + break; + + case 'messageEntityPre': + code.push( + '```', entityText, '```' + ); + break; + + default: + skipEntity = true; + } + lastOffset = entity.offset + (skipEntity ? 0 : entity.length); + } + + code.push(text.substr(lastOffset)); + + console.log(code, entities); + + return code.join(''); + } + function checkBrackets(url) { var urlLength = url.length, urlOpenBrackets = url.split('(').length - 1, @@ -1818,7 +1885,34 @@ angular.module('izhukov.utils', []) return url; } -}); +}) + +.service('ServerTimeManager', function (Storage) { + + var timestampNow = tsNow(true); + var midnightNoOffset = timestampNow - (timestampNow % 86400); + var midnightOffseted = new Date(); + midnightOffseted.setHours(0); + midnightOffseted.setMinutes(0); + midnightOffseted.setSeconds(0); + + var midnightOffset = midnightNoOffset - (Math.floor(+midnightOffseted / 1000)); + + var serverTimeOffset = 0; + var timeParams = { + midnightOffset: midnightOffset, + serverTimeOffset: serverTimeOffset + }; + + Storage.get('server_time_offset').then(function (to) { + if (to) { + serverTimeOffset = to; + timeParams.serverTimeOffset = to; + } + }); + + return timeParams; +}) diff --git a/app/js/locales/en-us.json b/app/js/locales/en-us.json index 3d93176e..218b3f3b 100644 --- a/app/js/locales/en-us.json +++ b/app/js/locales/en-us.json @@ -291,6 +291,7 @@ "conversation_message_deleted": "deleted message", "conversation_you": "You", + "conversation_draft": "Draft:", "conversation_media_photo": "Photo", "conversation_media_video": "Video", "conversation_media_document": "File", diff --git a/app/js/messages_manager.js b/app/js/messages_manager.js index e7ea66ae..6b767f36 100644 --- a/app/js/messages_manager.js +++ b/app/js/messages_manager.js @@ -9,7 +9,7 @@ angular.module('myApp.services') -.service('AppMessagesManager', function ($q, $rootScope, $location, $filter, $timeout, $sce, ApiUpdatesManager, AppUsersManager, AppChatsManager, AppPeersManager, AppPhotosManager, AppDocsManager, AppStickersManager, AppWebPagesManager, MtpApiManager, MtpApiFileManager, RichTextProcessor, NotificationsManager, Storage, AppProfileManager, TelegramMeWebService, ErrorService, StatusManager, _) { +.service('AppMessagesManager', function ($q, $rootScope, $location, $filter, $timeout, $sce, ApiUpdatesManager, AppUsersManager, AppChatsManager, AppPeersManager, AppPhotosManager, AppDocsManager, AppStickersManager, AppMessagesIDsManager, DraftsManager, AppWebPagesManager, MtpApiManager, MtpApiFileManager, ServerTimeManager, RichTextProcessor, NotificationsManager, Storage, AppProfileManager, TelegramMeWebService, ErrorService, StatusManager, _) { var messagesStorage = {}; var messagesForHistory = {}; @@ -35,18 +35,6 @@ angular.module('myApp.services') needIncrementMessageViews = [], incrementMessageViewsTimeout = false; - var serverTimeOffset = 0, - timestampNow = tsNow(true), - midnightNoOffset = timestampNow - (timestampNow % 86400), - midnightOffseted = new Date(), - midnightOffset; - - Storage.get('server_time_offset').then(function (to) { - if (to) { - serverTimeOffset = to; - } - }); - var maxSeenID = false; if (Config.Modes.packed) { Storage.get('max_seen_msg').then(function (maxID) { @@ -58,11 +46,6 @@ angular.module('myApp.services') var dateOrTimeFilter = $filter('dateOrTime'); var fwdMessagesPluralize = _.pluralize('conversation_forwarded_X_messages'); - midnightOffseted.setHours(0); - midnightOffseted.setMinutes(0); - midnightOffseted.setSeconds(0); - midnightOffset = midnightNoOffset - (Math.floor(+midnightOffseted / 1000)); - NotificationsManager.start(); var allDialogsLoaded = false @@ -141,24 +124,44 @@ angular.module('myApp.services') return []; } - function saveChannelDialog (channelID, dialog) { - var peerID = -channelID; + function saveConversation (dialog) { + var peerID = AppPeersManager.getPeerID(dialog.peer); + if (!peerID) { + return false; + } + var channelID = AppPeersManager.isChannel(peerID) ? -peerID : 0; var peerText = AppPeersManager.getPeerSearchText(peerID); SearchIndexManager.indexObject(peerID, peerText, dialogsIndex); - var isMegagroup = AppChatsManager.isMegagroup(channelID); - var mid = getFullMessageID(dialog.top_message, channelID); + var isMegagroup = AppPeersManager.isMegagroup(channelID); + var mid = AppMessagesIDsManager.getFullMessageID(dialog.top_message, channelID); var message = getMessage(mid); var offsetDate = message.date; + if (!channelID && peerID < 0) { + var chat = AppChatsManager.getChat(-peerID); + if (chat && chat.migrated_to && chat.pFlags.deactivated) { + var migratedToPeer = AppPeersManager.getPeerID(chat.migrated_to); + migratedFromTo[peerID] = migratedToPeer; + migratedToFrom[migratedToPeer] = peerID; + return; + } + } + dialog.top_message = mid; - dialog.read_inbox_max_id = getFullMessageID(dialog.read_inbox_max_id, channelID); - dialog.read_outbox_max_id = getFullMessageID(dialog.read_outbox_max_id, channelID); + dialog.read_inbox_max_id = AppMessagesIDsManager.getFullMessageID(dialog.read_inbox_max_id, channelID); + dialog.read_outbox_max_id = AppMessagesIDsManager.getFullMessageID(dialog.read_outbox_max_id, channelID); var topDate = message.date; - var channel = AppChatsManager.getChat(channelID); - if (!topDate || channel.date && channel.date > topDate) { - topDate = channel.date; + if (channelID) { + var channel = AppChatsManager.getChat(channelID); + if (!topDate || channel.date && channel.date > topDate) { + topDate = channel.date; + } + } + var savedDraft = DraftsManager.saveDraft(peerID, dialog.draft); + if (savedDraft && savedDraft.date > topDate) { + topDate = savedDraft.date; } dialog.index = generateDialogIndex(topDate); @@ -175,13 +178,34 @@ angular.module('myApp.services') if (historiesStorage[peerID] === undefined) { var historyStorage = {count: null, history: [mid], pending: []}; historiesStorage[peerID] = historyStorage; + if (mergeReplyKeyboard(historyStorage, message)) { + $rootScope.$broadcast('history_reply_markup', {peerID: peerID}); + } } NotificationsManager.savePeerSettings(peerID, dialog.notify_settings); - if (dialog.pts) { + if (channelID && dialog.pts) { ApiUpdatesManager.addChannelState(channelID, dialog.pts); } + + if ( + !channelID && + dialog.unread_count > 0 && + maxSeenID && + dialog.top_message > maxSeenID + ) { + var notifyPeer = message.flags & 16 ? message.from_id : peerID; + if (message.pFlags.unread && + !message.pFlags.out && + !message.pFlags.silent) { + NotificationsManager.getPeerMuted(notifyPeer).then(function (muted) { + if (!muted) { + notifyAboutMessage(message); + } + }); + } + } } function getTopMessages (limit) { @@ -191,11 +215,11 @@ angular.module('myApp.services') var offsetID = 0; var offsetPeerID = 0; if (dialogsOffsetDate) { - offsetDate = dialogsOffsetDate + serverTimeOffset; + offsetDate = dialogsOffsetDate + ServerTimeManager.serverTimeOffset; } return MtpApiManager.invokeApi('messages.getDialogs', { offset_date: offsetDate, - offset_id: getMessageLocalID(offsetID), + offset_id: AppMessagesIDsManager.getMessageLocalID(offsetID), offset_peer: AppPeersManager.getInputPeerByID(offsetPeerID), limit: limit }, { @@ -211,62 +235,12 @@ angular.module('myApp.services') var maxSeenIdIncremented = offsetDate ? true : false; angular.forEach(dialogsResult.dialogs, function (dialog) { - var peerID = AppPeersManager.getPeerID(dialog.peer); - if (dialog.pts) { - var channelID = -peerID; - saveChannelDialog(channelID, dialog); - ApiUpdatesManager.addChannelState(channelID, dialog.pts); - } else { - if (peerID < 0) { - var chat = AppChatsManager.getChat(-peerID); - if (chat && chat.migrated_to && chat.pFlags.deactivated) { - var migratedToPeer = AppPeersManager.getPeerID(chat.migrated_to); - migratedFromTo[peerID] = migratedToPeer; - migratedToFrom[migratedToPeer] = peerID; - return; - } - } - var peerText = AppPeersManager.getPeerSearchText(peerID); - SearchIndexManager.indexObject(peerID, peerText, dialogsIndex); - - var message = getMessage(dialog.top_message); - - dialog.index = generateDialogIndex(message.date); - dialog.peerID = peerID; + saveConversation(dialog); - pushDialogToStorage(dialog, message.date); - - if (!maxSeenIdIncremented) { - incrementMaxSeenID(dialog.top_message); - maxSeenIdIncremented = true; - } - - if (historiesStorage[peerID] === undefined) { - var historyStorage = {count: null, history: [dialog.top_message], pending: []}; - historiesStorage[peerID] = historyStorage; - if (mergeReplyKeyboard(historyStorage, message)) { - $rootScope.$broadcast('history_reply_markup', {peerID: peerID}); - } - } - - NotificationsManager.savePeerSettings(peerID, dialog.notify_settings); - - if ( - dialog.unread_count > 0 && - maxSeenID && - dialog.top_message > maxSeenID - ) { - var notifyPeer = message.flags & 16 ? message.from_id : peerID; - if (message.pFlags.unread && - !message.pFlags.out && - !message.pFlags.silent) { - NotificationsManager.getPeerMuted(notifyPeer).then(function (muted) { - if (!muted) { - notifyAboutMessage(message); - } - }); - } - } + if (!maxSeenIdIncremented && + !AppPeersManager.isChannel(AppPeersManager.getPeerID(dialog.peer))) { + incrementMaxSeenID(dialog.top_message); + maxSeenIdIncremented = true; } }); @@ -280,7 +254,7 @@ angular.module('myApp.services') function generateDialogIndex (date) { if (date === undefined) { - date = tsNow(true) + serverTimeOffset; + date = tsNow(true) + ServerTimeManager.serverTimeOffset; } return (date * 0x10000) + ((++dialogsNum) & 0xFFFF); } @@ -319,7 +293,7 @@ angular.module('myApp.services') return MtpApiManager.invokeApi('messages.getHistory', { peer: AppPeersManager.getInputPeerByID(peerID), - offset_id: maxID ? getMessageLocalID(maxID) : 0, + offset_id: maxID ? AppMessagesIDsManager.getMessageLocalID(maxID) : 0, add_offset: offset || 0, limit: limit || 0 }, { @@ -353,7 +327,7 @@ angular.module('myApp.services') to_id: AppPeersManager.getOutputPeer(peerID), flags: 0, pFlags: {}, - date: tsNow(true) + serverTimeOffset, + date: tsNow(true) + ServerTimeManager.serverTimeOffset, action: { _: 'messageActionBotIntro', description: description @@ -387,65 +361,6 @@ angular.module('myApp.services') }); } - var channelLocals = {}; - var channelsByLocals = {}; - var channelCurLocal = 0; - var fullMsgIDModulus = 4294967296; - - function getFullMessageID (msgID, channelID) { - if (!channelID || msgID <= 0) { - return msgID; - } - msgID = getMessageLocalID(msgID); - var localStart = channelLocals[channelID]; - if (!localStart) { - localStart = (++channelCurLocal) * fullMsgIDModulus; - channelsByLocals[localStart] = channelID; - channelLocals[channelID] = localStart; - } - - return localStart + msgID; - } - - function getMessageIDInfo (fullMsgID) { - if (fullMsgID < fullMsgIDModulus) { - return [fullMsgID, 0]; - } - var msgID = fullMsgID % fullMsgIDModulus; - var channelID = channelsByLocals[fullMsgID - msgID]; - - return [msgID, channelID]; - } - - function getMessageLocalID (fullMsgID) { - if (!fullMsgID) { - return 0; - } - return fullMsgID % fullMsgIDModulus; - } - - function splitMessageIDsByChannels (mids) { - var msgIDsByChannels = {}; - var midsByChannels = {}; - var i, mid, msgChannel, channelID; - for (i = 0; i < mids.length; i++) { - mid = mids[i]; - msgChannel = getMessageIDInfo(mid); - channelID = msgChannel[1]; - if (msgIDsByChannels[channelID] === undefined) { - msgIDsByChannels[channelID] = []; - midsByChannels[channelID] = []; - } - msgIDsByChannels[channelID].push(msgChannel[0]); - midsByChannels[channelID].push(mid); - } - - return { - msgIDs: msgIDsByChannels, - mids: midsByChannels - }; - } - function fillHistoryStorage (peerID, maxID, fullLimit, historyStorage) { // console.log('fill history storage', peerID, maxID, fullLimit, angular.copy(historyStorage)); var offset = (migratedFromTo[peerID] && !maxID) ? 1 : 0; @@ -885,7 +800,7 @@ angular.module('myApp.services') var offsetMessage = maxID && getMessage(maxID); if (offsetMessage && offsetMessage.date) { - offsetDate = offsetMessage.date + serverTimeOffset; + offsetDate = offsetMessage.date + ServerTimeManager.serverTimeOffset; offsetID = offsetMessage.id; offsetPeerID = getMessagePeer(offsetMessage); } @@ -893,7 +808,7 @@ angular.module('myApp.services') q: query, offset_date: offsetDate, offset_peer: AppPeersManager.getInputPeerByID(offsetPeerID), - offset_id: getMessageLocalID(offsetID), + offset_id: AppMessagesIDsManager.getMessageLocalID(offsetID), limit: limit || 20 }, { timeout: 300, @@ -941,7 +856,7 @@ angular.module('myApp.services') } function deleteMessages (messageIDs) { - var splitted = splitMessageIDsByChannels(messageIDs); + var splitted = AppMessagesIDsManager.splitMessageIDsByChannels(messageIDs); var promises = []; angular.forEach(splitted.msgIDs, function (msgIDs, channelID) { var promise; @@ -999,7 +914,7 @@ angular.module('myApp.services') } function getMessageShareLink(fullMsgID) { - var info = getMessageIDInfo(fullMsgID); + var info = AppMessagesIDsManager.getMessageIDInfo(fullMsgID); var msgID = info[0]; var channelID = info[1]; if (!channelID) { @@ -1176,11 +1091,11 @@ angular.module('myApp.services') var channelID = isChannel ? -toPeerID : 0; var isBroadcast = isChannel && !AppChatsManager.isMegagroup(channelID); - var mid = getFullMessageID(apiMessage.id, channelID); + var mid = AppMessagesIDsManager.getFullMessageID(apiMessage.id, channelID); apiMessage.mid = mid; var dialog = getDialogByPeerID(toPeerID)[0]; - if (dialog) { + if (dialog && mid > 0) { var dialogKey = apiMessage.pFlags.out ? 'read_outbox_max_id' : 'read_inbox_max_id'; @@ -1192,15 +1107,15 @@ angular.module('myApp.services') } if (apiMessage.reply_to_msg_id) { - apiMessage.reply_to_mid = getFullMessageID(apiMessage.reply_to_msg_id, channelID); + apiMessage.reply_to_mid = AppMessagesIDsManager.getFullMessageID(apiMessage.reply_to_msg_id, channelID); } - apiMessage.date -= serverTimeOffset; + apiMessage.date -= ServerTimeManager.serverTimeOffset; var fwdHeader = apiMessage.fwd_from; if (fwdHeader) { apiMessage.fwdFromID = fwdHeader.channel_id ? -fwdHeader.channel_id : fwdHeader.from_id; - fwdHeader.date -= serverTimeOffset; + fwdHeader.date -= ServerTimeManager.serverTimeOffset; } apiMessage.toID = toPeerID; @@ -1358,7 +1273,7 @@ angular.module('myApp.services') to_id: AppPeersManager.getOutputPeer(peerID), flags: flags, pFlags: pFlags, - date: tsNow(true) + serverTimeOffset, + date: tsNow(true) + ServerTimeManager.serverTimeOffset, message: text, random_id: randomIDS, reply_to_msg_id: replyToMsgID, @@ -1398,13 +1313,16 @@ angular.module('myApp.services') if (asChannel) { flags |= 16; } + if (options.clearDraft) { + flags |= 128; + } var apiPromise; if (options.viaBotID) { apiPromise = MtpApiManager.invokeApi('messages.sendInlineBotResult', { flags: flags, peer: AppPeersManager.getInputPeerByID(peerID), random_id: randomID, - reply_to_msg_id: getMessageLocalID(replyToMsgID), + reply_to_msg_id: AppMessagesIDsManager.getMessageLocalID(replyToMsgID), query_id: options.queryID, id: options.resultID }, sentRequestOptions); @@ -1417,7 +1335,7 @@ angular.module('myApp.services') peer: AppPeersManager.getInputPeerByID(peerID), message: text, random_id: randomID, - reply_to_msg_id: getMessageLocalID(replyToMsgID), + reply_to_msg_id: AppMessagesIDsManager.getMessageLocalID(replyToMsgID), entities: entities }, sentRequestOptions) } @@ -1467,6 +1385,15 @@ angular.module('myApp.services') // message.send(); // }, 5000); + ApiUpdatesManager.processUpdateMessage({ + _: 'updateShort', + update: { + _: 'updateDraftMessage', + peer: AppPeersManager.getOutputPeer(peerID), + draft: {_: 'draftMessageEmpty'} + } + }); + pendingByRandomID[randomIDS] = [peerID, messageID]; }; @@ -1539,7 +1466,7 @@ angular.module('myApp.services') to_id: AppPeersManager.getOutputPeer(peerID), flags: flags, pFlags: pFlags, - date: tsNow(true) + serverTimeOffset, + date: tsNow(true) + ServerTimeManager.serverTimeOffset, message: '', media: media, random_id: randomIDS, @@ -1603,7 +1530,7 @@ angular.module('myApp.services') peer: AppPeersManager.getInputPeerByID(peerID), media: inputMedia, random_id: randomID, - reply_to_msg_id: getMessageLocalID(replyToMsgID) + reply_to_msg_id: AppMessagesIDsManager.getMessageLocalID(replyToMsgID) }).then(function (updates) { ApiUpdatesManager.processUpdateMessage(updates); }, function (error) { @@ -1758,7 +1685,7 @@ angular.module('myApp.services') to_id: AppPeersManager.getOutputPeer(peerID), flags: flags, pFlags: pFlags, - date: tsNow(true) + serverTimeOffset, + date: tsNow(true) + ServerTimeManager.serverTimeOffset, message: '', media: media, random_id: randomIDS, @@ -1805,7 +1732,7 @@ angular.module('myApp.services') flags: flags, peer: AppPeersManager.getInputPeerByID(peerID), random_id: randomID, - reply_to_msg_id: getMessageLocalID(replyToMsgID), + reply_to_msg_id: AppMessagesIDsManager.getMessageLocalID(replyToMsgID), query_id: options.queryID, id: options.resultID }, sentRequestOptions); @@ -1815,7 +1742,7 @@ angular.module('myApp.services') peer: AppPeersManager.getInputPeerByID(peerID), media: inputMedia, random_id: randomID, - reply_to_msg_id: getMessageLocalID(replyToMsgID) + reply_to_msg_id: AppMessagesIDsManager.getMessageLocalID(replyToMsgID) }, sentRequestOptions); } apiPromise.then(function (updates) { @@ -1851,7 +1778,7 @@ angular.module('myApp.services') flags |= 16; } - var splitted = splitMessageIDsByChannels(mids); + var splitted = AppMessagesIDsManager.splitMessageIDsByChannels(mids); var promises = []; angular.forEach(splitted.msgIDs, function (msgIDs, channelID) { var len = msgIDs.length; @@ -2008,50 +1935,6 @@ angular.module('myApp.services') return false; } - function openChatInviteLink (hash) { - return MtpApiManager.invokeApi('messages.checkChatInvite', { - hash: hash - }).then(function (chatInvite) { - var chatTitle; - if (chatInvite._ == 'chatInviteAlready') { - AppChatsManager.saveApiChat(chatInvite.chat); - if (!chatInvite.chat.pFlags.left) { - return $rootScope.$broadcast('history_focus', { - peerString: AppChatsManager.getChatString(chatInvite.chat.id) - }); - } - chatTitle = chatInvite.chat.title; - } else { - chatTitle = chatInvite.title; - } - ErrorService.confirm({ - type: (chatInvite.pFlags.channel && !chatInvite.pFlags.megagroup) ? 'JOIN_CHANNEL_BY_LINK' : 'JOIN_GROUP_BY_LINK', - title: chatTitle - }).then(function () { - return MtpApiManager.invokeApi('messages.importChatInvite', { - hash: hash - }).then(function (updates) { - ApiUpdatesManager.processUpdateMessage(updates); - - if (updates.chats && updates.chats.length == 1) { - $rootScope.$broadcast('history_focus', {peerString: AppChatsManager.getChatString(updates.chats[0].id) - }); - } - else if (updates.updates && updates.updates.length) { - for (var i = 0, len = updates.updates.length, update; i < len; i++) { - update = updates.updates[i]; - if (update._ == 'updateNewMessage') { - $rootScope.$broadcast('history_focus', {peerString: AppChatsManager.getChatString(update.message.to_id.chat_id) - }); - break; - } - } - } - }); - }); - }); - } - function getMessagePeer (message) { var toID = message.to_id && AppPeersManager.getPeerID(message.to_id) || 0; @@ -2100,12 +1983,26 @@ angular.module('myApp.services') message.dateText = dateOrTimeFilter(message.date); if (useCache) { + message.draft = DraftsManager.getServerDraft(message.peerID); messagesForDialogs[msgID] = message; } return message; } + function wrapSingleMessage(msgID) { + if (messagesStorage[msgID]) { + return wrapForDialog(msgID); + } + if (needSingleMessages.indexOf(msgID) == -1) { + needSingleMessages.push(msgID); + if (fetchSingleMessagesTimeout === false) { + fetchSingleMessagesTimeout = setTimeout(fetchSingleMessages, 100); + } + } + return {mid: msgID, loading: true}; + } + function clearDialogCache (msgID) { delete messagesForDialogs[msgID]; } @@ -2206,17 +2103,7 @@ angular.module('myApp.services') var replyToMsgID = message.reply_to_mid; if (replyToMsgID) { - if (messagesStorage[replyToMsgID]) { - message.reply_to_msg = wrapForDialog(replyToMsgID); - } else { - message.reply_to_msg = {mid: replyToMsgID, loading: true}; - if (needSingleMessages.indexOf(replyToMsgID) == -1) { - needSingleMessages.push(replyToMsgID); - if (fetchSingleMessagesTimeout === false) { - fetchSingleMessagesTimeout = setTimeout(fetchSingleMessages, 100); - } - } - } + message.reply_to_msg = wrapSingleMessage(replyToMsgID); } return messagesForHistory[msgID] = message; @@ -2261,7 +2148,7 @@ angular.module('myApp.services') var mids = needSingleMessages.slice(); needSingleMessages = []; - var splitted = splitMessageIDsByChannels(mids); + var splitted = AppMessagesIDsManager.splitMessageIDsByChannels(mids); angular.forEach(splitted.msgIDs, function (msgIDs, channelID) { var promise; if (channelID > 0) { @@ -2296,7 +2183,7 @@ angular.module('myApp.services') var mids = needIncrementMessageViews.slice(); needIncrementMessageViews = []; - var splitted = splitMessageIDsByChannels(mids); + var splitted = AppMessagesIDsManager.splitMessageIDsByChannels(mids); angular.forEach(splitted.msgIDs, function (msgIDs, channelID) { // console.log('increment', msgIDs, channelID); MtpApiManager.invokeApi('messages.getMessagesViews', { @@ -2345,7 +2232,7 @@ angular.module('myApp.services') for (i = start; i < end; i++) { curMessage = history[i]; - curDay = Math.floor((curMessage.date + midnightOffset) / 86400); + curDay = Math.floor((curMessage.date + ServerTimeManager.midnightOffset) / 86400); prevGrouped = prevMessage && prevMessage.grouped; curGrouped = curMessage.grouped; @@ -2695,7 +2582,7 @@ angular.module('myApp.services') if (pendingData) { var peerID = pendingData[0]; var channelID = AppPeersManager.isChannel(peerID) ? -peerID : 0; - pendingByMessageID[getFullMessageID(update.id, channelID)] = randomID; + pendingByMessageID[AppMessagesIDsManager.getFullMessageID(update.id, channelID)] = randomID; } break; @@ -2827,7 +2714,7 @@ angular.module('myApp.services') var message = update.message; var peerID = getMessagePeer(message); var channelID = message.to_id._ == 'peerChannel' ? -peerID : 0; - var mid = getFullMessageID(message.id, channelID); + var mid = AppMessagesIDsManager.getFullMessageID(message.id, channelID); if (messagesStorage[mid] === undefined) { break; } @@ -2856,7 +2743,7 @@ angular.module('myApp.services') case 'updateReadChannelOutbox': var isOut = update._ == 'updateReadHistoryOutbox' || update._ == 'updateReadChannelOutbox'; var channelID = update.channel_id; - var maxID = getFullMessageID(update.max_id, channelID); + var maxID = AppMessagesIDsManager.getFullMessageID(update.max_id, channelID); var peerID = channelID ? -channelID : AppPeersManager.getPeerID(update.peer); var foundDialog = getDialogByPeerID(peerID); var history = (historiesStorage[peerID] || {}).history || []; @@ -2902,12 +2789,14 @@ angular.module('myApp.services') } } } - if (!isOut && foundDialog[0]) { - if (newUnreadCount && + if (foundDialog[0]) { + if (!isOut && + newUnreadCount && foundDialog[0].top_message <= maxID) { newUnreadCount = foundDialog[0].unread_count = 0; } - foundDialog[0].read_inbox_max_id = maxID; + var dialogKey = isOut ? 'read_outbox_max_id' : 'read_inbox_max_id'; + foundDialog[0][dialogKey] = maxID; } if (newUnreadCount !== false) { @@ -2942,7 +2831,7 @@ angular.module('myApp.services') var peerMessagesToHandle, peerMessagesHandlePos; for (i = 0; i < update.messages.length; i++) { - messageID = getFullMessageID(update.messages[i], channelID); + messageID = AppMessagesIDsManager.getFullMessageID(update.messages[i], channelID); message = messagesStorage[messageID]; if (message) { peerID = getMessagePeer(message); @@ -3043,7 +2932,7 @@ angular.module('myApp.services') } if (hasDialog != needDialog) { if (needDialog) { - reloadChannelDialog(channelID); + reloadConversation(-channelID); } else { if (foundDialog[0]) { dialogsStorage.dialogs.splice(foundDialog[1], 1); @@ -3061,14 +2950,14 @@ angular.module('myApp.services') dialogsStorage.dialogs.splice(foundDialog[1], 1); } delete historiesStorage[peerID]; - reloadChannelDialog(channelID).then(function () { + reloadConversation(-channelID).then(function () { $rootScope.$broadcast('history_reload', peerID); }); break; case 'updateChannelMessageViews': var views = update.views; - var mid = getFullMessageID(update.id, update.channel_id); + var mid = AppMessagesIDsManager.getFullMessageID(update.id, update.channel_id); var message = getMessage(mid); if (message && message.views && message.views < views) { message.views = views; @@ -3078,31 +2967,41 @@ angular.module('myApp.services') }); } break; + + case 'updateDraftMessage': + var peerID = AppPeersManager.getPeerID(update.peer); + var draft = DraftsManager.saveDraft(peerID, update.draft); + + var dialog = getDialogByPeerID(peerID)[0]; + if (dialog) { + if (dialog && draft && draft.date) { + dialog.index = generateDialogIndex(draft.date); + pushDialogToStorage(dialog); + } + $rootScope.$broadcast('dialog_draft', { + peerID: peerID, + draft: draft + }); + } + break; } }); - function reloadChannelDialog (channelID) { - var peerID = -channelID; - return $q.all([ - AppProfileManager.getChannelFull(channelID, true), - getHistory(peerID, 0) - ]).then(function (results) { - var channelResult = results[0]; - var historyResult = results[1]; - var topMsgID = historyResult.history[0]; - var dialog = { - _: 'dialog', - peer: AppPeersManager.getOutputPeer(peerID), - top_message: topMsgID, - read_inbox_max_id: channelResult.read_inbox_max_id, - read_outbox_max_id: channelResult.read_outbox_max_id, - unread_count: channelResult.unread_count, - notify_settings: channelResult.notify_settings - }; - saveChannelDialog(channelID, dialog); + function reloadConversation (peerID) { + return MtpApiManager.invokeApi('messages.getPeerDialogs', { + peers: [ + AppPeersManager.getInputPeerByID(peerID) + ] + }).then(function (dialogsResult) { + AppUsersManager.saveApiUsers(dialogsResult.users); + AppChatsManager.saveApiChats(dialogsResult.chats); + saveMessages(dialogsResult.messages); var updatedDialogs = {}; - updatedDialogs[peerID] = dialog; + angular.forEach(dialogsResult.dialogs, function (dialog) { + saveConversation(dialog); + updatedDialogs[dialog.peerID] = dialog; + }); $rootScope.$broadcast('dialogs_multiupdate', updatedDialogs); }); } @@ -3119,33 +3018,111 @@ angular.module('myApp.services') }) }); + $rootScope.$on('draft_updated', function (e, eventData) { + var peerID = eventData.peerID; + var draft = eventData.draft; + + var dialog = getDialogByPeerID(peerID)[0]; + if (dialog) { + if (dialog && draft && draft.date) { + dialog.index = generateDialogIndex(draft.date); + pushDialogToStorage(dialog); + } + } + }); + return { getConversations: getConversations, getHistory: getHistory, getSearch: getSearch, getMessage: getMessage, - getMessageLocalID: getMessageLocalID, getReplyKeyboard: getReplyKeyboard, readHistory: readHistory, readMessages: readMessages, flushHistory: flushHistory, deleteMessages: deleteMessages, - saveMessages: saveMessages, sendText: sendText, sendFile: sendFile, sendOther: sendOther, forwardMessages: forwardMessages, startBot: startBot, - openChatInviteLink: openChatInviteLink, convertMigratedPeer: convertMigratedPeer, getMessagePeer: getMessagePeer, - getFullMessageID: getFullMessageID, getMessageThumb: getMessageThumb, getMessageShareLink: getMessageShareLink, clearDialogCache: clearDialogCache, wrapForDialog: wrapForDialog, wrapForHistory: wrapForHistory, wrapReplyMarkup: wrapReplyMarkup, + wrapSingleMessage: wrapSingleMessage, regroupWrappedHistory: regroupWrappedHistory } -}); +}) + +.service('AppMessagesIDsManager', function () { + var channelLocals = {}; + var channelsByLocals = {}; + var channelCurLocal = 0; + var fullMsgIDModulus = 4294967296; + + return { + getFullMessageID: getFullMessageID, + getMessageIDInfo: getMessageIDInfo, + getMessageLocalID: getMessageLocalID, + splitMessageIDsByChannels: splitMessageIDsByChannels, + } + + function getFullMessageID (msgID, channelID) { + if (!channelID || msgID <= 0) { + return msgID; + } + msgID = getMessageLocalID(msgID); + var localStart = channelLocals[channelID]; + if (!localStart) { + localStart = (++channelCurLocal) * fullMsgIDModulus; + channelsByLocals[localStart] = channelID; + channelLocals[channelID] = localStart; + } + + return localStart + msgID; + } + + function getMessageIDInfo (fullMsgID) { + if (fullMsgID < fullMsgIDModulus) { + return [fullMsgID, 0]; + } + var msgID = fullMsgID % fullMsgIDModulus; + var channelID = channelsByLocals[fullMsgID - msgID]; + + return [msgID, channelID]; + } + + function getMessageLocalID (fullMsgID) { + if (!fullMsgID) { + return 0; + } + return fullMsgID % fullMsgIDModulus; + } + + function splitMessageIDsByChannels (mids) { + var msgIDsByChannels = {}; + var midsByChannels = {}; + var i, mid, msgChannel, channelID; + for (i = 0; i < mids.length; i++) { + mid = mids[i]; + msgChannel = getMessageIDInfo(mid); + channelID = msgChannel[1]; + if (msgIDsByChannels[channelID] === undefined) { + msgIDsByChannels[channelID] = []; + midsByChannels[channelID] = []; + } + msgIDsByChannels[channelID].push(msgChannel[0]); + midsByChannels[channelID].push(mid); + } + + return { + msgIDs: msgIDsByChannels, + mids: midsByChannels + }; + } +}) diff --git a/app/js/services.js b/app/js/services.js index a38cc00d..cad85501 100755 --- a/app/js/services.js +++ b/app/js/services.js @@ -11,7 +11,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) -.service('AppUsersManager', function ($rootScope, $modal, $modalStack, $filter, $q, qSync, MtpApiManager, RichTextProcessor, Storage, _) { +.service('AppUsersManager', function ($rootScope, $modal, $modalStack, $filter, $q, qSync, MtpApiManager, RichTextProcessor, ServerTimeManager, Storage, _) { var users = {}, usernames = {}, userAccess = {}, @@ -19,14 +19,8 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) contactsFillPromise, contactsList, contactsIndex = SearchIndexManager.createIndex(), - myID, - serverTimeOffset = 0; + myID; - Storage.get('server_time_offset').then(function (to) { - if (to) { - serverTimeOffset = to; - } - }); MtpApiManager.getUserID().then(function (id) { myID = id; }); @@ -134,10 +128,10 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) if (apiUser.status) { if (apiUser.status.expires) { - apiUser.status.expires -= serverTimeOffset; + apiUser.status.expires -= ServerTimeManager.serverTimeOffset; } if (apiUser.status.was_online) { - apiUser.status.was_online -= serverTimeOffset; + apiUser.status.was_online -= ServerTimeManager.serverTimeOffset; } } if (apiUser.pFlags.bot) { @@ -428,10 +422,10 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) user.status = update.status; if (user.status) { if (user.status.expires) { - user.status.expires -= serverTimeOffset; + user.status.expires -= ServerTimeManager.serverTimeOffset; } if (user.status.was_online) { - user.status.was_online -= serverTimeOffset; + user.status.was_online -= ServerTimeManager.serverTimeOffset; } } user.sortStatus = getUserStatusForSort(user.status); @@ -2660,7 +2654,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) return MtpApiManager.invokeApi('messages.getBotCallbackAnswer', { peer: AppPeersManager.getInputPeerByID(peerID), - msg_id: AppMessagesManager.getMessageLocalID(id), + msg_id: AppMessagesIDsManager.getMessageLocalID(id), data: button.data }, {timeout: 1, stopTime: -1, noErrorBox: true}).then(function (callbackAnswer) { if (typeof callbackAnswer.message != 'string' || @@ -4218,7 +4212,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) }) -.service('LocationParamsService', function (qSync, $rootScope, $routeParams, AppPeersManager, AppUsersManager, AppMessagesManager, PeersSelectService, AppStickersManager, ErrorService) { +.service('LocationParamsService', function (qSync, $rootScope, $routeParams, AppPeersManager, AppUsersManager, AppChatsManager, AppMessagesManager, AppMessagesIDsManager, MtpApiManager, ApiUpdatesManager, PeersSelectService, AppStickersManager, ErrorService) { var tgAddrRegExp = /^(web\+)?tg:(\/\/)?(.+)/; @@ -4263,7 +4257,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) params.startParam = matches[3]; } else if (matches[2] == 'post') { - params.messageID = AppMessagesManager.getFullMessageID(parseInt(matches[3]), -peerID); + params.messageID = AppMessagesIDsManager.getFullMessageID(parseInt(matches[3]), -peerID); } $rootScope.$broadcast('history_focus', params); @@ -4272,7 +4266,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) } if (matches = url.match(/^join\?invite=(.+)$/)) { - AppMessagesManager.openChatInviteLink(matches[1]); + openChatInviteLink(matches[1]); return true; } @@ -4444,6 +4438,52 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) }); } + function openChatInviteLink (hash) { + return MtpApiManager.invokeApi('messages.checkChatInvite', { + hash: hash + }).then(function (chatInvite) { + var chatTitle; + if (chatInvite._ == 'chatInviteAlready') { + AppChatsManager.saveApiChat(chatInvite.chat); + var canJump = !chatInvite.chat.pFlags.left || + AppChatsManager.isChannel(chatInvite.chat.id) && chatInvite.chat.username; + if (canJump) { + return $rootScope.$broadcast('history_focus', { + peerString: AppChatsManager.getChatString(chatInvite.chat.id) + }); + } + chatTitle = chatInvite.chat.title; + } else { + chatTitle = chatInvite.title; + } + ErrorService.confirm({ + type: (chatInvite.pFlags.channel && !chatInvite.pFlags.megagroup) ? 'JOIN_CHANNEL_BY_LINK' : 'JOIN_GROUP_BY_LINK', + title: chatTitle + }).then(function () { + return MtpApiManager.invokeApi('messages.importChatInvite', { + hash: hash + }).then(function (updates) { + ApiUpdatesManager.processUpdateMessage(updates); + + if (updates.chats && updates.chats.length == 1) { + $rootScope.$broadcast('history_focus', {peerString: AppChatsManager.getChatString(updates.chats[0].id) + }); + } + else if (updates.updates && updates.updates.length) { + for (var i = 0, len = updates.updates.length, update; i < len; i++) { + update = updates.updates[i]; + if (update._ == 'updateNewMessage') { + $rootScope.$broadcast('history_focus', {peerString: AppChatsManager.getChatString(update.message.to_id.chat_id) + }); + break; + } + } + } + }); + }); + }); + } + return { start: start, shareUrl: shareUrl @@ -4451,52 +4491,197 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) }) -.service('DraftsManager', function (qSync, Storage) { +.service('DraftsManager', function ($rootScope, qSync, MtpApiManager, ApiUpdatesManager, AppMessagesIDsManager, AppPeersManager, RichTextProcessor, Storage, ServerTimeManager) { + + var cachedServerDrafts = {}; - var localDrafts = {}; + $rootScope.$on('apiUpdate', function (e, update) { + if (update._ != 'updateDraftMessage') { + return; + } + var peerID = AppPeersManager.getPeerID(update.peer); + saveDraft(peerID, update.draft, true); + }); return { getDraft: getDraft, + getServerDraft: getServerDraft, saveDraft: saveDraft, changeDraft: changeDraft, syncDraft: syncDraft }; - function getDraft (peerID, options) { + function getDraft (peerID, unsyncOnly) { + console.warn(dT(), 'get draft', peerID, unsyncOnly); return Storage.get('draft' + peerID).then(function (draft) { - if (typeof draft === 'string' && draft.length > 0) { - draft = { - text: draft - }; + if (typeof draft === 'string') { + if (draft.length > 0) { + draft = { + text: draft + }; + } else { + draft = false; + } } - if (draft === false || draft == null) { - draft = ''; + if (!draft && !unsyncOnly) { + draft = getServerDraft(peerID); + console.warn(dT(), 'server', draft); + } else { + console.warn(dT(), 'local', draft); } - + var replyToMsgID = draft && draft.replyToMsgID; + if (replyToMsgID) { + var channelID = AppPeersManager.isChannel(peerID) ? -peerID : false; + draft.replyToMsgID = AppMessagesIDsManager.getFullMessageID(replyToMsgID, channelID); + } + return draft; }); } - function saveDraft(peerID, draftData) { - localDrafts[peerID] = draftData; + function getServerDraft(peerID) { + var cached = cachedServerDrafts[peerID]; + if (cached !== undefined) { + return cached; + } + return false; + } + + function saveDraft(peerID, apiDraft, notify) { + if (notify) { + console.warn(dT(), 'save draft', peerID, apiDraft, notify); + } + var draft = processApiDraft(apiDraft); + cachedServerDrafts[peerID] = draft; + + if (notify) { + changeDraft(peerID, draft); + $rootScope.$broadcast('draft_updated', { + peerID: peerID, + draft: draft + }); + } + + return draft; } - function changeDraft(peerID, message, options) { - options = options || {}; - if (typeof message === 'string' || options.replyToMsgID) { - var localDraft = { - text: message, - replyToMsgID: replyToMsgID + function changeDraft(peerID, draft) { + console.warn(dT(), 'change draft', peerID, draft); + if (!peerID) { + console.trace('empty peerID'); + } + if (!draft) { + draft = { + text: '', + replyToMsgID: 0 }; + } + draft.replyToMsgID = draft.replyToMsgID + ? AppMessagesIDsManager.getMessageLocalID(draft.replyToMsgID) + : 0; + + var draftKey = 'draft' + peerID; + + if (!isEmptyDraft(draft)) { var backupDraftObj = {}; - backupDraftObj['draft' + peerID] = localDraft; + backupDraftObj[draftKey] = draft; Storage.set(backupDraftObj); } else { - Storage.remove('draft' + peerID); + Storage.remove(draftKey); } } - function syncDraft(peerID) { + function draftsAreEqual(draft1, draft2) { + var isEmpty1 = isEmptyDraft(draft1); + var isEmpty2 = isEmptyDraft(draft2); + if (isEmpty1 && isEmpty2) { + return true; + } + if (isEmpty1 != isEmpty2) { + return false; + } + if (draft1.replyToMsgID != draft2.replyToMsgID) { + return false; + } + if (draft1.text != draft2.text) { + return false; + } + return true; + } + + function isEmptyDraft(draft) { + if (!draft) { + return true; + } + if (draft.replyToMsgID > 0) { + return false; + } + if (typeof draft.text !== 'string' || !draft.text.length) { + return true; + } + return false; + } + function processApiDraft(draft) { + if (!draft || draft._ != 'draftMessage') { + return false; + } + + var entities = RichTextProcessor.parseEntities(draft.message); + var serverEntities = draft.entities || []; + entities = RichTextProcessor.mergeEntities(entities, serverEntities); + + var text = RichTextProcessor.wrapDraftText(draft.message, {entities: entities}); + var richMessage = RichTextProcessor.wrapRichText(draft.message, {noLinks: true, noLinebreaks: true}); + + return { + text: text, + richMessage: richMessage, + replyToMsgID: draft.reply_to_msg_id || 0, + date: draft.date - ServerTimeManager.serverTimeOffset + } + } + + function syncDraft(peerID) { + console.warn(dT(), 'sync draft', peerID); + getDraft(peerID, true).then(function (localDraft) { + var serverDraft = cachedServerDrafts[peerID]; + if (draftsAreEqual(serverDraft, localDraft)) { + console.warn(dT(), 'equal drafts', localDraft, serverDraft); + return; + } + console.warn(dT(), 'changed draft', localDraft, serverDraft); + var params = { + flags: 0, + peer: AppPeersManager.getInputPeerByID(peerID) + }; + var draftObj; + if (isEmptyDraft(localDraft)) { + draftObj = {_: 'draftMessageEmpty'}; + params.message = ''; + } else { + draftObj = {_: 'draftMessage'}; + var message = localDraft.text; + var entities = []; + message = RichTextProcessor.parseEmojis(message); + message = RichTextProcessor.parseMarkdown(message, entities); + if (localDraft.replyToMsgID > 0) { + params.flags |= 1; + params.reply_to_msg_id = localDraft.replyToMsgID; + draftObj.reply_to_msg_id = localDraft.replyToMsgID; + } + if (entities.length) { + params.flags |= 8; + params.entities = entities; + draftObj.entities = entities; + } + params.message = message; + draftObj.message = message; + } + MtpApiManager.invokeApi('messages.saveDraft', params).then(function () { + draftObj.date = tsNow(true) + ServerTimeManager.serverTimeOffset; + saveDraft(peerID, draftObj, true); + }); + }); } }) \ No newline at end of file diff --git a/app/less/app.less b/app/less/app.less index d6b2a6ca..63cf01a8 100644 --- a/app/less/app.less +++ b/app/less/app.less @@ -1310,9 +1310,12 @@ a.im_dialog { .im_short_message_text { color: #808080; } - + .im_dialog_draft_from { + color: #c05f5a; + } } + a.im_dialog:hover, a.im_dialog_selected { .im_short_message_text { @@ -1326,7 +1329,8 @@ a.im_dialog_selected { .im_short_message_media, .im_short_message_service, .im_short_message_text, - .im_dialog_message { + .im_dialog_message, + .im_dialog_draft_from { color: #fff; } } diff --git a/app/partials/desktop/dialog.html b/app/partials/desktop/dialog.html index 95e9e247..b584dffd 100644 --- a/app/partials/desktop/dialog.html +++ b/app/partials/desktop/dialog.html @@ -22,17 +22,26 @@ -
- - - -
+
-
-
+
+ + + +
+ +
+ + + + +
+ +
-
+ +
@@ -62,4 +71,5 @@
+ diff --git a/app/partials/desktop/im.html b/app/partials/desktop/im.html index 507c5a73..374e8414 100644 --- a/app/partials/desktop/im.html +++ b/app/partials/desktop/im.html @@ -128,6 +128,7 @@
+ @@ -187,7 +188,7 @@
- +
diff --git a/app/partials/mobile/dialog.html b/app/partials/mobile/dialog.html index 95e9e247..b584dffd 100644 --- a/app/partials/mobile/dialog.html +++ b/app/partials/mobile/dialog.html @@ -22,17 +22,26 @@
-
- - - -
+
-
-
+
+ + + +
+ +
+ + + + +
+ +
-
+ +
@@ -62,4 +71,5 @@
+