From 2da69c40871ada3cd695b8eed3a98e5477f7b99b Mon Sep 17 00:00:00 2001 From: Simon Eisenmann Date: Thu, 12 Jun 2014 19:55:42 +0200 Subject: [PATCH] Store contacts in per user database. --- static/js/directives/contactrequest.js | 7 +- static/js/services/contactdata.js | 15 +++ static/js/services/contacts.js | 138 ++++++++++++++++++------- 3 files changed, 120 insertions(+), 40 deletions(-) diff --git a/static/js/directives/contactrequest.js b/static/js/directives/contactrequest.js index 3889d13b..ef5358a7 100644 --- a/static/js/directives/contactrequest.js +++ b/static/js/directives/contactrequest.js @@ -50,7 +50,12 @@ define(['jquery', 'underscore'], function($, _) { // Add support for contacts on controller creation. var request = $scope.request; if (request.Success && request.Userid && request.Token) { - $scope.addContact(request, buddyData.lookup($scope.id).status || null); + var buddy = buddyData.lookup($scope.id); + var status = {}; + if (buddy) { + $.extend(status, buddy.status); + } + $scope.addContact(request, status); } }]; diff --git a/static/js/services/contactdata.js b/static/js/services/contactdata.js index b06d2f0e..20cf81e9 100644 --- a/static/js/services/contactdata.js +++ b/static/js/services/contactdata.js @@ -28,6 +28,16 @@ define(['underscore', 'jquery'], function(underscore, $) { var count = 0; var contactData = { + clear: function(cb) { + _.each(users, _.bind(function(idx, userid) { + var contact = contacts[idx]; + if (cb && contact) { + cb(contact); + } + this.remove(userid); + }, this)); + count = 0; + }, addByRequest: function(request, status) { //console.log("addByRequest", request, status); var userid = request.Userid; @@ -69,6 +79,10 @@ define(['underscore', 'jquery'], function(underscore, $) { return null; }, remove: function(userid) { + if (users.hasOwnProperty(userid)) { + var id = users[userid]; + delete contacts[id]; + } delete users[userid]; }, getById: function(id) { @@ -81,6 +95,7 @@ define(['underscore', 'jquery'], function(underscore, $) { return null } }; + return contactData; }]; diff --git a/static/js/services/contacts.js b/static/js/services/contacts.js index e2f6921b..efbcf2c0 100644 --- a/static/js/services/contacts.js +++ b/static/js/services/contacts.js @@ -18,44 +18,51 @@ * along with this program. If not, see . * */ -define(['underscore', 'jquery', 'modernizr'], function(underscore, $, Modernizr) { +define(['underscore', 'jquery', 'modernizr', 'sjcl'], function(underscore, $, Modernizr, sjcl) { - var Database = function(name, version) { + var Database = function(name) { + this.version = 3; this.ready = false; this.db = null; this.name = name; this.e = $({}); - var request = indexedDB.open("mediastream", version); + var request = indexedDB.open(this.name, this.version); var that = this; request.onupgradeneeded = function(event) { var db = event.target.result; var transaction = event.target.transaction; transaction.onerror = _.bind(that.onerror, that); - if (db.objectStoreNames.contains(name)) { - // TODO(longsleep): Database upgrade should keep the data and migrate it. - db.deleteObjectStore("contacts") - console.warn("Removed contacts database with old format.") - } - db.createObjectStore(name, { - // We use id field as our unique identifier. - keyPath: "id" - }); + that.init(db); console.log("Created contacts database.") }; request.onsuccess = _.bind(that.onsuccess, that); }; + Database.prototype.init = function(db) { + var createOrUpdateStore = function(name, obj) { + if (db.objectStoreNames.contains(name)) { + // TODO(longsleep): Migrate data. + db.deleteObjectStore(name); + } + db.createObjectStore(name, obj); + } + // Create our object stores. + createOrUpdateStore("contacts", { + // We use id field as our unique identifier. + keyPath: "id" + }); + }; Database.prototype.onerror = function(event) { console.log("IndexDB database error", event); }; Database.prototype.onsuccess = function(event) { this.db = event.target.result; this.ready = true; - console.log("Openend database for contacts", this.db); + console.log("Openend database", this.db); this.e.triggerHandler("ready"); }; - Database.prototype.put = function(data, successCallback, errorCallback) { - var transaction = this.db.transaction(this.name, "readwrite"); - var store = transaction.objectStore(this.name); + Database.prototype.put = function(store, data, successCallback, errorCallback) { + var transaction = this.db.transaction(store, "readwrite"); + var store = transaction.objectStore(store); var request = store.put(data); if (!errorCallback) { errorCallback = _.bind(this.onerror, this); @@ -66,9 +73,9 @@ define(['underscore', 'jquery', 'modernizr'], function(underscore, $, Modernizr) } return request; }; - Database.prototype.delete = function(id, successCallback, errorCallback) { - var transaction = this.db.transaction(this.name, "readwrite"); - var store = transaction.objectStore(this.name); + Database.prototype.delete = function(store, id, successCallback, errorCallback) { + var transaction = this.db.transaction(store, "readwrite"); + var store = transaction.objectStore(store); var request = store.delete(id); if (!errorCallback) { errorCallback = _.bind(this.onerror, this); @@ -79,9 +86,9 @@ define(['underscore', 'jquery', 'modernizr'], function(underscore, $, Modernizr) } return request; }; - Database.prototype.all = function(iteratorCallback, errorCallback) { - var transaction = this.db.transaction(this.name); - var store = transaction.objectStore(this.name); + Database.prototype.all = function(store, iteratorCallback, errorCallback) { + var transaction = this.db.transaction(store); + var store = transaction.objectStore(store); var keyRange = IDBKeyRange.lowerBound(0); var cursorRequest = store.openCursor(keyRange); cursorRequest.onsuccess = function(event) { @@ -99,45 +106,98 @@ define(['underscore', 'jquery', 'modernizr'], function(underscore, $, Modernizr) transaction.onerror = cursorRequest.onerror = errorCallback; return cursorRequest; }; - - var database; - if (Modernizr.indexeddb) { - database = new Database("contacts", 1); - } + Database.prototype.close = function() { + // TODO(longsleep): Database close. + this.e.off(); + if (this.db) { + this.db.close(); + this.db = null; + this.ready = false; + } + _.defer(_.bind(function() { + this.e.triggerHandler("closed"); + }, this)); + }; // contacts return ["appData", "contactData", function(appData, contactData) { var Contacts = function() { + this.e = $({}); this.userid = null; + this.database = null; + appData.e.on("authenticationChanged", _.bind(function(event, userid) { - this.userid = userid; + var database = this.open(userid); if (database && userid) { // TODO(longsleep): This needs to be delayed util self has ha userid. if (database.ready) { - _.defer(_.bind(this.load, this)); + _.defer(_.bind(function() { + if (this.database === database) { + this.load(); + } + }, this)); } else { - database.e.one("ready", _.bind(this.load, this)); + database.e.one("ready", _.bind(function() { + if (this.database === database) { + this.load(); + } + }, this)); } } }, this)); + + }; + + Contacts.prototype.open = function(userid) { + + if (this.database && (!userid || this.userid !== userid)) { + // Unload existing contacts. + this.unload(); + // Close existing database. + this.database.close(); + this.database = null; + } + if (userid) { + if (!Modernizr.indexeddb) { + return; + } + // Create HMAC database name for user. + var hmac = new sjcl.misc.hmac('mediastream'); + var id = "mediastream-"+sjcl.codec.base64.fromBits(hmac.encrypt(userid)); + console.log("Open of database:", id); + var database = this.database = new Database(id); + return database; + } else { + this.database = null; + return null; + } + }; Contacts.prototype.load = function() { - console.log("Load contacts from storage", database); - database.all(_.bind(function(data) { - var contact = contactData.addByData(data.contact); - // TODO(longsleep): Convert buddyImage string to Blob. - this.e.triggerHandler("contactadded", contact); + if (this.database) { + console.log("Load contacts from storage", this); + this.database.all("contacts", _.bind(function(data) { + var contact = contactData.addByData(data.contact); + // TODO(longsleep): Convert buddyImage string to Blob. + this.e.triggerHandler("contactadded", contact); + }, this)); + } + }; + + Contacts.prototype.unload = function() { + contactData.clear(_.bind(function(contact) { + this.e.triggerHandler("contactremoved", contact); }, this)); }; Contacts.prototype.add = function(request, status) { var contact = contactData.addByRequest(request, status); this.e.triggerHandler("contactadded", contact); - if (database) { - database.put({ + if (this.database) { + this.database.put("contacts", { id: contact.Userid, contact: contact }) @@ -149,8 +209,8 @@ define(['underscore', 'jquery', 'modernizr'], function(underscore, $, Modernizr) console.log("contacts remove", userid, contact); if (contact) { contactData.remove(userid); - if (database) { - database.delete(userid); + if (this.database) { + this.database.delete("contacts", userid); } this.e.triggerHandler("contactremoved", contact); }