Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
384 lines
10 KiB
JavaScript
Executable file
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);
|