Browse Source

Added support for drafts

master
Igor Zhukov 8 years ago
parent
commit
7909731d1e
  1. 113
      app/js/controllers.js
  2. 96
      app/js/lib/ng_utils.js
  3. 1
      app/js/locales/en-us.json
  4. 493
      app/js/messages_manager.js
  5. 259
      app/js/services.js
  6. 8
      app/less/app.less
  7. 26
      app/partials/desktop/dialog.html
  8. 3
      app/partials/desktop/im.html
  9. 26
      app/partials/mobile/dialog.html

113
app/js/controllers.js

@ -753,6 +753,21 @@ angular.module('myApp.controllers', ['myApp.i18n']) @@ -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']) @@ -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']) @@ -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']) @@ -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']) @@ -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']) @@ -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']) @@ -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']) @@ -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']) @@ -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']) @@ -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']) @@ -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']) @@ -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]*)$/;

96
app/js/lib/ng_utils.js

@ -1258,6 +1258,7 @@ angular.module('izhukov.utils', []) @@ -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', []) @@ -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', []) @@ -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;
})

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

@ -291,6 +291,7 @@ @@ -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",

493
app/js/messages_manager.js

@ -9,7 +9,7 @@ @@ -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') @@ -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') @@ -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') @@ -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') @@ -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') @@ -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') @@ -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') @@ -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') @@ -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') @@ -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') @@ -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') @@ -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') @@ -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') @@ -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') @@ -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') @@ -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') @@ -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') @@ -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') @@ -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') @@ -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') @@ -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') @@ -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') @@ -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') @@ -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') @@ -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') @@ -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') @@ -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') @@ -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') @@ -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') @@ -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') @@ -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') @@ -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') @@ -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') @@ -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') @@ -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') @@ -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') @@ -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') @@ -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') @@ -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') @@ -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') @@ -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') @@ -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
};
}
})

259
app/js/services.js

@ -11,7 +11,7 @@ @@ -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']) @@ -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']) @@ -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']) @@ -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']) @@ -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']) @@ -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']) @@ -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']) @@ -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']) @@ -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']) @@ -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);
});
});
}
})

8
app/less/app.less

@ -1310,9 +1310,12 @@ a.im_dialog { @@ -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 { @@ -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;
}
}

26
app/partials/desktop/dialog.html

@ -22,17 +22,26 @@ @@ -22,17 +22,26 @@
<span my-peer-link="dialogMessage.peerID" verified="true"></span>
</div>
<div ng-if="dialogMessage.typing > 0" class="im_dialog_message im_dialog_message_typing">
<span class="im_short_message_service" my-i18n="im_conversation_group_typing">
<my-i18n-param name="name"><span my-peer-link="dialogMessage.typing" short="true" class="im_dialog_chat_from_wrap"></span></my-i18n-param><my-i18n-param name="dots"><span my-loading-dots></span></my-i18n-param>
</span>
</div>
<div ng-switch="dialogMessage.typing ? 'typing' : (!dialogMessage.unreadCount && dialogMessage.draft ? 'draft' : (dialogMessage.deleted ? 'deleted' : 'message'))">
<div class="im_dialog_message_notyping" ng-switch="dialogMessage.deleted">
<div ng-switch-when="true" class="im_dialog_message">
<div ng-switch-when="typing" class="im_dialog_message im_dialog_message_typing">
<span class="im_short_message_service" my-i18n="im_conversation_group_typing">
<my-i18n-param name="name"><span my-peer-link="dialogMessage.typing" short="true" class="im_dialog_chat_from_wrap"></span></my-i18n-param><my-i18n-param name="dots"><span my-loading-dots></span></my-i18n-param>
</span>
</div>
<div ng-switch-when="draft" class="im_dialog_message">
<span class="im_dialog_chat_from_wrap">
<span class="im_dialog_draft_from" my-i18n="conversation_draft"></span>
</span>
<span class="im_short_message_text" ng-bind-html="dialogMessage.draft.richMessage"></span>
</div>
<div ng-switch-when="deleted" class="im_dialog_message">
<span class="im_short_message_text" my-i18n="conversation_message_deleted"></span>
</div>
<div ng-switch-default class="im_dialog_message">
<div ng-switch-when="message" class="im_dialog_message">
<span ng-switch="dialogMessage.peerID > 0 || dialogMessage.fromID < 0">
<span ng-switch-when="true">
<span class="im_dialog_chat_from_wrap" ng-if="dialogMessage.pFlags.out && dialogMessage.fromID > 0">
@ -62,4 +71,5 @@ @@ -62,4 +71,5 @@
</div>
</div>
</a>

