diff --git a/clientapp/helpers/xmppEventHandlers.js b/clientapp/helpers/xmppEventHandlers.js index f02ed90..fe37350 100644 --- a/clientapp/helpers/xmppEventHandlers.js +++ b/clientapp/helpers/xmppEventHandlers.js @@ -147,6 +147,12 @@ module.exports = function (client, app) { }); }); + client.on('subscribe', function (pres) { + me.contactRequests.add({ + jid: pres.from.bare + }); + }); + client.on('available', function (pres) { pres = pres.toJSON(); var contact = me.getContact(pres.from); diff --git a/clientapp/models/contactRequest.js b/clientapp/models/contactRequest.js new file mode 100644 index 0000000..603d846 --- /dev/null +++ b/clientapp/models/contactRequest.js @@ -0,0 +1,12 @@ +/*global app, me*/ +"use strict"; + +var HumanModel = require('human-model'); + + +module.exports = HumanModel.define({ + type: 'contactRequest', + props: { + jid: ['string', true, ''] + } +}); diff --git a/clientapp/models/contactRequests.js b/clientapp/models/contactRequests.js new file mode 100644 index 0000000..6028507 --- /dev/null +++ b/clientapp/models/contactRequests.js @@ -0,0 +1,10 @@ +"use strict"; + +var BaseCollection = require('./baseCollection'); +var ContactRequest = require('./contactRequest'); + + +module.exports = BaseCollection.extend({ + type: 'contactRequests', + model: ContactRequest +}); diff --git a/clientapp/models/me.js b/clientapp/models/me.js index 247a4d7..7cfeeca 100644 --- a/clientapp/models/me.js +++ b/clientapp/models/me.js @@ -8,6 +8,7 @@ var Calls = require('./calls'); var Contact = require('./contact'); var MUCs = require('./mucs'); var MUC = require('./muc'); +var ContactRequests = require('./contactRequests'); var fetchAvatar = require('../helpers/fetchAvatar'); @@ -45,6 +46,7 @@ module.exports = HumanModel.define({ }, collections: { contacts: Contacts, + contactRequests: ContactRequests, mucs: MUCs, calls: Calls }, @@ -104,8 +106,9 @@ module.exports = HumanModel.define({ } }, removeContact: function (jid) { - this.contacts.remove(jid.bare); - app.storage.roster.remove(jid.bare); + var contact = this.getContact(jid); + this.contacts.remove(contact.jid); + app.storage.roster.remove(contact.storageId); }, load: function () { if (!this.jid.bare) return; diff --git a/clientapp/pages/main.js b/clientapp/pages/main.js index c6b621f..e79ed64 100644 --- a/clientapp/pages/main.js +++ b/clientapp/pages/main.js @@ -5,6 +5,8 @@ var crypto = require('crypto'); var BasePage = require('./base'); var templates = require('../templates'); +var ContactRequestItem = require('../views/contactRequest'); + module.exports = BasePage.extend({ template: templates.pages.main, @@ -20,13 +22,19 @@ module.exports = BasePage.extend({ events: { 'click .enableAlerts': 'enableAlerts', 'click .installFirefox': 'installFirefox', + 'click .addContact': 'handleAddContact', 'dragover': 'handleAvatarChangeDragOver', 'drop': 'handleAvatarChange', 'change #uploader': 'handleAvatarChange', 'blur .status': 'handleStatusChange' }, initialize: function (spec) { + this.render(); + }, + render: function () { this.renderAndBind(); + this.renderCollection(this.model.contactRequests, ContactRequestItem, this.$('#contactrequests')); + return this; }, enableAlerts: function () { if (app.notifications.permissionNeeded()) { @@ -92,5 +100,16 @@ module.exports = BasePage.extend({ status: text, caps: client.disco.caps }); + }, + handleAddContact: function (e) { + e.preventDefault(); + + var contact = this.$('#addcontact').val(); + if (contact) { + app.api.sendPresence({to: contact, type: 'subscribe'}); + } + this.$('#addcontact').val(''); + + return false; } }); diff --git a/clientapp/templates.js b/clientapp/templates.js index 02461c2..a60ba21 100644 --- a/clientapp/templates.js +++ b/clientapp/templates.js @@ -74,6 +74,15 @@ exports.includes.contactListItemResource = function anonymous(locals) { return buf.join(""); }; +// contactRequest.jade compiled template +exports.includes.contactRequest = function anonymous(locals) { + var buf = []; + with (locals || {}) { + buf.push('
  • '); + } + return buf.join(""); +}; + // message.jade compiled template exports.includes.message = function anonymous(locals) { var buf = []; @@ -187,7 +196,7 @@ exports.pages.groupchat = function anonymous(locals) { exports.pages.main = function anonymous(locals) { var buf = []; with (locals || {}) { - buf.push('

    Current status

    Change Avatar

    Drag and drop a new avatar here

    Desktop Integration

    '); + buf.push('

    Current status

    Change Avatar

    Drag and drop a new avatar here

    Add / Approve Contacts

    Desktop Integration

    '); } return buf.join(""); }; diff --git a/clientapp/templates/includes/contactRequest.jade b/clientapp/templates/includes/contactRequest.jade new file mode 100644 index 0000000..7920627 --- /dev/null +++ b/clientapp/templates/includes/contactRequest.jade @@ -0,0 +1,4 @@ +li + span.jid + button.approve Approve + button.deny Deny diff --git a/clientapp/templates/pages/main.jade b/clientapp/templates/pages/main.jade index b01e9fd..1516938 100644 --- a/clientapp/templates/pages/main.jade +++ b/clientapp/templates/pages/main.jade @@ -12,6 +12,12 @@ section.page.main form input#uploader(type="file") + div + h3 Add / Approve Contacts + input#addcontact + button.addContact Add + ul#contactrequests + div h3 Desktop Integration button.enableAlerts Enable alerts diff --git a/clientapp/views/contactRequest.js b/clientapp/views/contactRequest.js new file mode 100644 index 0000000..365cfc1 --- /dev/null +++ b/clientapp/views/contactRequest.js @@ -0,0 +1,43 @@ +/*global $, app*/ +"use strict"; + +var _ = require('underscore'); +var HumanView = require('human-view'); +var templates = require('../templates'); + + +module.exports = HumanView.extend({ + template: templates.includes.contactRequest, + initialize: function (opts) { + this.render(); + }, + events: { + 'click .approve': 'handleApprove', + 'click .deny': 'handleDeny' + }, + textBindings: { + jid: '.jid' + }, + render: function () { + this.renderAndBind({message: this.model}); + return this; + }, + handleApprove: function (e) { + e.preventDefault(); + app.api.sendPresence({ + to: this.model.jid, + type: 'subscribed' + }); + app.me.contactRequests.remove(this.model); + return false; + }, + handleDeny: function (e) { + e.preventDefault(); + app.api.sendPresence({ + to: this.model.jid, + type: 'unsubscribed' + }); + app.me.contactRequests.remove(this.model); + return false; + } +});