From 0133ffd486bae5c3a36c8fba6210589cb484b022 Mon Sep 17 00:00:00 2001 From: Simon Grim Date: Fri, 14 Jul 2017 00:56:30 +0500 Subject: [PATCH] tune DMS and mentions processing, add DMs caching to localStorage --- js/interface_common.js | 15 +- js/tmobile.js | 60 ++++--- js/twister_actions.js | 135 +++++++++++++-- js/twister_directmsg.js | 186 ++++++--------------- js/twister_newmsgs.js | 356 +++++++++++++++++++++++++++++++--------- js/twister_timeline.js | 17 +- 6 files changed, 501 insertions(+), 268 deletions(-) diff --git a/js/interface_common.js b/js/interface_common.js index 664bcf3..d2ea20c 100644 --- a/js/interface_common.js +++ b/js/interface_common.js @@ -672,17 +672,10 @@ function openMentionsModalHandler(peerAlias) { }); var req = queryStart(modal.content.find('.postboard-posts'), peerAlias, 'mention'); - modal.content.find('.postboard-news') - .on('click', - {req: req, cbFunc: (peerAlias === defaultScreenName) ? resetMentionsCount : ''}, - handleClickDisplayPendingTwists - ) - ; + modal.content.find('.postboard-news').on('click', {req: req}, handleClickDisplayPendingTwists); - if (peerAlias === defaultScreenName) { + if (peerAlias === defaultScreenName) modal.content.on('scroll', handleMentionsModalScroll); - resetMentionsCount(); - } } function openFollowersModal(peerAlias) { @@ -943,9 +936,9 @@ function addToCommonDMsList(list, targetAlias, message) { getFullname(targetAlias, item.find('a.post-info-name')); } - if (_newDMsPerUser[targetAlias] > 0) + if (twister.DMs[targetAlias].lengthNew > 0) item.addClass('new') - .find('.messages-qtd').text(_newDMsPerUser[targetAlias]).show(); + .find('.messages-qtd').text(twister.DMs[targetAlias].lengthNew).show(); var items = list.children(); for (var i = 0; i < items.length; i++) { diff --git a/js/tmobile.js b/js/tmobile.js index bf69bbe..7947829 100644 --- a/js/tmobile.js +++ b/js/tmobile.js @@ -273,23 +273,37 @@ var router=new $.mobile.Router( $.mobile.showPageLoadingMsg(); initializeTwister( true, true, function() { $.mobile.showPageLoadingMsg(); - requestDMsnippetList($('#directmsg .direct-messages-list')); + modalDMsSummaryDraw($('#directmsg .direct-messages-list')); }); }, dmchat: function(type,match,ui) { var params=router.getParams(match[1]); $.mobile.showPageLoadingMsg(); initializeTwister( true, true, function() { - var user = params.user; - var dmConvo = $('#dmchat .direct-messages-thread'); - $("#dmchat .rtitle").text("Chat @" + user); + var peerAlias = params.user; + var board = $('#dmchat .direct-messages-thread').empty(); + + $('#dmchat .rtitle').text('Chat @' + peerAlias); $("#dmchat textarea").val(""); - dmConvo.html(""); - installDMSendClick(); + installDMSendClick(peerAlias); $.mobile.showPageLoadingMsg(); - dmChatUser = user; - requestDmConversation(dmConvo,user); + + tmobileQueryReq = queryStart(board, peerAlias, 'direct', undefined, 2000, { + boardAutoAppend: true, + lastId: 0, + lengthNew: 0, + ready: function (req, peerAlias) { + twister.DMs[peerAlias] = twister.res[req]; + }, + readyReq: peerAlias, + drawFinish: function (req) { + setTimeout($.MAL.dmConversationLoaded, 200, twister.res[req].board); + }, + skidoo: function (req) { + return $.mobile.activePage.attr('id') !== 'dmchat' || req !== tmobileQueryReq; + } + }); }); }, search: function(type,match,ui) { @@ -390,23 +404,21 @@ function installSubmitClick() { }); } -function installDMSendClick() { - var $postSubmit = $(".dm-submit"); - $postSubmit.unbind('click').click(function(e){ - e.stopPropagation(); - e.preventDefault(); - var $this = $( this ); - var $replyText = $this.closest(".post-area-new").find("textarea"); +function installDMSendClick(peerAlias) { + $('.dm-submit').off('click').on('click', {peerAlias: peerAlias}, + function (event) { + muteEvent(event, true); - var $dmConversation = $(".directMessages"); + var elemTextArea = $(event.target).closest('.post-area-new').find('textarea'); + if (!elemTextArea.val()) + return; - var s = encode_utf8($replyText.val()); - newDirectMsg(s, dmChatUser); - $replyText.val(""); - }); + newDirectMsg(encode_utf8(elemTextArea.val()), event.data.peerAlias); + elemTextArea.val(''); + } + ); } - function installRetransmitConfirmClick() { var $postConfirmRt = $(".retransmit-confirm"); $postConfirmRt.unbind('click').click(function(e){ @@ -535,15 +547,13 @@ function setupHashtagOrMention(board, query, resource) { $.mobile.showPageLoadingMsg(); board.empty(); - var req = queryStart(board, query, resource, undefined, undefined, { + tmobileQueryReq = queryStart(board, query, resource, undefined, undefined, { boardAutoAppend: true, skidoo: function (req) { var curPage = $.mobile.activePage.attr('id'); return (curPage !== 'mentions' && curPage !== 'hashtag') || req !== tmobileQueryReq; } }); - - tmobileQueryReq = req; } // every 2 seconds do something page specific. @@ -565,8 +575,6 @@ function tmobileTick() { } }, {} ); } - if (curPage === 'dmchat') - requestDmConversation($('#dmchat .direct-messages-thread'), dmChatUser); } $(document).bind('mobileinit', function () { diff --git a/js/twister_actions.js b/js/twister_actions.js index 3626440..8cdd6c7 100644 --- a/js/twister_actions.js +++ b/js/twister_actions.js @@ -436,6 +436,24 @@ function updateProfilePosts(postsView, username, useGetposts) { }); } +function queryCreateRes(query, resource, extra) { + var req = query + '@' + resource; + twister.res[req] = { + query: query, + resource: resource, + lengthCached: 0, + twists: { + cached: {}, + pending: [] + } + }; + if (extra) + for (i in extra) + twister.res[req][i] = extra[i]; + + return twister.res[req]; +} + function queryStart(board, query, resource, timeoutArgs, intervalTimeout, extra) { var req = query + '@' + resource; @@ -444,6 +462,7 @@ function queryStart(board, query, resource, timeoutArgs, intervalTimeout, extra) board: board, query: query, resource: resource, + lengthCached: 0, twists: { cached: {}, pending: [] @@ -462,6 +481,15 @@ function queryStart(board, query, resource, timeoutArgs, intervalTimeout, extra) if (twister.res[req].twists.pending.indexOf(i) === -1) twister.res[req].twists.pending.push(i); + if (extra) { + if (typeof extra.drawFinish === 'function') { + twister.res[req].drawFinish = extra.drawFinish; + twister.res[req].drawFinishReq = extra.drawFinishReq; + } + if (typeof extra.skidoo === 'function') + twister.res[req].skidoo = extra.skidoo; + } + queryPendingDraw(req); } @@ -510,27 +538,45 @@ function queryRequest(req) { } else if (twister.res[req].resource === 'fav') twisterRpc('getfavs', [twister.res[req].query, 1000], queryProcess, req); - else + else if (twister.res[req].resource === 'direct') { + var lengthStandard = 100; // FIXME there may be the gap between .lastId and the lesser twist.id in response greater than 100 (very rare case) + if (twister.res[req].lengthCached < Math.min(twister.res[req].lastId, lengthStandard) + && !twister.res[req].triedToReCache) { + twister.res[req].triedToReCache = true; + var length = Math.min(twister.res[req].lastId + 1, lengthStandard); + var query = [{username: twister.res[req].query, max_id: twister.res[req].lastId}]; + } else + var length = lengthStandard, query = [{username: twister.res[req].query, since_id: twister.res[req].lastId}]; + + twisterRpc('getdirectmsgs', [defaultScreenName, length, query], + queryProcess, req, + function (req, res) { + console.warn(polyglot.t('ajax_error', {error: (res && res.message) ? res.message : res})); + } + ); + } else dhtget(twister.res[req].query, twister.res[req].resource, 'm', queryProcess, req, twister.res[req].timeoutArgs); } -function queryProcess(req, twists) { - if (!req || !twister.res[req] || !twists || !twists.length) +function queryProcess(req, res) { + if (!req || !twister.res[req] || typeof res !== 'object' || $.isEmptyObject(res)) return; var lengthNew = 0; var lengthPending = twister.res[req].twists.pending.length; if (twister.res[req].resource === 'mention' && twister.res[req].query === defaultScreenName) - lengthNew = queryPendingPushMentions(req, twists); + lengthNew = queryPendingPushMentions(req, res); + else if (twister.res[req].resource === 'direct') + lengthNew = queryPendingPushDMs(res); else - lengthNew = queryPendingPush(req, twists); + lengthNew = queryPendingPush(req, res); if (typeof twister.res[req].skidoo === 'function' && twister.res[req].skidoo(req)) return; - if (lengthNew) + if (lengthNew) { if (twister.res[req].resource === 'mention' && twister.res[req].query === defaultScreenName) { $.MAL.updateNewMentionsUI(twister.res[req].lengthNew); $.MAL.soundNotifyMentions(); @@ -550,6 +596,25 @@ function queryProcess(req, twists) { $.MAL.showMentions(defaultScreenName); }).bind({req: req}) }); + } else if (twister.res[req].resource === 'direct') { + if (twister.res[req].query[0] !== '*') + $.MAL.updateNewDMsUI(getNewDMsCount()); + else + $.MAL.updateNewGroupDMsUI(getNewGroupDMsCount()); + + $.MAL.soundNotifyDM(); + if (!$.mobile && $.Options.showDesktopNotifDMs.val === 'enable') + $.MAL.showDesktopNotification({ + body: twister.res[req].query[0] === '*' ? + polyglot.t('You got') + ' ' + polyglot.t('new_group_messages', getNewGroupDMsCount()) + '.' + : polyglot.t('You got') + ' ' + polyglot.t('new_direct_messages', getNewDMsCount()) + '.', + tag: 'twister_notification_new_DMs', + timeout: $.Options.showDesktopNotifDMsTimer.val, + funcClick: (function () { + focusModalWithElement(twister.res[this.req].board); + }).bind({req: req}) + }); + // TODO new DMs counters on minimized modals' } else if (!$.mobile && $.Options.showDesktopNotifPostsModal.val === 'enable' && (twister.res[req].resource !== 'mention' || twister.res[req].query !== defaultScreenName) && twister.res[req].board && isModalWithElemExists(twister.res[req].board) @@ -559,7 +624,7 @@ function queryProcess(req, twists) { + polyglot.t('in search result') + '.', tag: 'twister_notification_new_posts_modal', timeout: $.Options.showDesktopNotifPostsModalTimer.val, - funcClick: (function() { + funcClick: (function () { focusModalWithElement(twister.res[this.req].board, function (req) { twister.res[req].board.closest('.postboard') @@ -569,6 +634,7 @@ function queryProcess(req, twists) { ); }).bind({req: req}) }); + } if (twister.res[req].twists.pending.length > lengthPending) { // there is some twists may be which are not considered new so lengthNew equals zero (mentions thing) if (!twister.res[req].board || (!$.mobile && !isModalWithElemExists(twister.res[req].board))) @@ -619,6 +685,7 @@ function queryPendingPush(req, twists) { lengthNew++; twister.res[req].twists.cached[j] = twists[i]; + twister.res[req].lengthCached++; twister.res[req].twists.pending.push(j); } } @@ -627,13 +694,57 @@ function queryPendingPush(req, twists) { } function queryPendingDraw(req) { - var twists = []; - for (var i = 0; i < twister.res[req].twists.pending.length; i++) - twists.push(twister.res[req].twists.cached[twister.res[req].twists.pending[i]]); + var twists = [], length = 0; + + if (twister.res[req].resource === 'direct') { + for (var j = 0; j < twister.res[req].twists.pending.length; j++) { + var twist = twister.res[req].twists.cached[twister.res[req].twists.pending[j]]; + for (var i = 0; i < length; i++) + if (twist.id < twists[i].id) { + twists.splice(i, 0, twist); + break; + } + + if (length === twists.length) + twists.push(twist); + + length++; + } + attachPostsToStream(twister.res[req].board, twists, false, + function (twist, req) { + return {item: postToElemDM(twist, req.peerAliasLocal, req.peerAliasRemote) + .attr('data-id', twist.id), time: twist.time}; + }, + {peerAliasLocal: defaultScreenName, peerAliasRemote: twister.res[req].query} + ); + resetNewDMsCountForPeer(twister.res[req].query); + } else { + for (var j = 0; j < twister.res[req].twists.pending.length; j++) { + var twist = twister.res[req].twists.cached[twister.res[req].twists.pending[j]]; + for (var i = 0; i < length; i++) + if (twist.userpost.time > twists[i].userpost.time) { + twists.splice(i, 0, twist); + break; + } + + if (length === twists.length) + twists.push(twist); - attachPostsToStream(twister.res[req].board, twists, false); + length++; + } + attachPostsToStream(twister.res[req].board, twists, true, + function (twist) { + return {item: postToElem(twist, 'original'), time: twist.userpost.time}; + } + ); + if (twister.res[req].resource === 'mention' && twister.res[req].query === defaultScreenName) + resetMentionsCount(); + } queryPendingClear(req); - $.MAL.postboardLoaded(); + if (typeof twister.res[req].drawFinish === 'function') + twister.res[req].drawFinish(req, twister.res[req].drawFinishReq); + else + $.MAL.postboardLoaded(); } diff --git a/js/twister_directmsg.js b/js/twister_directmsg.js index c21877d..f82cb8b 100644 --- a/js/twister_directmsg.js +++ b/js/twister_directmsg.js @@ -5,120 +5,6 @@ var _groupMsgInviteToGroupQueue = []; -function requestDMsnippetList(elemList, forGroup) { - var followList = []; - for (var i = 0; i < followingUsers.length; i++) - followList.push({username: followingUsers[i]}); - for (var i = 0; i < groupChatAliases.length; i++) - followList.push({username: groupChatAliases[i]}); - - twisterRpc('getdirectmsgs', [defaultScreenName, 1, followList], - processDMsnippet, {elemList: elemList, forGroup: forGroup}, - function(req, ret) {console.log('ajax error:' + ret);}, null - ); -} - -function processDMsnippet(req, DMs) { - req.elemList.empty(); - - for (var alias in DMs) - if ((req.forGroup && alias[0] === '*') || (!req.forGroup && alias[0] !== '*')) - addToCommonDMsList(req.elemList, alias, DMs[alias][0]); - - $.MAL.commonDMsListLoaded(); -} - -function requestDmConversationModal(postboard, peerAlias) { - if (!isModalWithElemExists(postboard)) - return; - - requestDmConversation(postboard, peerAlias); - setTimeout(requestDmConversationModal, 1000, postboard, peerAlias); -} - -function requestDmConversation(postboard, peerAlias) { - var since_id = undefined; - - var oldItems = postboard.children(); - if (oldItems.length) - since_id = parseInt(oldItems.eq(oldItems.length - 1).attr('data-id')); - - var userDmReq = [{username: peerAlias}]; - if (typeof since_id !== 'undefined') - userDmReq[0].since_id = since_id; - - var count = 100; - twisterRpc('getdirectmsgs', [defaultScreenName, count, userDmReq], - function(req, ret) {processDmConversation(req.postboard, req.peerAlias, ret);}, - {postboard: postboard, peerAlias: peerAlias}, - function(req, ret) { - var msg = (ret.message) ? ret.message : ret; - alert(polyglot.t('ajax_error', {error: msg})); - } - ); -} - -function processDmConversation(stream, peerAlias, posts) { - if (!isModalWithElemExists(stream)) - return; - - var streamItems = stream.children(); - var streamPostsIDs = []; - var newPosts = 0; - - for (var i = 0; i < streamItems.length; i++) { - streamPostsIDs.push(parseInt(streamItems.eq(i).attr('data-id'))); - } - - if (posts[peerAlias] && posts[peerAlias].length) { - for (var i = 0; i < posts[peerAlias].length; i++) { - if (streamPostsIDs.indexOf(posts[peerAlias][i].id) === -1) { - var lastPostID = posts[peerAlias][i].id; - newPosts++; - postToElemDM(posts[peerAlias][i], defaultScreenName, peerAlias) - .attr('data-id', lastPostID) - .appendTo(stream) - ; - streamPostsIDs.push(lastPostID); - } - } - $.MAL.dmConversationLoaded(stream); - } - - if (newPosts) { - resetNewDMsCountForUser(peerAlias, lastPostID); - - if (getHashOfMinimizedModalWithElem(stream)) { - $.MAL.soundNotifyDM(); - _newDMsPerUser[peerAlias] += newPosts; - if (peerAlias[0] === '*') - $.MAL.updateNewGroupDMsUI(getNewGroupDMsCount()); - else - $.MAL.updateNewDMsUI(getNewDMsCount()); - - if (!$.hasOwnProperty('mobile') && $.Options.showDesktopNotifDMs.val === 'enable') - $.MAL.showDesktopNotification({ - body: peerAlias[0] === '*' ? - polyglot.t('You got') + ' ' + polyglot.t('new_group_messages', newPosts) + '.' - : polyglot.t('You got') + ' ' + polyglot.t('new_direct_messages', newPosts) + '.', - tag: 'twister_notification_new_DMs', - timeout: $.Options.showDesktopNotifDMsTimer.val, - funcClick: (function() { - focusModalWithElement(this.postboard, - function (peerAlias) { - _newDMsPerUser[peerAlias] = 0; - if (peerAlias[0] === '*') - $.MAL.updateNewGroupDMsUI(getNewGroupDMsCount()); - else - $.MAL.updateNewDMsUI(getNewDMsCount()); - }, this.peerAlias); - }).bind({postboard: stream, peerAlias: peerAlias}) - }); - // TODO here we need to set new DMs counter on minimized modal button - } - } -} - function directMsgSubmit(e) { e.stopPropagation(); e.preventDefault(); @@ -161,6 +47,20 @@ function newDirectMsg(msg, peerAlias) { alert(polyglot.t('Internal error: lastPostId unknown (following yourself may fix!)')); } +function modalDMsSummaryDraw(elem, group) { + elem.empty(); + + for (var peerAlias in twister.DMs) + if (group ? peerAlias[0] === '*' : peerAlias[0] !== '*') + for (var j in twister.DMs[peerAlias].twists.cached) + if (twister.DMs[peerAlias].lastId === twister.DMs[peerAlias].twists.cached[j].id) { + addToCommonDMsList(elem, peerAlias, twister.DMs[peerAlias].twists.cached[j]); + break; + } + + $.MAL.commonDMsListLoaded(); +} + // dispara o modal de direct messages function openCommonDMsModal() { if (!defaultScreenName) { @@ -174,19 +74,16 @@ function openCommonDMsModal() { title: polyglot.t('Direct Messages') }); - requestDMsnippetList(modal.content.find('.direct-messages-list')); + modalDMsSummaryDraw(modal.content.find('.direct-messages-list')); modal.self.find('.mark-all-as-read') .css('display', 'inline') .attr('title', polyglot.t('Mark all as read')) .on('click', function (event) { - for (var user in _newDMsPerUser) { - if (user[0] !== '*') - _newDMsPerUser[user] = 0; - } - saveDMsToStorage(); - $.MAL.updateNewDMsUI(getNewDMsCount()); - $(event.target).closest('.directMessages').find('.direct-messages-list .messages-qtd').hide(); + resetNewDMsCount(); + var elem = $(event.target).closest('.directMessages').find('.direct-messages-list'); + elem.find('.messages-qtd').hide(); + elem.find('.post.new').removeClass('new'); }) ; } @@ -210,7 +107,21 @@ function openDmWithUserModal(peerAlias) { else getFullname(peerAlias, modal.self.find('.modal-header h3 span')); - requestDmConversationModal(modal.self.find('.direct-messages-thread').empty(), peerAlias); + queryStart(modal.content.find('.direct-messages-thread'), + peerAlias, 'direct', undefined, 2000, { + boardAutoAppend: true, + lastId: 0, + lengthNew: 0, + ready: function (req, peerAlias) { + twister.DMs[peerAlias] = twister.res[req]; + }, + readyReq: peerAlias, + drawFinish: function (req) { + $.MAL.dmConversationLoaded(twister.res[req].board); + } + } + ); + modal.content.on('scroll', {req: peerAlias}, handleDMsModalScroll); $('.dm-form-template').children().clone(true) .addClass('open').appendTo(modal.content).fadeIn('fast') @@ -232,19 +143,16 @@ function openGroupMessagesModal(groupAlias) { modal.content.prepend($('#group-messages-profile-modal-control-template').children().clone(true)); - requestDMsnippetList(modal.content.find('.direct-messages-list'), true); + modalDMsSummaryDraw(modal.content.find('.direct-messages-list'), true); modal.self.find('.mark-all-as-read') .css('display', 'inline') .attr('title', polyglot.t('Mark all as read')) .on('click', function (event) { - for (var user in _newDMsPerUser) { - if (user[0] === '*') - _newDMsPerUser[user] = 0; - } - saveDMsToStorage(); - $.MAL.updateNewGroupDMsUI(getNewGroupDMsCount()); - $(event.target).closest('.groupMessages').find('.direct-messages-list .messages-qtd').hide(); + resetNewDMsCountGroup(); + var elem = $(event.target).closest('.groupMessages').find('.direct-messages-list'); + elem.find('.messages-qtd').hide(); + elem.find('.post.new').removeClass('new'); }) ; } else { @@ -261,7 +169,21 @@ function openGroupMessagesModal(groupAlias) { function(req, ret) { if (ret && ret.members.indexOf(defaultScreenName) !== -1) { req.modal.content.append($('.messages-thread-template').children().clone(true)); - requestDmConversationModal(req.modal.content.find('.direct-messages-thread'), req.groupAlias); + queryStart(req.modal.content.find('.direct-messages-thread'), + req.groupAlias, 'direct', undefined, 2000, { + boardAutoAppend: true, + lastId: 0, + lengthNew: 0, + ready: function (req, peerAlias) { + twister.DMs[peerAlias] = twister.res[req]; + }, + readyReq: req.groupAlias, + drawFinish: function (req) { + $.MAL.dmConversationLoaded(twister.res[req].board); + } + } + ); + modal.content.on('scroll', {req: req.groupAlias}, handleDMsModalScroll); var control = $('#group-messages-messages-modal-control-template').children().clone(true) .appendTo(req.modal.content); diff --git a/js/twister_newmsgs.js b/js/twister_newmsgs.js index 9fa6ec8..a648a7e 100644 --- a/js/twister_newmsgs.js +++ b/js/twister_newmsgs.js @@ -42,6 +42,7 @@ function loadMentionsFromStorage() { var j = mentions.twists[i].userpost.n + '/' + mentions.twists[i].userpost.time; if (typeof twister.mentions.twists.cached[j] === 'undefined') { twister.mentions.twists.cached[j] = mentions.twists[i]; + twister.mentions.lengthCached++; if (twister.mentions.twists.cached[j].isNew) twister.mentions.lengthNew++; @@ -61,6 +62,7 @@ function loadMentionsFromStorage() { var j = mentions[i].data.userpost.n + '/' + mentions[i].mentionTime; if (typeof twister.mentions.twists.cached[j] === 'undefined') { twister.mentions.twists.cached[j] = mentions[i].data; + twister.mentions.lengthCached++; if (twister.mentions.twists.cached[j].isNew) twister.mentions.lengthNew++; @@ -104,6 +106,7 @@ function queryPendingPushMentions(req, res) { var j = res[i].userpost.n + '/' + res[i].userpost.time; if (typeof twister.res[req].twists.cached[j] === 'undefined') { twister.res[req].twists.cached[j] = res[i]; + twister.res[req].lengthCached++; twister.res[req].twists.pending.push(j); // mention must be somewhat recent compared to last known one to be considered new @@ -159,10 +162,10 @@ function handleMentionsModalScroll(event) { if (elem.scrollTop() >= elem[0].scrollHeight - elem.height() - 50) { twister.mentions.scrollQueryActive = true; - twisterRpc('getmentions', [twister.mentions.query, 10, + twisterRpc('getmentions', [twister.mentions.query, postsPerRefresh, {max_id: twister.mentions.lastTorrentId - twister.mentions.lengthFromTorrent}], function (req, res) { - twister.mentions.scrollQueryActive = false; + twister.res[req].scrollQueryActive = false; twister.res[req].boardAutoAppend = true; // FIXME all pending twists will be appended queryProcess(req, res); twister.res[req].boardAutoAppend = false; @@ -174,119 +177,274 @@ function handleMentionsModalScroll(event) { // --- direct messages --- -var _lastDMIdPerUser = {}; -var _newDMsPerUser = {}; - function saveDMsToStorage() { - var ns = $.initNamespaceStorage(defaultScreenName); - ns.localStorage.set('lastDMIdPerUser', _lastDMIdPerUser); - ns.localStorage.set('newDMsPerUser', _newDMsPerUser); + var pool = {}; + + for (var peerAlias in twister.DMs) { + var twists = [], length = 0; + for (var j in twister.DMs[peerAlias].twists.cached) { + for (var i = 0; i < length; i++) + if (twister.DMs[peerAlias].twists.cached[j].id > twists[i].id) { + twists.splice(i, 0, twister.DMs[peerAlias].twists.cached[j]); + break; + } + + if (length === twists.length) + twists.push(twister.DMs[peerAlias].twists.cached[j]); + + length++; + } + pool[peerAlias] = { + twists: twists.slice(0, 100), // TODO add an option to specify number of DMs to cache + lastId: twister.DMs[peerAlias].lastId, + }; + } + + $.initNamespaceStorage(defaultScreenName).localStorage.set('DMs', pool); } function loadDMsFromStorage() { - var ns = $.initNamespaceStorage(defaultScreenName); - if (ns.localStorage.isSet('lastDMIdPerUser')) - _lastDMIdPerUser = ns.localStorage.get('lastDMIdPerUser'); - if (ns.localStorage.isSet('newDMsPerUser')) - _newDMsPerUser = ns.localStorage.get('newDMsPerUser'); + var storage = $.initNamespaceStorage(defaultScreenName).localStorage; + + if (storage.isSet('DMs')) { + var pool = storage.get('DMs'); + if (typeof pool === 'object') { + for (var peerAlias in pool) { + if (!twister.DMs[peerAlias]) + twister.DMs[peerAlias] = queryCreateRes(peerAlias, 'direct', + {boardAutoAppend: true, lastId: 0, lengthNew: 0}); + + for (var i = 0; i < pool[peerAlias].twists.length; i++) { + var j = pool[peerAlias].twists[i].from + '/' + pool[peerAlias].twists[i].time; + if (typeof twister.DMs[peerAlias].twists.cached[j] === 'undefined') { + twister.DMs[peerAlias].twists.cached[j] = pool[peerAlias].twists[i]; + twister.DMs[peerAlias].lengthCached++; + if (twister.DMs[peerAlias].twists.cached[j].isNew) + twister.DMs[peerAlias].lengthNew++; + } + } + twister.DMs[peerAlias].lastId = pool[peerAlias].lastId; + } + } + } + + // WARN all following storage keys are deprecated (see commit FIXME) + if (storage.isSet('lastDMIdPerUser')) { + var pool = storage.get('lastDMIdPerUser'); + if (typeof pool === 'object') + for (var peerAlias in pool) { + if (!twister.DMs[peerAlias]) + twister.DMs[peerAlias] = queryCreateRes(peerAlias, 'direct', + {boardAutoAppend: true, lastId: 0, lengthNew: 0}); + + twister.DMs[peerAlias].lastId = pool[peerAlias]; + } + + storage.remove('lastDMIdPerUser'); + } + if (storage.isSet('newDMsPerUser')) { + var pool = storage.get('newDMsPerUser'); + if (typeof pool === 'object') + for (var peerAlias in pool) { + if (!twister.DMs[peerAlias]) + twister.DMs[peerAlias] = queryCreateRes(peerAlias, 'direct', + {boardAutoAppend: true, lastId: 0, lengthNew: 0}); + + twister.DMs[peerAlias].lengthNew = pool[peerAlias]; + } + + storage.remove('newDMsPerUser'); + } +} + +function queryPendingPushDMs(res) { + var lengthNew = 0; + var lengthPending = 0; + + for (var peerAlias in res) { + if (!res[peerAlias] || !res[peerAlias].length || !twister.DMs[peerAlias]) + continue; + + for (var i = 0; i < res[peerAlias].length; i++) { + var j = res[peerAlias][i].from + '/' + res[peerAlias][i].time; + if (typeof twister.DMs[peerAlias].twists.cached[j] === 'undefined') { + twister.DMs[peerAlias].twists.cached[j] = res[peerAlias][i]; + twister.DMs[peerAlias].lengthCached++; + twister.DMs[peerAlias].twists.pending.push(j); + lengthPending++; + if (twister.DMs[peerAlias].lastId < res[peerAlias][i].id) { + twister.DMs[peerAlias].lastId = res[peerAlias][i].id; + if ((!twister.DMs[peerAlias].board || !twister.DMs[peerAlias].board.is('html *')) + && !res[peerAlias][i].fromMe && res[peerAlias][i].from !== defaultScreenName) { + lengthNew++; + twister.DMs[peerAlias].lengthNew += 1; + twister.DMs[peerAlias].twists.cached[j].isNew = true; + } + } + } + } + } + + if (lengthPending) + saveDMsToStorage(); + + return lengthNew; } function requestDMsCount() { - var followList = []; + var list = []; for (var i = 0; i < followingUsers.length; i++) - followList.push({username: followingUsers[i]}); - for (var i = 0; i < groupChatAliases.length; i++ ) - followList.push({username: groupChatAliases[i]}); + list.push({username: followingUsers[i]}); + for (var i = 0; i < groupChatAliases.length; i++) + list.push({username: groupChatAliases[i]}); - twisterRpc('getdirectmsgs', [defaultScreenName, 1, followList], - function(req, dmUsers) { - var newDMsUpdated; + twisterRpc('getdirectmsgs', [defaultScreenName, 1, list], + function (req, res) { + var lengthNew = 0, lengthNewMax = 0; + var list = []; - for (var u in dmUsers) { - if (!dmUsers[u]) + for (var peerAlias in res) { + if (!res[peerAlias] || !res[peerAlias].length) continue; - var dmData = dmUsers[u][0]; - if (dmData.from !== defaultScreenName) { - if (u in _lastDMIdPerUser && u in _newDMsPerUser) { - if (dmData.id !== _lastDMIdPerUser[u]) { - _newDMsPerUser[u] += dmData.id - _lastDMIdPerUser[u]; - newDMsUpdated = true; - } - } else { - _newDMsPerUser[u] = dmData.id + 1; - newDMsUpdated = true; - } - } - _lastDMIdPerUser[u] = dmData.id; + if (!twister.DMs[peerAlias]) + twister.DMs[peerAlias] = queryCreateRes(peerAlias, 'direct', + {boardAutoAppend: true, lastId: 0, lengthNew: 0}); + + if (res[peerAlias][0].id > twister.DMs[peerAlias].lastId) { + lengthNew = res[peerAlias][0].id - twister.DMs[peerAlias].lastId; + if (lengthNewMax < lengthNew) + lengthNewMax = lengthNew; + + list.push({username: peerAlias}); + } else if (!twister.DMs[peerAlias].lengthCached) + queryPendingPushDMs(res); } - if (newDMsUpdated) { - saveDMsToStorage(); - var newDMs = getNewDMsCount(); - if (newDMs) { - $.MAL.updateNewDMsUI(newDMs); - $.MAL.soundNotifyDM(); - - if (!$.mobile && $.Options.showDesktopNotifDMs.val === 'enable') { - $.MAL.showDesktopNotification({ - body: polyglot.t('You got') + ' ' + polyglot.t('new_direct_messages', newDMs) + '.', - tag: 'twister_notification_new_DMs', - timeout: $.Options.showDesktopNotifDMsTimer.val, - funcClick: function () {$.MAL.showDMchat();} - }); - } - } - var newDMs = getNewGroupDMsCount(); - if (newDMs) { - $.MAL.updateNewGroupDMsUI(newDMs); - $.MAL.soundNotifyDM(); - - if (!$.mobile && $.Options.showDesktopNotifDMs.val === 'enable') { - $.MAL.showDesktopNotification({ - body: polyglot.t('You got') + ' ' + polyglot.t('new_group_messages', newDMs) + '.', - tag: 'twister_notification_new_DMs', - timeout: $.Options.showDesktopNotifDMsTimer.val, - funcClick: function () {$.MAL.showDMchat({group: true});} - }); + + if (list.length === 1) + queryProcess(list[0].username + '@direct', res); + else if (lengthNewMax === 1) { + if (queryPendingPushDMs(res)) + DMsSummaryProcessNew(); + } else if (lengthNewMax) { + twisterRpc('getdirectmsgs', [defaultScreenName, lengthNewMax, list], + function (req, res) { + if (typeof res !== 'object' || $.isEmptyObject(res)) + return; + + if (queryPendingPushDMs(res)) + DMsSummaryProcessNew(); + }, undefined, + function (req, res) { + console.warn(polyglot.t('ajax_error', + {error: (res && res.message) ? res.message : res})); } - } + ); } - }, null, - function(req, ret) {console.warn('ajax error:' + ret);}, null + }, undefined, + function (req, res) { + console.warn(polyglot.t('ajax_error', {error: (res && res.message) ? res.message : res})); + } ); } +function DMsSummaryProcessNew() { + var lengthNew = getNewDMsCount(); + if (lengthNew) { + $.MAL.updateNewDMsUI(lengthNew); + $.MAL.soundNotifyDM(); + if (!$.mobile) { + if ($.Options.showDesktopNotifDMs.val === 'enable') { + $.MAL.showDesktopNotification({ + body: polyglot.t('You got') + ' ' + polyglot.t('new_direct_messages', lengthNew) + '.', + tag: 'twister_notification_new_DMs', + timeout: $.Options.showDesktopNotifDMsTimer.val, + funcClick: function () {$.MAL.showDMchat();} + }); + } + var elem = getElem('.directMessages .direct-messages-list'); + if (isModalWithElemExists(elem)) + modalDMsSummaryDraw(elem); + } else if ($.mobile.activePage.attr('id') !== 'directmsg') + modalDMsSummaryDraw($('#directmsg .direct-messages-list')); + } + lengthNew = getNewGroupDMsCount(); + if (lengthNew) { + $.MAL.updateNewGroupDMsUI(lengthNew); + $.MAL.soundNotifyDM(); + if (!$.mobile) { + if ($.Options.showDesktopNotifDMs.val === 'enable') { + $.MAL.showDesktopNotification({ + body: polyglot.t('You got') + ' ' + polyglot.t('new_group_messages', lengthNew) + '.', + tag: 'twister_notification_new_DMs', + timeout: $.Options.showDesktopNotifDMsTimer.val, + funcClick: function () {$.MAL.showDMchat({group: true});} + }); + } + var elem = getElem('.groupMessages .direct-messages-list'); + if (isModalWithElemExists(elem)) + modalDMsSummaryDraw(elem, true); + } else if ($.mobile.activePage.attr('id') !== 'directmsg') + modalDMsSummaryDraw($('#directmsg .direct-messages-list'), true); + } +} + function getNewDMsCount() { - var newDMs = 0; + var lengthNew = 0; - for (var user in _newDMsPerUser) { - if (user[0] !== '*' && _newDMsPerUser[user]) - newDMs += _newDMsPerUser[user]; - } + for (var peerAlias in twister.DMs) + if (peerAlias[0] !== '*' && twister.DMs[peerAlias].lengthNew) + lengthNew += twister.DMs[peerAlias].lengthNew; - return newDMs; + return lengthNew; } function getNewGroupDMsCount() { - var newDMs = 0; + var lengthNew = 0; - for (var user in _newDMsPerUser) { - if (user[0] === '*' && _newDMsPerUser[user]) - newDMs += _newDMsPerUser[user]; - } + for (var peerAlias in twister.DMs) + if (peerAlias[0] === '*' && twister.DMs[peerAlias].lengthNew) + lengthNew += twister.DMs[peerAlias].lengthNew; - return newDMs; + return lengthNew; } -function resetNewDMsCountForUser(user, lastId) { - _newDMsPerUser[user] = 0; - _lastDMIdPerUser[user] = lastId; +function resetNewDMsCount() { + for (var peerAlias in twister.DMs) + if (peerAlias[0] !== '*') { + twister.DMs[peerAlias].lengthNew = 0; + for (var j in twister.DMs[peerAlias].twists.cached) + delete twister.DMs[peerAlias].twists.cached[j].isNew; + } saveDMsToStorage(); $.MAL.updateNewDMsUI(getNewDMsCount()); +} + +function resetNewDMsCountGroup() { + for (var peerAlias in twister.DMs) + if (peerAlias[0] === '*') { + twister.DMs[peerAlias].lengthNew = 0; + for (var j in twister.DMs[peerAlias].twists.cached) + delete twister.DMs[peerAlias].twists.cached[j].isNew; + } + + saveDMsToStorage(); $.MAL.updateNewGroupDMsUI(getNewGroupDMsCount()); } +function resetNewDMsCountForPeer(peerAlias) { + twister.DMs[peerAlias].lengthNew = 0; + for (var j in twister.DMs[peerAlias].twists.cached) + delete twister.DMs[peerAlias].twists.cached[j].isNew; + + saveDMsToStorage(); + if (peerAlias[0] !== '*') + $.MAL.updateNewDMsUI(getNewDMsCount()); + else + $.MAL.updateNewGroupDMsUI(getNewGroupDMsCount()); +} + function updateGroupList() { twisterRpc('listgroups', [], function(req, ret) {groupChatAliases = ret;}, null, @@ -295,6 +453,7 @@ function updateGroupList() { } function initDMsCount() { + twister.DMs = {}; loadDMsFromStorage(); $.MAL.updateNewDMsUI(getNewDMsCount()); $.MAL.updateNewGroupDMsUI(getNewGroupDMsCount()); @@ -310,3 +469,38 @@ function initDMsCount() { function newmsgsChangedUser() { clearInterval(twister.mentions.interval); } + +function handleDMsModalScroll(event) { + if (!event || !event.data.req || !twister.DMs[event.data.req] + || twister.DMs[event.data.req].scrollQueryActive) + return; + + var length = twister.DMs[event.data.req].lastId - twister.DMs[event.data.req].lengthCached + 1; + if (!length) + return; + + var elem = $(event.target); + if (elem.scrollTop() < 100) { + twister.DMs[event.data.req].scrollQueryActive = true; + + twisterRpc('getdirectmsgs', [defaultScreenName, Math.min(length, postsPerRefresh), + [{username: twister.DMs[event.data.req].query, max_id: length - 1}]], + function (req, res) { + twister.res[req.k].scrollQueryActive = false; + //twister.res[req.k].boardAutoAppend = true; // FIXME all pending twists will be appended + queryProcess(req.k, res); + //twister.res[req.k].boardAutoAppend = false; + if (req.container[0].scrollHeight !== req.containerScrollHeightPrev) + req.container.scrollTop(req.container[0].scrollHeight - req.containerScrollHeightPrev); + }, { + k: twister.DMs[event.data.req].query + '@' + twister.DMs[event.data.req].resource, + container: elem, + containerScrollHeightPrev: elem[0].scrollHeight + }, + function (req, res) { + console.warn(polyglot.t('ajax_error', + {error: (res && res.message) ? res.message : res})); + } + ); + } +} diff --git a/js/twister_timeline.js b/js/twister_timeline.js index 09c4010..80c8542 100644 --- a/js/twister_timeline.js +++ b/js/twister_timeline.js @@ -183,13 +183,18 @@ function processReceivedPosts(req, posts) } function updateTimeline(req, posts) { - attachPostsToStream($.MAL.getStreamPostsParent(), posts, req.getspam); + attachPostsToStream($.MAL.getStreamPostsParent(), posts, true, + function (twist, promoted) { + return {item: postToElem(twist, 'original', promoted), time: twist.userpost.time}; + }, + req.getspam + ); for (var i = 0; i < posts.length; i++) { req.reportProcessedPost(posts[i]['userpost']['n'], posts[i]['userpost']['k'], true); } } -function attachPostsToStream(stream, posts, isPromoted) { +function attachPostsToStream(stream, posts, descendingOrder, createElem, createElemReq) { //console.log('attachPostsToStream:'); //console.log(posts); @@ -204,7 +209,7 @@ function attachPostsToStream(stream, posts, isPromoted) { for (var i = 0; i < posts.length; i++) { //console.log(posts[i]); var isAttached = false; - var intrantPost = {item: postToElem(posts[i], 'original', isPromoted), time: posts[i].userpost.time}; + var intrantPost = createElem(posts[i], createElemReq); intrantPost.item.attr('data-time', intrantPost.time); if (streamPosts.length) { @@ -213,10 +218,10 @@ function attachPostsToStream(stream, posts, isPromoted) { if (intrantPost.time === streamPosts[j].time && intrantPost.item[0].innerHTML === streamPosts[j].item[0].innerHTML) { isAttached = true; - console.log('appending of duplicate twist prevented'); + console.warn('appending of duplicate twist prevented'); break; - } else if (intrantPost.time > streamPosts[j].time) { - // this post in stream is older, so post must be inserted above + } else if (descendingOrder ? + intrantPost.time > streamPosts[j].time : intrantPost.time < streamPosts[j].time) { intrantPost.item.insertBefore(streamPosts[j].item).show(); streamPosts.splice(j, 0, intrantPost); isAttached = true;