3
app/partials/desktop/im.html

@ -128,6 +128,7 @@ @@ -128,6 +128,7 @@
<div my-arc-progress stroke="3" width="26"></div>
</div>
<div ng-switch-default class="im_history_typing" ng-show="historyState.typing.length > 0 &amp;&amp; !historyFilter.mediaType &amp;&amp; !state.empty" ng-switch="historyState.typing.length" my-i18n>
<span ng-switch-when="0"></span>
<span ng-switch-when="1" my-i18n-format="im_one_typing"></span>
<span ng-switch-when="2" my-i18n-format="im_two_typing"></span>
<span ng-switch-default my-i18n-format="im_many_typing"></span>
@ -187,7 +188,7 @@ @@ -187,7 +188,7 @@
<div class="im_send_form_inline_results" my-inline-results="inlineResults"></div>
<div class="im_send_reply_wrap" ng-if="draftMessage.replyToMessage != null">
<a class="im_send_reply_cancel" ng-mousedown="draftMessage.replyClear()"><i class="icon icon-reply-bar"></i><i class="icon icon-reply-bar"></i></a>
<a class="im_send_reply_cancel" ng-mousedown="draftMessage.replyClear(true)"><i class="icon icon-reply-bar"></i><i class="icon icon-reply-bar"></i></a>
<a class="im_message_reply_wrap" my-reply-message="draftMessage.replyToMessage" watch="true"></a>
</div>

26
app/partials/mobile/dialog.html

@ -22,17 +22,26 @@ @@ -22,17 +22,26 @@
<span my-peer-link="dialogMessage.peerID" verified="true"></span>
</div>
<div ng-if="dialogMessage.typing > 0" class="im_dialog_message im_dialog_message_typing">
<span class="im_short_message_service" my-i18n="im_conversation_group_typing">
<my-i18n-param name="name"><span my-peer-link="dialogMessage.typing" short="true" class="im_dialog_chat_from_wrap"></span></my-i18n-param><my-i18n-param name="dots"><span my-loading-dots></span></my-i18n-param>
</span>
</div>
<div ng-switch="dialogMessage.typing ? 'typing' : (!dialogMessage.unreadCount && dialogMessage.draft ? 'draft' : (dialogMessage.deleted ? 'deleted' : 'message'))">
<div class="im_dialog_message_notyping" ng-switch="dialogMessage.deleted">
<div ng-switch-when="true" class="im_dialog_message">
<div ng-switch-when="typing" class="im_dialog_message im_dialog_message_typing">
<span class="im_short_message_service" my-i18n="im_conversation_group_typing">
<my-i18n-param name="name"><span my-peer-link="dialogMessage.typing" short="true" class="im_dialog_chat_from_wrap"></span></my-i18n-param><my-i18n-param name="dots"><span my-loading-dots></span></my-i18n-param>
</span>
</div>
<div ng-switch-when="draft" class="im_dialog_message">
<span class="im_dialog_chat_from_wrap">
<span class="im_dialog_draft_from" my-i18n="conversation_draft"></span>
</span>
<span class="im_short_message_text" ng-bind-html="dialogMessage.draft.richMessage"></span>
</div>
<div ng-switch-when="deleted" class="im_dialog_message">
<span class="im_short_message_text" my-i18n="conversation_message_deleted"></span>
</div>
<div ng-switch-default class="im_dialog_message">
<div ng-switch-when="message" class="im_dialog_message">
<span ng-switch="dialogMessage.peerID > 0 || dialogMessage.fromID < 0">
<span ng-switch-when="true">
<span class="im_dialog_chat_from_wrap" ng-if="dialogMessage.pFlags.out && dialogMessage.fromID > 0">
@ -62,4 +71,5 @@ @@ -62,4 +71,5 @@
</div>
</div>
</a>

Loading…
Cancel
Save