VulpesOS/gaia/apps/communications/facebook/js/sync_worker.js
OverSu b38dea0597 Publish Vulpes Preview 15 sources for desktop, Android and Tundra
Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
2026-09-30 23:13:03 +02:00

384 lines
10 KiB
JavaScript
Executable file

/* globals fb */
'use strict';
importScripts('/shared/js/fb/fb_request.js',
'/shared/js/contacts/import/facebook/fb_query.js',
'/shared/js/contacts/import/facebook/fb_contact_utils.js',
'/shared/js/fb/fb_reader_utils.js',
'console.js');
(function(wutils) {
var uids,
timestamp,
access_token,
forceUpdateUids,
targetPictureSize;
wutils.addEventListener('message', processMessage);
debug('Worker up and running ....');
// Query to know what friends need to be updated
var UPDATED_QUERY = [
'SELECT uid, name, first_name, last_name, ' ,
'middle_name, birthday_date, email, current_location, ' ,
'work, phones, hometown_location, pic_big ' ,
' FROM user' ,
' WHERE uid ',
' IN (SELECT uid1 FROM friend WHERE uid2=me()',
' AND uid1 IN (',
null,
') )',
' AND profile_update_time > ',
null,
' OR uid IN (',
null,
')'
];
// Query to know what friends need to be removed
var REMOVED_QUERY = [
'SELECT target_id FROM ',
'connection where source_id = me() ',
' AND target_type = "user" AND is_deleted="true"',
' AND target_id IN (',
null,
')'
];
// Query to know the total amount of friends
var COUNT_QUERY = 'SELECT uid FROM user WHERE uid IN ' +
'(SELECT uid1 FROM friend WHERE uid2=me())';
// Indicates whether the worker started with or without data
var withData = false;
function debug() {
function getString(a) {
var out = '';
for (var c = 0; c < a.length; c++) {
out += a[c];
}
return out;
}
self.console.log(getString(arguments));
}
function errorQueryCb(e) {
self.console.error('<<FB Sync>>: Error while trying to sync', e);
postError({
type: 'query_error',
data: {}
});
}
function timeoutQueryCb(e) {
postError({
type: 'timeout_error'
});
}
function postError(e) {
// Message type to propagate to the worker parent
var type = 'query_error';
if (e && e.code === 190) {
self.console.log('This is a token error. Notifying worker parent');
type = 'token_error';
}
else if (e && e.type === 'timeout_error') {
self.console.log('This is a timeout error. Notifying worker parent');
type = e.type;
}
wutils.postMessage({
type: type,
data: e
});
}
function buildQueries(ts, uids, forcedUids) {
var uidsFilter = uids.join(',');
// The index at which the timestamp is set
var IDX_TS = 10;
UPDATED_QUERY[IDX_TS] = Math.round(ts / 1000);
// The index at which uids filter is set
var IDX_UIDS = 7;
UPDATED_QUERY[IDX_UIDS] = uidsFilter;
// The index at which those uids forced to be updated is set
var IDX_FORCE = 12;
var forceUpdateUidsFilter = '';
if (forcedUids && forcedUids.length > 0) {
forceUpdateUidsFilter = forcedUids.join(',');
}
UPDATED_QUERY[IDX_FORCE] = forceUpdateUidsFilter;
var R_IDX_UIDS = 4;
REMOVED_QUERY[R_IDX_UIDS] = uidsFilter;
// Two queries launched at the same time
var outQueries = {
query1: UPDATED_QUERY.join(''),
query2: REMOVED_QUERY.join('')
};
// In any sync without data (periodic one) we also request the total amount
// of friends in order to update it
// Please take into account that that cannot be done with a friend_count
// query as it also returns those friends who are deactivated
// See https://bugzilla.mozilla.org/show_bug.cgi?id=838605
if (!withData) {
outQueries.query3 = COUNT_QUERY;
}
return JSON.stringify(outQueries);
}
function processMessage(e) {
var message = e.data;
if (message.type === 'start') {
withData = false;
uids = message.data.uids;
access_token = message.data.access_token;
timestamp = message.data.timestamp;
forceUpdateUids = message.data.imgNeedsUpdate;
fb.operationsTimeout = message.data.operationsTimeout;
targetPictureSize = message.data.targetPictureSize;
debug('Worker acks contacts to check: ', Object.keys(uids).length);
if (forceUpdateUids && forceUpdateUids.length > 0) {
debug('These friends are forced to be updated: ' ,
JSON.stringify(forceUpdateUids));
}
getFriendsToBeUpdated(Object.keys(uids), Object.keys(forceUpdateUids));
}
else if (message.type === 'startWithData') {
withData = true;
debug('worker Acks start with data');
fb.operationsTimeout = message.data.operationsTimeout;
uids = message.data.uids;
access_token = message.data.access_token;
targetPictureSize = message.data.targetPictureSize;
getNewImgsForFriends(Object.keys(uids), access_token);
}
}
// Launch a multiple query to obtain friends to be updated and deleted
function getFriendsToBeUpdated(uids, forcedUids) {
var query = buildQueries(timestamp, uids, forcedUids);
var callbacks = {
success: friendsReady,
error: errorQueryCb,
timeout: timeoutQueryCb
};
fb.utils.runQuery(query, callbacks, access_token);
}
// Callback executed when data is ready
function friendsReady(response) {
// Timestamp is captured right now to avoid problems
// with updates in between
var qts = Date.now();
if (typeof response.error === 'undefined') {
var updateList = response.data[0].fql_result_set;
var removeList = response.data[1].fql_result_set;
// removeList = [{target_id: '100001127136581'}];
var theData = {
totalToChange: updateList.length + removeList.length,
queryTimestamp: qts
};
if (!withData) {
if (response.data[2] &&
Array.isArray(response.data[2].fql_result_set)) {
var friendCount = response.data[2].fql_result_set.length;
theData.newFriendNumber = friendCount;
}
}
wutils.postMessage({
type: 'totals',
data: theData
});
syncUpdatedFriends(updateList);
syncRemovedFriends(removeList);
}
else {
postError(response.error);
}
}
function syncRemovedFriends(removedFriends) {
debug('Friends to be removed: ', removedFriends.length);
// Simply an iteration over the collection is done and a message passed
removedFriends.forEach(function(aremoved) {
var removedRef = uids[aremoved.target_id];
if (removedRef) {
wutils.postMessage({
type: 'friendRemoved',
data: {
uid: aremoved.target_id,
contactId: removedRef.contactId
}
});
}
}); // forEach
}
function syncUpdatedFriends(updatedFriends) {
debug('Friends to be updated: ', updatedFriends.length);
// Friends which image has to be updated
var friendsImgToBeUpdated = {};
updatedFriends.forEach(function(afriend) {
var friendInfo = forceUpdateUids[afriend.uid] || uids[afriend.uid];
if (!friendInfo) {
debug('The uid provided is unknown. Doing nothing');
return;
}
if (afriend.pic_big !== friendInfo.photoUrl) {
// Photo changed
debug('Contact Photo Changed!!! for ', afriend.uid);
friendsImgToBeUpdated[afriend.uid] = afriend;
}
else {
debug('Contact Photo unchanged for ', afriend.uid);
wutils.postMessage({
type: 'friendUpdated',
data: {
updatedFbData: afriend,
contactId: friendInfo.contactId
}
});
}
});
debug('Worker terminated processing updates');
var friendImgList = Object.keys(friendsImgToBeUpdated);
if (friendImgList.length > 0) {
debug('Now starting synch for ', friendImgList.length, ' images');
// Now it is time to download the images needed
var imgSync = new ImgSynchronizer(friendImgList);
imgSync.start();
// Once an image is ready friend update is notified
imgSync.onimageready = function(uid, blob) {
var friendData;
if (blob) {
debug('Image retrieved correctly for ', uid);
friendData = friendsImgToBeUpdated[uid];
friendData.fbInfo = {};
friendData.fbInfo.photo = [blob];
fb.setFriendPictureUrl(friendData.fbInfo, friendData.pic_big);
}
else {
self.console.error('Img for UID', uid, ' could not be retrieved ');
// This friend has to be marked in a special state just to be
// synced later on
friendData = friendsImgToBeUpdated[uid];
friendData.fbInfo = {};
friendData.fbInfo.photo = [];
// TODO: This should only remove the URL of the profile picture
friendData.fbInfo.url = [];
}
var contact = uids[uid] || forceUpdateUids[uid];
wutils.postMessage({
type: 'friendUpdated',
data: {
updatedFbData: friendData,
contactId: contact.contactId
}
});
};
}
}
// For dealing with the case that only new imgs have to be retrieved
function getNewImgsForFriends(friendList) {
debug('Getting new imgs for friends', JSON.stringify(friendList));
var imgSync = new ImgSynchronizer(friendList);
imgSync.start();
// Once an image is ready friend update is notified
imgSync.onimageready = function(uid, blob) {
self.console.log('Img Ready from worker');
if (!blob) {
self.console.error('Img for UID: ', uid, ' could not be retrieved ');
}
wutils.postMessage({
type: 'friendImgReady',
data: {
photo: blob,
contactId: uids[uid].contactId
}
});
};
}
var ImgSynchronizer = function(friends) {
var next = 0;
var self = this;
this.friends = friends;
this.start = function() {
retrieveImg(this.friends[next]);
};
function imgRetrieved(blob) {
if (typeof self.onimageready === 'function') {
var uid = self.friends[next];
wutils.setTimeout(function() {
self.onimageready(uid, blob);
},0);
}
// And lets go for the next
next++;
if (next < self.friends.length) {
retrieveImg(self.friends[next]);
}
}
function retrieveImg(uid) {
fb.utils.getFriendPicture(uid, imgRetrieved, access_token,
targetPictureSize);
}
};
})(self);