VulpesOS/overrides/shared/js/fb/fb_data_reader.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

423 lines
11 KiB
JavaScript

'use strict';
/* global LazyLoader, TelIndexer */
// Reader Module for FB Data in a Datastore
// To use this library you need to include 'shared/js/fb/fb_request.js'
// WARNING: This file lazy loads 'shared/js/fb/fb_tel_index.js'
var fb = self.fb || {};
self.fb = fb;
(function() {
var contacts = fb.contacts || {};
fb.contacts = contacts;
var datastore;
// Datastore name declared on the manifest.webapp
var DATASTORE_NAME = 'Gaia_Facebook_Friends';
// Record Id for the index
var INDEX_ID = 1;
// Indicates the initialization state
var readyState = 'notInitialized';
// Custom event for notifying initializations
var INITIALIZE_EVENT = 'fb_ds_init';
// Last known revision Id. Used to determine whether the index has to
// be reloaded or not
var revisionId;
// Creates the internal Object in the datastore that will act as an index
function createIndex() {
return {
// By tel number and all its possible variants
// (We are not supporting dups right now)
byTel: Object.create(null),
// Prefix tree for enabling searching by partial tel numbers
treeTel: []
};
}
// The index we need to keep the correspondance between FB Friends and
// datastore Ids
var index;
// Ensures a proper error object is returned
function safeError(err) {
if (err && err.name) {
return err;
}
return {
name: 'UnknownError'
};
}
function notifyOpenSuccess(cb) {
readyState = 'initialized';
if (typeof cb === 'function') {
window.setTimeout(cb, 0);
}
var ev = new CustomEvent(INITIALIZE_EVENT);
document.dispatchEvent(ev);
}
function initError(outRequest, error) {
outRequest.failed(error);
}
function handleInitError(err, errorCb) {
readyState = 'error';
contacts.error = err;
if (typeof errorCb === 'function') {
window.setTimeout(function() {
errorCb(err);
}, 0);
}
// We resume other callers state
document.dispatchEvent(new CustomEvent(INITIALIZE_EVENT));
}
// Creates a default handler for errors
function defaultError(request) {
return defaultErrorCb.bind(null, request);
}
// Creates a default handler for success
function defaultErrorCb(request, error) {
request.failed(error);
}
function setIndex(obj) {
index = obj && obj.byTel && Array.isArray(obj.treeTel) ? obj : createIndex();
}
function successGet(outReq, data) {
outReq.done(data);
}
function errorGet(outReq, uid, err) {
window.console.error('Error while getting object for UID: ', uid);
outReq.failed(err.name);
}
Object.defineProperty(contacts, 'datastore', {
get: function getDataStore() { return datastore; },
enumerable: false,
configurable: false
});
Object.defineProperty(contacts, 'dsIndex', {
get: function getIndex() { return index; },
set: setIndex,
enumerable: false,
configurable: false
});
/**
* Allows to obtain the FB friend information by UID
*
*
*/
contacts.get = function(uid) {
var outRequest = new fb.utils.Request();
window.setTimeout(function get() {
contacts.init(function() {
doGet(uid, outRequest);
}, function(err) {
initError(outRequest, err);
});
}, 0);
return outRequest;
};
function doGet(uid, outRequest) {
var successCb = successGet.bind(null, outRequest);
var errorCb = errorGet.bind(null, outRequest, uid);
datastore.get(uid).then(successCb, errorCb);
}
/**
* Allows to get FB information by tel number (short or long)
* The tel number must be complete
*
*/
contacts.getByPhone = function(tel) {
var outRequest = new fb.utils.Request();
window.setTimeout(function get_by_phone() {
contacts.init(function get_by_phone() {
doGetByPhone(tel, outRequest);
},
function(err) {
initError(outRequest, err);
});
}, 0);
return outRequest;
};
function doGetByPhone(tel, outRequest) {
var dsId;
var normalizedNumber = navigator.mozPhoneNumberService.normalize(tel);
if (datastore.revisionId !== revisionId) {
window.console.info('Datastore revision id has changed!');
// Refreshing the index just in case
datastore.get(INDEX_ID).then(function success(obj) {
setIndex(obj);
revisionId = datastore.revisionId;
dsId = index.byTel[normalizedNumber];
if (typeof dsId !== 'undefined') {
datastore.get(dsId).then(function success(friend) {
outRequest.done(friend);
}, defaultError(outRequest));
}
else {
outRequest.done(null);
}
}, function(err) {
err = safeError(err);
window.console.error('The index cannot be refreshed: ', err.name);
outRequest.failed(err);
});
}
else {
dsId = index.byTel[normalizedNumber];
if (typeof dsId !== 'undefined') {
datastore.get(dsId).then(function success(friend) {
outRequest.done(friend);
}, defaultError(outRequest));
}
else {
outRequest.done(null);
}
}
}
/**
* Allows to search for friends by different criteria.
* Numbers can be partial and partial matchings will be found
*
* by === tel : By Tel number
*
*/
contacts.search = function(by, number) {
var outRequest = new fb.utils.Request();
window.setTimeout(function do_search() {
contacts.init(function do_search_init() {
if (by === 'phone') {
doSearchByPhone(number, outRequest);
}
},
function(err) {
initError(outRequest, err);
});
}, 0);
return outRequest;
};
function doSearchByPhone(number, outRequest) {
var normalizedNumber = navigator.mozPhoneNumberService.normalize(number);
LazyLoader.load(['/shared/js/fb/fb_tel_index.js',
'/shared/js/binary_search.js'], function() {
var toSearchNumber = normalizedNumber;
// TODO: Temporal way of searching for international numbers
// A follow-up is needed by using PhoneNumber.js exposed to Gaia
if (number.charAt(0) === '+') {
toSearchNumber = number.substring(1);
}
if (datastore.revisionId !== revisionId) {
window.console.info('Datastore revision id has changed!');
// Refreshing the index
datastore.get(INDEX_ID).then(function success(obj) {
setIndex(obj);
revisionId = datastore.revisionId;
var results = TelIndexer.search(index.treeTel, toSearchNumber);
var out = null;
if (results.length > 0) {
out = datastore.get.apply(datastore, results);
}
return out;
},function(err) {
err = safeError(err);
window.console.error('The index cannot be refreshed: ', err.name);
outRequest.failed(err);
}).then(function success(objList) {
if (objList && !Array.isArray(objList)) {
objList = [objList];
}
else {
objList = [];
}
outRequest.done(objList);
}, function error(err) {
err = safeError(err);
window.console.error('Error while retrieving result data: ',
err.name);
outRequest.failed(err);
});
}
else {
var results = TelIndexer.search(index.treeTel, toSearchNumber);
if (results.length > 0) {
datastore.get.apply(datastore, results).then(
function success(objList) {
if (!Array.isArray(objList)) {
objList = [objList];
}
outRequest.done(objList);
}, function error(err) {
err = safeError(err);
window.console.error('Error while retrieving result data: ',
err.name);
outRequest.failed(err);
});
}
else {
outRequest.done(results);
}
}
});
}
/**
* Refreshes the index data
*
*/
contacts.refresh = function() {
var outRequest = new fb.utils.Request();
window.setTimeout(function clear() {
contacts.init(function() {
doRefresh(outRequest);
},
function(err) {
initError(outRequest, err);
});
}, 0);
return outRequest;
};
function doRefresh(outRequest) {
datastore.get(INDEX_ID).then(function success(obj) {
setIndex(obj);
outRequest.done();
}, defaultError(outRequest));
}
/**
* Allows to obtain how many records contain he FB Database
*
*/
contacts.getLength = function() {
var retRequest = new fb.utils.Request();
window.setTimeout(function() {
contacts.init(function get_all() {
doGetLength(retRequest);
},
function(err) {
initError(retRequest, err);
});
}, 0);
return retRequest;
};
/**
* Allows to return the total number of records in the DataStore (minus 1)
* That's because the index object also counts
*
*/
function doGetLength(outRequest) {
datastore.getLength().then(function success(length) {
outRequest.done(length - 1);
},
function error(err) {
outRequest.failed(safeError(err));
});
}
// Needed only for testing purposes
contacts.restart = function() {
readyState = 'notInitialized';
};
/**
* Initialization function
*
*/
contacts.init = function(cb, errorCb) {
if (!navigator.getDataStores) {
handleInitError({ name: 'DatastoreNotFound' }, errorCb);
return;
}
if (readyState === 'initialized') {
cb();
return;
}
if (readyState === 'initializing') {
document.addEventListener(INITIALIZE_EVENT, function oninitalized() {
cb();
document.removeEventListener(INITIALIZE_EVENT, oninitalized);
});
return;
}
readyState = 'initializing';
navigator.getDataStores(DATASTORE_NAME).then(function success(ds) {
if (ds.length < 1) {
handleInitError({ name: 'DatastoreNotFound' }, errorCb);
return;
}
datastore = ds[0];
// Checking the length as the index should be there
datastore.getLength().then(function(length) {
if (length === 0 && datastore.readOnly === false) {
window.console.info('Adding index as length is 0');
setIndex(createIndex());
return datastore.add(index);
}
else if (length > 0) {
return datastore.get(INDEX_ID);
}
else if (datastore.readOnly === true) {
window.console.warn('The datastore is empty and readonly');
}
return null;
}).then(function add_index_success(v) {
// Index is created in order not to cause errors
if (typeof v !== 'number') {
setIndex(v);
}
revisionId = datastore.revisionId;
notifyOpenSuccess(cb);
}, function add_index_error(err) {
err = safeError(err);
window.console.error('Error while setting the index: ', err.name);
handleInitError(err, errorCb);
});
}, function error(err) {
err = safeError(err);
window.console.error('FB: Error while opening the DataStore: ',
err.name);
handleInitError(err, errorCb);
});
};
})();