diff --git a/.jshintignore b/.jshintignore index 3546263..7da1c6d 100644 --- a/.jshintignore +++ b/.jshintignore @@ -4,3 +4,4 @@ clientapp/helpers/htmlify.js clientapp/libraries clientapp/templates.js clientapp/.build +clientapp/helpers/callbar.js diff --git a/clientapp/app.js b/clientapp/app.js index e7396a1..a8f2673 100644 --- a/clientapp/app.js +++ b/clientapp/app.js @@ -32,6 +32,7 @@ module.exports = { _.extend(this, Backbone.Events); + var profile = {}; async.series([ function (cb) { app.notifications = new Notify(); @@ -40,16 +41,17 @@ module.exports = { app.storage.open(cb); }, function (cb) { - app.storage.rosterver.get(config.jid, function (err, ver) { - if (ver) { - config.rosterVer = ver; + app.storage.profiles.get(config.jid, function (err, res) { + if (res) { + profile = res; + config.rosterVer = res.rosterVer; } cb(); }); }, function (cb) { app.state = new AppState(); - app.me = window.me = new MeModel(); + app.me = window.me = new MeModel(profile); window.onbeforeunload = function () { if (app.api.sessionStarted) { diff --git a/clientapp/helpers/xmppEventHandlers.js b/clientapp/helpers/xmppEventHandlers.js index 7b9d77e..4c0c13e 100644 --- a/clientapp/helpers/xmppEventHandlers.js +++ b/clientapp/helpers/xmppEventHandlers.js @@ -107,7 +107,7 @@ module.exports = function (client, app) { client.getRoster(function (err, resp) { resp = resp.toJSON(); - app.storage.rosterver.set(me.jid.bare, resp.roster.ver); + me.rosterVer = resp.roster.ver; _.each(resp.roster.items, function (item) { me.setContact(item, true); @@ -116,6 +116,7 @@ module.exports = function (client, app) { var caps = client.updateCaps(); app.storage.disco.add(caps.ver, caps.discoInfo, function () { client.sendPresence({ + status: me.status, caps: client.disco.caps }); client.enableCarbons(); @@ -129,7 +130,7 @@ module.exports = function (client, app) { iq = iq.toJSON(); var items = iq.roster.items; - app.storage.rosterver.set(me.jid.bare, iq.roster.ver); + me.rosterVer = iq.roster.ver; _.each(items, function (item) { var contact = me.getContact(item.jid); diff --git a/clientapp/models/contact.js b/clientapp/models/contact.js index a5e225d..a6f1616 100644 --- a/clientapp/models/contact.js +++ b/clientapp/models/contact.js @@ -148,6 +148,12 @@ module.exports = HumanModel.define({ return res.supportsJingleMedia; }); } + }, + callable: { + deps: ['jingleResources'], + fn: function () { + return !!this.jingleResources.length; + } } }, collections: { diff --git a/clientapp/models/me.js b/clientapp/models/me.js index 36443b5..628ff55 100644 --- a/clientapp/models/me.js +++ b/clientapp/models/me.js @@ -11,20 +11,30 @@ var fetchAvatar = require('../helpers/fetchAvatar'); module.exports = HumanModel.define({ - initialize: function () { - this.bind('change:jid', this.loadContacts, this); + initialize: function (opts) { + if (opts.avatarID) { + this.setAvatar(opts.avatarID); + } + + this.bind('change:jid', this.load, this); this.bind('change:hasFocus', function () { this.setActiveContact(this._activeContact); }, this); this.calls.bind('add remove reset', this.updateActiveCalls, this); + this.bind('change:avatarID', this.save, this); + this.bind('change:status', this.save, this); + this.bind('change:rosterVer', this.save, this); this.contacts.bind('change:unreadCount', this.updateUnreadCount, this); app.state.bind('change:active', this.updateIdlePresence, this); }, - session: { + props: { jid: ['object', true], status: ['string', true, ''], - avatar: ['string', true, ''], avatarID: ['string', true, ''], + rosterVer: ['string', true, ''] + }, + session: { + avatar: ['string', true, ''], connected: ['bool', true, false], shouldAskForAlertsPermission: ['bool', true, false], hasFocus: ['bool', true, false], @@ -83,22 +93,30 @@ module.exports = HumanModel.define({ this.contacts.remove(jid.bare); app.storage.roster.remove(jid.bare); }, - loadContacts: function () { + load: function () { if (!this.jid.bare) return; var self = this; - app.storage.roster.getAll(this.jid.bare, function (err, contacts) { - if (err) return; - contacts.forEach(function (contact) { - contact = new Contact(contact); - contact.owner = self.jid.bare; - contact.inRoster = true; - contact.save(); - self.contacts.add(contact); + app.storage.profiles.get(this.jid.bare, function (err, profile) { + if (!err) { + self.status = profile.status; + self.avatarID = profile.avatarID; + } + self.save(); + app.storage.roster.getAll(self.jid.bare, function (err, contacts) { + if (err) return; + + contacts.forEach(function (contact) { + contact = new Contact(contact); + contact.owner = self.jid.bare; + contact.inRoster = true; + contact.save(); + self.contacts.add(contact); + }); + + self.contacts.trigger('loaded'); }); - - self.contacts.trigger('loaded'); }); }, isMe: function (jid) { @@ -127,5 +145,14 @@ module.exports = HumanModel.define({ }, updateActiveCalls: function () { app.state.hasActiveCall = !!this.calls.length; + }, + save: function () { + var data = { + jid: this.jid.bare, + avatarID: this.avatarID, + status: this.status, + rosterVer: this.rosterVer + }; + app.storage.profiles.set(data); } }); diff --git a/clientapp/storage/index.js b/clientapp/storage/index.js index de83d3d..2a7383c 100644 --- a/clientapp/storage/index.js +++ b/clientapp/storage/index.js @@ -5,7 +5,7 @@ var AvatarStorage = require('./avatars'); var RosterStorage = require('./roster'); var DiscoStorage = require('./disco'); var ArchiveStorage = require('./archive'); -var RosterVerStorage = require('./rosterver'); +var ProfileStorage = require('./profile'); function Storage() { @@ -16,13 +16,13 @@ function Storage() { this.roster = new RosterStorage(this); this.disco = new DiscoStorage(this); this.archive = new ArchiveStorage(this); - this.rosterver = new RosterVerStorage(this); + this.profiles = new ProfileStorage(this); } Storage.prototype = { constructor: { value: Storage }, - version: 2, + version: 3, open: function (cb) { cb = cb || function () {}; @@ -38,7 +38,7 @@ Storage.prototype = { self.roster.setup(db); self.disco.setup(db); self.archive.setup(db); - self.rosterver.setup(db); + self.profiles.setup(db); }; request.onerror = cb; } diff --git a/clientapp/storage/profile.js b/clientapp/storage/profile.js new file mode 100644 index 0000000..d66c3ff --- /dev/null +++ b/clientapp/storage/profile.js @@ -0,0 +1,66 @@ +/*global, IDBKeyRange*/ +"use strict"; + +// SCHEMA +// jid: string +// name: string +// avatarID: string +// status: string +// rosterVer: string + + +function ProfileStorage(storage) { + this.storage = storage; +} + +ProfileStorage.prototype = { + constructor: { + value: ProfileStorage + }, + setup: function (db) { + if (db.objectStoreNames.contains('profiles')) { + db.deleteObjectStore('profiles'); + } + var store = db.createObjectStore('profiles', { + keyPath: 'jid' + }); + }, + transaction: function (mode) { + var trans = this.storage.db.transaction('profiles', mode); + return trans.objectStore('profiles'); + }, + set: function (profile, cb) { + cb = cb || function () {}; + var request = this.transaction('readwrite').put(profile); + request.onsuccess = function () { + cb(false, profile); + }; + request.onerror = cb; + }, + get: function (id, cb) { + cb = cb || function () {}; + if (!id) { + return cb('not-found'); + } + var request = this.transaction('readonly').get(id); + request.onsuccess = function (e) { + var res = request.result; + if (res === undefined) { + return cb('not-found'); + } + cb(false, request.result); + }; + request.onerror = cb; + }, + remove: function (id, cb) { + cb = cb || function () {}; + var request = this.transaction('readwrite')['delete'](id); + request.onsuccess = function (e) { + cb(false, request.result); + }; + request.onerror = cb; + } +}; + + +module.exports = ProfileStorage; diff --git a/clientapp/templates/main.html b/clientapp/templates/main.html index e9b3ae5..d4c08ff 100644 --- a/clientapp/templates/main.html +++ b/clientapp/templates/main.html @@ -11,7 +11,7 @@

Connecting...

- Cancel + Cancel
diff --git a/public/css/app/aux.styl b/public/css/app/aux.styl index 3e742f1..e7aa9d9 100644 --- a/public/css/app/aux.styl +++ b/public/css/app/aux.styl @@ -24,6 +24,10 @@ h2 padding: 0 + .button + float: none + margin-top: 20px + .head, .content padding: 0 20px diff --git a/public/css/app/chat.styl b/public/css/app/chat.styl index d98bc05..2c85d54 100644 --- a/public/css/app/chat.styl +++ b/public/css/app/chat.styl @@ -3,7 +3,6 @@ .page.chat padding-top: 0px - height: 100% borderbox() .conversation @@ -12,7 +11,6 @@ left: 0px right: 0px padding: 0px - height: 100% width: 100% borderbox() @@ -24,9 +22,6 @@ width: 100% display: block - .messages - padding-top: 630px - header padding: 5px border-bottom: 2px solid $grayOutline @@ -45,17 +40,21 @@ left: 11px vertical-align: top + .name, .call + float: left + .name margin: 15px padding: 0px margin-left: 45px font-size: 14px line-height: 14px + max-width: 50% .tzo:not(:empty) position: absolute right: 15px - top: 25px + top: 28px height: 20px margin-top: -10px padding: 0 5px @@ -67,6 +66,12 @@ color: lighten($baseText, 30%) background: $grayOutline + .call + margin-top: 10px + height: 25px + line-height: 25px + min-width: 60px + .messages margin: 0px padding: 0px diff --git a/public/css/app/forms.styl b/public/css/app/forms.styl index 8c111bc..57c411a 100644 --- a/public/css/app/forms.styl +++ b/public/css/app/forms.styl @@ -59,4 +59,7 @@ button, a.button cursor: pointer &:hover - background: darken($activeBlue, 30%) \ No newline at end of file + background: darken($activeBlue, 30%) + +.enableAlerts + margin-right: 5px \ No newline at end of file diff --git a/public/css/app/roster.styl b/public/css/app/roster.styl index f1007cf..9fda121 100644 --- a/public/css/app/roster.styl +++ b/public/css/app/roster.styl @@ -148,7 +148,7 @@ margin-top: -15px position: absolute left: 10px - top: 50% + top: 20px avatar() noselect() @@ -178,7 +178,7 @@ padding-top: 8px roundall(30px) position: absolute - top: 7px + top: 5px left: 10px font-size: 10px font-weight: bold @@ -191,6 +191,11 @@ font-size: 10px color: darken($textSecondary, 50%) +#bookmarks + + .name + padding: 10px 15px 10px 40px + @keyframes pulsate 0% opacity: 1.0 diff --git a/public/css/app/settings.styl b/public/css/app/settings.styl index c5b402b..37da7e2 100644 --- a/public/css/app/settings.styl +++ b/public/css/app/settings.styl @@ -12,6 +12,7 @@ padding: 5px background: $grayBackground roundall(3px) + font-size: $fontSmall .uploadRegion padding: 15px @@ -26,3 +27,6 @@ input width: 100% + + img + margin: 10px 0 diff --git a/public/css/otalk.css b/public/css/otalk.css index bd2b9d0..6e0b9d3 100644 --- a/public/css/otalk.css +++ b/public/css/otalk.css @@ -503,7 +503,7 @@ h3 { margin-top: -15px; position: absolute; left: 10px; - top: 50%; + top: 20px; width: 30px; height: 30px; -moz-border-radius: 50px; @@ -554,7 +554,7 @@ h3 { -border-radius: 30px; border-radius: 30px; position: absolute; - top: 7px; + top: 5px; left: 10px; font-size: 10px; font-weight: bold; @@ -568,6 +568,9 @@ h3 { font-size: 10px; color: #5c5c5c; } +#bookmarks .name { + padding: 10px 15px 10px 40px; +} @-moz-keyframes pulsate { 0% { opacity: 1; @@ -635,7 +638,6 @@ h3 { } .page.chat { padding-top: 0px; - height: 100%; -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; @@ -646,7 +648,6 @@ h3 { left: 0px; right: 0px; padding: 0px; - height: 100%; width: 100%; -moz-box-sizing: border-box; -webkit-box-sizing: border-box; @@ -659,9 +660,6 @@ h3 { width: 100%; display: block; } -.conversation.activeCall .messages { - padding-top: 630px; -} .conversation header { padding: 5px; border-bottom: 2px solid #e4e4e4; @@ -689,17 +687,22 @@ h3 { left: 11px; vertical-align: top; } +.conversation header .name, +.conversation header .call { + float: left; +} .conversation header .name { margin: 15px; padding: 0px; margin-left: 45px; font-size: 14px; line-height: 14px; + max-width: 50%; } .conversation header .tzo:not(:empty) { position: absolute; right: 15px; - top: 25px; + top: 28px; height: 20px; margin-top: -10px; padding: 0 5px; @@ -716,6 +719,12 @@ h3 { color: #898989; background: #e4e4e4; } +.conversation header .call { + margin-top: 10px; + height: 25px; + line-height: 25px; + min-width: 60px; +} .messages { margin: 0px; padding: 0px; @@ -866,6 +875,7 @@ h3 { -o-border-radius: 3px; -border-radius: 3px; border-radius: 3px; + font-size: 12px; } .uploadRegion { padding: 15px; @@ -886,6 +896,9 @@ h3 { .uploadRegion input { width: 100%; } +.uploadRegion img { + margin: 10px 0; +} .aux { background: #f7f7f7; } @@ -917,6 +930,10 @@ h3 { .box.connect h2 { padding: 0; } +.box.connect .button { + float: none; + margin-top: 20px; +} .box .head, .box .content { padding: 0 20px; @@ -1027,6 +1044,9 @@ button:hover, a.button:hover { background: #007aa7; } +.enableAlerts { + margin-right: 5px; +} #wrapper { position: relative !important; -webkit-transition: all 1s; diff --git a/public/x-manifest.cache b/public/x-manifest.cache index 6ba0198..0452612 100644 --- a/public/x-manifest.cache +++ b/public/x-manifest.cache @@ -1,5 +1,5 @@ CACHE MANIFEST -# 0.0.1 1381879209705 +# 0.0.1 1381879352769 CACHE: /app.js