From 4e31529b3b23bd21bd79084223ec91c1c6088187 Mon Sep 17 00:00:00 2001 From: Simon Eisenmann Date: Fri, 6 Jun 2014 19:10:28 +0200 Subject: [PATCH] Implemented local contact storage. --- src/styles/components/_buddylist.scss | 4 +- .../js/controllers/mediastreamcontroller.js | 1 + static/js/services/appdata.js | 11 +- static/js/services/buddylist.js | 25 +++- static/js/services/buddysession.js | 41 +++--- static/js/services/contactdata.js | 16 ++- static/js/services/contacts.js | 120 +++++++++++++++++- static/partials/buddy.html | 2 +- 8 files changed, 187 insertions(+), 33 deletions(-) diff --git a/src/styles/components/_buddylist.scss b/src/styles/components/_buddylist.scss index 4a11f974..1d000e03 100644 --- a/src/styles/components/_buddylist.scss +++ b/src/styles/components/_buddylist.scss @@ -126,10 +126,10 @@ } .buddy { - &.withSubline .buddy1 { + &.withSubline .buddy1, &.contact .buddy1 { top: 15px; } - &.withSubline .buddy2 { + &.withSubline .buddy2, &.contact .buddy2 { display: block; } &.hovered .buddyactions { diff --git a/static/js/controllers/mediastreamcontroller.js b/static/js/controllers/mediastreamcontroller.js index 17322dcb..d9a936a6 100644 --- a/static/js/controllers/mediastreamcontroller.js +++ b/static/js/controllers/mediastreamcontroller.js @@ -624,6 +624,7 @@ define(['underscore', 'bigscreen', 'moment', 'sjcl', 'webrtc.adapter'], function if (userid) { console.info("Session is now authenticated:", userid); } + appData.e.triggerHandler("authenticationChanged", [userid]); }); // Apply all layout stuff as classes to our element. diff --git a/static/js/services/appdata.js b/static/js/services/appdata.js index 2344904c..dea2595f 100644 --- a/static/js/services/appdata.js +++ b/static/js/services/appdata.js @@ -18,13 +18,17 @@ * along with this program. If not, see . * */ -define([], function() { +define(["jquery"], function($) { + + // appData.e events + // - authenticationChanged(userid) // appData return [function() { var data = { - data: null + data: null, + e: $({}) } var appData = { get: function() { @@ -33,7 +37,8 @@ define([], function() { set: function(d) { data.data = d; return d; - } + }, + e: data.e } return appData; diff --git a/static/js/services/buddylist.js b/static/js/services/buddylist.js index 833e4562..5920e77d 100644 --- a/static/js/services/buddylist.js +++ b/static/js/services/buddylist.js @@ -67,6 +67,12 @@ define(['underscore', 'modernizr', 'avltree', 'text!partials/buddy.html', 'text! }; + BuddyTree.prototype.check = function(id) { + + return this.data.hasOwnProperty(id); + + }; + /** * Returns undefined when no change required. Position result otherwise. */ @@ -535,7 +541,10 @@ define(['underscore', 'modernizr', 'avltree', 'text!partials/buddy.html', 'text! this.updateDisplay(sessionData.Id, scope, sessionData, "status"); } else if (scope.contact) { // Use it with userid as id in tree. - this.tree.add(session.Userid, scope); + if (!this.tree.check(session.Userid)) { + this.tree.add(session.Userid, scope); + buddyCount++; + } } if (!noApply) { scope.$apply(); @@ -567,7 +576,7 @@ define(['underscore', 'modernizr', 'avltree', 'text!partials/buddy.html', 'text! Buddylist.prototype.onContactAdded = function(contact) { - console.log("onContactAdded", contact); + //console.log("onContactAdded", contact); var userid = contact.Userid; var scope = buddyData.get(userid); @@ -581,17 +590,23 @@ define(['underscore', 'modernizr', 'avltree', 'text!partials/buddy.html', 'text! console.log("Injected status into contact", contact); } this.updateDisplay(sessionData.Id, scope, contact, "status"); + scope.$apply(); } } else { - // TODO(longsleep): Implement rendering of contacts without scope. - console.log("No scope for contact", userid); + // Create new scope for contact. + scope = this.onJoined({ + Id: contact.Userid, + Userid: contact.Userid, + Status: contact.Status + }); + scope.contact = contact; } }; Buddylist.prototype.onContactRemoved = function(contact) { - console.log("onContactRemoved", contact); + //console.log("onContactRemoved", contact); var userid = contact.Userid; var scope = buddyData.get(userid); diff --git a/static/js/services/buddysession.js b/static/js/services/buddysession.js index cf2a2013..7d153c51 100644 --- a/static/js/services/buddysession.js +++ b/static/js/services/buddysession.js @@ -30,7 +30,7 @@ define(["underscore"], function(_) { this.serial = serials++; this.sessions = {}; this.count = 0; - //console.log("creating session", this.serial, this); + //console.log("creating session", this.serial, data.Id, data.Userid, this); var id = data.Id; if (data.Id) { var userid = data.Userid || null; @@ -78,7 +78,7 @@ define(["underscore"], function(_) { } else { this.Id = null; } - console.log("Use session as default", id, data, this); + //console.log("Use session as default", id, data, this); }; BuddySession.prototype.remove = function(id, onEmptyCallback) { @@ -96,9 +96,9 @@ define(["underscore"], function(_) { if (sessionData) { this.use(sessionData.Id, sessionData); } else { - console.log("Last session removed", sessions); + //console.log("Last session removed", sessions); if (this.Userid) { - console.log("Using userid as session id"); + //console.log("Using userid as session id"); this.use(this.Userid); } else { this.use(null); @@ -112,22 +112,29 @@ define(["underscore"], function(_) { BuddySession.prototype.update = function(id, data, onUseridCallback) { - var sessionData = this.get(id); - if (!sessionData) { - sessionData = this.add(id, data); - } - var userid = data.Userid; + //console.log("session update", id, userid, this, data); + + var sessionData + if (id === userid) { + // Fake updates from userid ids. + sessionData = data; + } else { + sessionData = this.get(id); + if (!sessionData) { + sessionData = this.add(id, data); + } + } if (userid) { this.auth(userid, sessionData, onUseridCallback); } - - if (data.Rev) { - sessionData.Rev = data.Rev; - } - - if (data.Status) { - sessionData.Status = data.Status; + if (data !== sessionData) { + if (data.Rev) { + sessionData.Rev = data.Rev; + } + if (data.Status) { + sessionData.Status = data.Status; + } } if (id === this.Id) { @@ -142,7 +149,7 @@ define(["underscore"], function(_) { if (!this.Userid) { this.Userid = userid; - console.log("Session now has a user id", this.Id, userid); + //console.log("Session now has a user id", this.Id, userid); } // Trigger callback if defined and not triggered before. if (onUseridCallback && !sessionData.auth) { diff --git a/static/js/services/contactdata.js b/static/js/services/contactdata.js index 0ab3f0dd..b06d2f0e 100644 --- a/static/js/services/contactdata.js +++ b/static/js/services/contactdata.js @@ -29,7 +29,7 @@ define(['underscore', 'jquery'], function(underscore, $) { var contactData = { addByRequest: function(request, status) { - console.log("addByRequest", request, status); + //console.log("addByRequest", request, status); var userid = request.Userid; var token = request.Token; var id; @@ -46,7 +46,19 @@ define(['underscore', 'jquery'], function(underscore, $) { Token: token, Status: null } - // TODO(longsleep): Trigger this to somewhere. + return contact; + }, + addByData: function(data) { + //console.log("addByData", data.Userid, data); + var userid = data.Userid; + if (users.hasOwnProperty(userid)) { + id = users[userid] + } else { + id = String(count++); + users[userid] = id; + } + var contact = contacts[id] = data; + contact.Id = id; return contact; }, get: function(userid) { diff --git a/static/js/services/contacts.js b/static/js/services/contacts.js index cc89d90c..5cd2d2e4 100644 --- a/static/js/services/contacts.js +++ b/static/js/services/contacts.js @@ -18,28 +18,142 @@ * along with this program. If not, see . * */ -define(['underscore', 'jquery'], function(underscore, $) { +define(['underscore', 'jquery', 'modernizr'], function(underscore, $, Modernizr) { + + var Database = function(name, version) { + this.ready = false; + this.db = null; + this.name = name; + this.e = $({}); + var request = indexedDB.open("mediastream", 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" + }); + console.log("Created contacts database.") + }; + request.onsuccess = _.bind(that.onsuccess, that); + }; + 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); + 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); + var request = store.put(data); + if (!errorCallback) { + errorCallback = _.bind(this.onerror, this); + } + transaction.onerror = request.onerror = errorCallback; + if (successCallback) { + request.onsuccess = successCallback; + } + return request; + }; + Database.prototype.delete = function(id, successCallback, errorCallback) { + var transaction = this.db.transaction(this.name, "readwrite"); + var store = transaction.objectStore(this.name); + var request = store.delete(id); + if (!errorCallback) { + errorCallback = _.bind(this.onerror, this); + } + transaction.onerror = request.onerror = errorCallback; + if (successCallback) { + request.onsuccess = successCallback; + } + return request; + }; + Database.prototype.all = function(iteratorCallback, errorCallback) { + var transaction = this.db.transaction(this.name); + var store = transaction.objectStore(this.name); + var keyRange = IDBKeyRange.lowerBound(0); + var cursorRequest = store.openCursor(keyRange); + cursorRequest.onsuccess = function(event) { + var result = event.target.result; + if (!!result === false) { + return; + } + //console.log("read data idb", result, event); + iteratorCallback(result.value); + result.continue(); + }; + if (!errorCallback) { + errorCallback = _.bind(this.onerror, this); + } + transaction.onerror = cursorRequest.onerror = errorCallback; + return cursorRequest; + }; + + var database; + if (Modernizr.indexeddb) { + database = new Database("contacts", 1); + } // contacts - return ["contactData", function(contactData) { + return ["appData", "contactData", function(appData, contactData) { var Contacts = function() { this.e = $({}); + this.userid = null; + appData.e.on("authenticationChanged", _.bind(function(event, userid) { + this.userid = userid; + if (database && userid) { + // TODO(longsleep): This needs to be delayed util self has ha userid. + if (database.ready) { + _.defer(_.bind(this.load, this)); + } else { + database.e.one("ready", _.bind(this.load, this)); + } + } + }, this)); + }; + + Contacts.prototype.load = function() { + console.log("Load contacts from storage", database); + database.all(_.bind(function(data) { + var contact = contactData.addByData(data.contact); + this.e.triggerHandler("contactadded", contact); + }, this)); }; Contacts.prototype.add = function(request, status) { var contact = contactData.addByRequest(request, status); this.e.triggerHandler("contactadded", contact); + if (database) { + database.put({ + id: contact.Userid, + contact: contact + }) + } }; Contacts.prototype.remove = function(userid) { var contact = contactData.get(userid); - //console.log("contacts remove", userid, contact); + console.log("contacts remove", userid, contact); if (contact) { contactData.remove(userid); + if (database) { + database.delete(userid); + } this.e.triggerHandler("contactremoved", contact); } diff --git a/static/partials/buddy.html b/static/partials/buddy.html index d0075eea..ceae38d2 100644 --- a/static/partials/buddy.html +++ b/static/partials/buddy.html @@ -1,5 +1,5 @@
-
{{session.Id|displayName}}
+
{{session.Id|displayName}} {{session.serial}}
({{session.count}}) {{display.subLine}}