You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
540 lines
20 KiB
540 lines
20 KiB
// twister_newmsgs.js |
|
// 2013 Miguel Freitas |
|
// |
|
// Periodically check for new mentions and private messages (DMs) |
|
// Update UI counters in top bar. Load/save state to localStorage. |
|
|
|
// --- mentions --- |
|
|
|
var groupChatAliases = [] |
|
|
|
function saveMentionsToStorage() { |
|
var twists = [], length = 0; |
|
for (var j in twister.mentions.twists.cached) { |
|
for (var i = 0; i < length; i++) |
|
if (twister.mentions.twists.cached[j].userpost.time > twists[i].userpost.time) { |
|
twists.splice(i, 0, twister.mentions.twists.cached[j]); |
|
break; |
|
} |
|
|
|
if (length === twists.length) |
|
twists.push(twister.mentions.twists.cached[j]); |
|
|
|
length++; |
|
} |
|
|
|
$.initNamespaceStorage(defaultScreenName).localStorage |
|
.set('mentions', { |
|
twists: twists.slice(0, 100), // TODO add an option to specify number of mentions to cache |
|
lastTime: twister.mentions.lastTime, |
|
lastTorrentId: twister.mentions.lastTorrentId |
|
}) |
|
; |
|
} |
|
|
|
function loadMentionsFromStorage() { |
|
var storage = $.initNamespaceStorage(defaultScreenName).localStorage; |
|
|
|
if (storage.isSet('mentions')) { |
|
var mentions = storage.get('mentions'); |
|
if (typeof mentions === 'object') { |
|
for (var i = 0; i < mentions.twists.length; i++) { |
|
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++; |
|
|
|
twister.mentions.lengthFromTorrent++; |
|
} |
|
} |
|
twister.mentions.lastTime = mentions.lastTime; |
|
twister.mentions.lastTorrentId = mentions.lastTorrentId; |
|
} |
|
} |
|
|
|
// WARN all following storage keys are deprecated (see commit dc8cfc20ef10ff3008b4abfdb30d31e7fcbec0cd) |
|
if (storage.isSet('knownMentions')) { |
|
var mentions = storage.get('knownMentions'); |
|
if (typeof mentions === 'object') |
|
for (var i in mentions) { |
|
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++; |
|
|
|
twister.mentions.lengthFromTorrent++; |
|
} |
|
} |
|
|
|
storage.remove('knownMentions'); |
|
} |
|
if (storage.isSet('lastMentionTime')) { |
|
twister.mentions.lastTime = storage.get('lastMentionTime'); |
|
storage.remove('lastMentionTime'); |
|
} |
|
if (storage.isSet('lastLocalMentionId')) { |
|
twister.mentions.lastTorrentId = storage.get('lastLocalMentionId'); |
|
storage.remove('lastLocalMentionId'); |
|
} |
|
if (storage.isSet('newMentions')) |
|
storage.remove('newMentions'); |
|
} |
|
|
|
function queryPendingPushMentions(req, res) { |
|
var lengthNew = 0; |
|
var lengthPending = twister.res[req].twists.pending.length; |
|
var timeCurrent = new Date().getTime() / 1000 + 7200; // 60 * 60 * 2 |
|
var timeLastMention = twister.res[req].lastTime; |
|
|
|
for (var i = 0; i < res.length; i++) { |
|
if (res[i].userpost.time > timeCurrent) { |
|
console.warn('ignoring mention from the future:'); |
|
console.log(res[i]); |
|
continue; |
|
} |
|
|
|
if (res[i].id) { |
|
twister.res[req].lastTorrentId = Math.max(twister.res[req].lastTorrentId, res[i].id); |
|
delete res[i].id; |
|
twister.res[req].lengthFromTorrent++; |
|
} |
|
|
|
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 |
|
if (res[i].userpost.time + 259200 > timeLastMention) { // 3600 * 24 * 3 |
|
lengthNew++; |
|
twister.res[req].lastTime = Math.max(res[i].userpost.time, twister.res[req].lastTime); |
|
twister.res[req].twists.cached[j].isNew = true; |
|
} |
|
} |
|
} |
|
|
|
if (lengthNew) |
|
twister.res[req].lengthNew += lengthNew; |
|
|
|
if (twister.res[req].twists.pending.length > lengthPending) |
|
saveMentionsToStorage(); |
|
|
|
return lengthNew; |
|
} |
|
|
|
function resetMentionsCount() { |
|
if (!twister.mentions.lengthNew) |
|
return; |
|
|
|
twister.mentions.lengthNew = 0; |
|
|
|
for (var j in twister.mentions.twists.cached) |
|
if (twister.mentions.twists.cached[j].isNew) |
|
delete twister.mentions.twists.cached[j].isNew; |
|
|
|
saveMentionsToStorage(); |
|
$.MAL.updateNewMentionsUI(twister.mentions.lengthNew); |
|
} |
|
|
|
function initMentionsCount() { |
|
var req = queryStart('', defaultScreenName, 'mention', [10000, 2000, 3], 10000, { |
|
lastTime: 0, |
|
lastTorrentId: -1, |
|
lengthNew: 0, |
|
ready: function (req) { |
|
twister.mentions = twister.res[req]; |
|
twister.mentions.lengthFromTorrent = 0; |
|
loadMentionsFromStorage(); |
|
}, |
|
skidoo: function () {return false;} |
|
}); |
|
|
|
$.MAL.updateNewMentionsUI(twister.mentions.lengthNew); |
|
} |
|
|
|
function handleMentionsModalScroll(event) { |
|
if (!event || twister.mentions.scrollQueryActive) |
|
return; |
|
|
|
var elem = $(event.target); |
|
if (elem.scrollTop() >= elem[0].scrollHeight - elem.height() - 50) { |
|
twister.mentions.scrollQueryActive = true; |
|
|
|
twisterRpc('getmentions', [twister.mentions.query, postsPerRefresh, |
|
{max_id: twister.mentions.lastTorrentId - twister.mentions.lengthFromTorrent}], |
|
function (req, res) { |
|
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; |
|
}, twister.mentions.query + '@' + twister.mentions.resource, |
|
function () {console.warn('getmentions API requires twister-core > 0.9.27');} |
|
); |
|
} |
|
} |
|
|
|
// --- direct messages --- |
|
|
|
function saveDMsToStorage() { |
|
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, |
|
}; |
|
} |
|
|
|
if ($.Options.get('dmEncryptCache') === 'enable') { |
|
pool = twister.var.key.pub.encrypt(JSON.stringify(pool)); |
|
delete pool.orig; // WORKAROUND the decrypt function does .slice(0, orig) but something goes wrong in process of buffer decoding (if original string contains non-ASCII characters) and orig may be smaller than the actual size, if it is undefined .slice gets it whole |
|
} |
|
$.initNamespaceStorage(defaultScreenName).localStorage.set('DMs', pool); |
|
} |
|
|
|
function loadDMsFromStorage() { |
|
var storage = $.initNamespaceStorage(defaultScreenName).localStorage; |
|
|
|
if (storage.isSet('DMs')) { |
|
var pool = storage.get('DMs'); |
|
if (pool.key && pool.body && pool.mac) { |
|
if (pool = twister.var.key.decrypt(pool)) |
|
pool = JSON.parse(pool.toString()); |
|
else |
|
console.warn('can\'t decrypt DMs\' data cache'); |
|
} |
|
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 list = []; |
|
for (var i = 0; i < followingUsers.length; i++) |
|
list.push({username: followingUsers[i]}); |
|
for (var i = 0; i < groupChatAliases.length; i++) |
|
list.push({username: groupChatAliases[i]}); |
|
|
|
twisterRpc('getdirectmsgs', [defaultScreenName, 1, list], |
|
function (req, res) { |
|
var lengthNew = 0, lengthNewMax = 0; |
|
var list = []; |
|
|
|
for (var peerAlias in res) { |
|
if (!res[peerAlias] || !res[peerAlias].length) |
|
continue; |
|
|
|
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 (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})); |
|
} |
|
); |
|
} |
|
}, 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 lengthNew = 0; |
|
|
|
for (var peerAlias in twister.DMs) |
|
if (peerAlias[0] !== '*' && twister.DMs[peerAlias].lengthNew) |
|
lengthNew += twister.DMs[peerAlias].lengthNew; |
|
|
|
return lengthNew; |
|
} |
|
|
|
function getNewGroupDMsCount() { |
|
var lengthNew = 0; |
|
|
|
for (var peerAlias in twister.DMs) |
|
if (peerAlias[0] === '*' && twister.DMs[peerAlias].lengthNew) |
|
lengthNew += twister.DMs[peerAlias].lengthNew; |
|
|
|
return lengthNew; |
|
} |
|
|
|
function resetNewDMsCount() { |
|
var isNewDetected; |
|
|
|
for (var peerAlias in twister.DMs) |
|
if (twister.DMs[peerAlias].lengthNew && peerAlias[0] !== '*') { |
|
twister.DMs[peerAlias].lengthNew = 0; |
|
for (var j in twister.DMs[peerAlias].twists.cached) |
|
delete twister.DMs[peerAlias].twists.cached[j].isNew; |
|
|
|
isNewDetected = true; |
|
} |
|
|
|
if (!isNewDetected) |
|
return; |
|
|
|
saveDMsToStorage(); |
|
$.MAL.updateNewDMsUI(getNewDMsCount()); |
|
} |
|
|
|
function resetNewDMsCountGroup() { |
|
var isNewDetected; |
|
|
|
for (var peerAlias in twister.DMs) |
|
if (twister.DMs[peerAlias].lengthNew && peerAlias[0] === '*') { |
|
twister.DMs[peerAlias].lengthNew = 0; |
|
for (var j in twister.DMs[peerAlias].twists.cached) |
|
delete twister.DMs[peerAlias].twists.cached[j].isNew; |
|
|
|
isNewDetected = true; |
|
} |
|
|
|
if (!isNewDetected) |
|
return; |
|
|
|
saveDMsToStorage(); |
|
$.MAL.updateNewGroupDMsUI(getNewGroupDMsCount()); |
|
} |
|
|
|
function resetNewDMsCountForPeer(peerAlias) { |
|
if (!twister.DMs[peerAlias].lengthNew) |
|
return; |
|
|
|
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, |
|
function(req, ret) {console.warn('twisterd >= 0.9.30 required for listgroups');}, null |
|
); |
|
} |
|
|
|
function initDMsCount() { |
|
twister.DMs = {}; |
|
dumpPrivkey(defaultScreenName, function (req, res) { |
|
twister.var.key = TwisterCrypto.PrivKey.fromWIF(res); |
|
|
|
loadDMsFromStorage(); |
|
$.MAL.updateNewDMsUI(getNewDMsCount()); |
|
$.MAL.updateNewGroupDMsUI(getNewGroupDMsCount()); |
|
//quick hack to obtain list of group chat aliases |
|
updateGroupList(); |
|
setInterval(updateGroupList, 60000); |
|
|
|
setTimeout(requestDMsCount, 200); |
|
//polling not needed: processNewPostsConfirmation will call requestDMsCount. |
|
//setInterval('requestDMsCount()', 5000); |
|
}); |
|
} |
|
|
|
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})); |
|
} |
|
); |
|
} |
|
}
|
|
|