Skip to content

Commit

Permalink
Extend UI to require user name input
Browse files Browse the repository at this point in the history
This includes the addition of a "User" model and associated unit tests.
  • Loading branch information
jugglinmike committed Mar 25, 2013
1 parent adfd2dd commit c925664
Show file tree
Hide file tree
Showing 9 changed files with 129 additions and 27 deletions.
1 change: 1 addition & 0 deletions prototype/Gruntfile.js
Expand Up @@ -37,6 +37,7 @@ module.exports = function(grunt) {
define: true,
assert: true,
suite: true,
suiteSetup: true,
test: true
}
},
Expand Down
42 changes: 17 additions & 25 deletions prototype/public/scripts/app.js
@@ -1,6 +1,6 @@
require([
'modules/transport', 'modules/pc', 'modules/layout', 'backbone'
], function(Transport, PC, Layout, Backbone) {
'modules/user', 'modules/transport', 'modules/pc', 'modules/layout', 'backbone'
], function(User, Transport, PC, Layout, Backbone) {
'use strict';

var config = {
Expand Down Expand Up @@ -36,9 +36,11 @@ require([
console.error('Create Answer failed');
}

var user = new User();
var pc = new PC();
var layout = new Layout({
el: '#app',
user: user,
contacts: new Backbone.Collection(contacts)
});
layout.render();
Expand All @@ -54,7 +56,7 @@ require([
this.setLocalDescription(sessionDescription);
transport.peerLocationFind(targetUser, {
session: sessionDescription,
userName: userName
userName: user.get('name')
});
},
createOfferFailed,
Expand Down Expand Up @@ -123,27 +125,17 @@ require([
}*/
});

// Infer username from 'username' query string parameter (default to
// 'creationx') and immediately initiate a connection.
// TODO: First prompt user for name, then initiate a connection.
var userName = 'creationix';
window.location.search
// Remove leading '?'
.slice(1)
.split('&')
.forEach(function(pair) {
pair = pair.split('=');
if (pair[0] === 'username') {
userName = pair[1];
}
});
transport.open(new WebSocket(config.socketServer))
.then(function() {
return transport.sessionCreate(userName);
})
.then(function() {
console.log('Logged in!');
// TODO: Update UI to reflect logged-in state.
}, console.error.bind(console));
user.on('change:name', function() {
transport.open(new WebSocket(config.socketServer))
.then(function() {
return transport.sessionCreate(user.get('name'));
})
.then(function() {
// Simulate network latency
setTimeout(function() {
layout.login();
}, 800);
}, console.error.bind(console));
});

});
10 changes: 8 additions & 2 deletions prototype/public/scripts/modules/layout.js
@@ -1,7 +1,7 @@
define([
'modules/stream-views', 'modules/contacts-view',
'modules/stream-views', 'modules/contacts-view', 'modules/login-view',
'text!templates/layout.html', 'backbone', '_', 'layoutmanager'
], function(StreamViews, ContactsView, html, Backbone, _) {
], function(StreamViews, ContactsView, LoginView, html, Backbone, _) {
'use strict';

var Layout = Backbone.Layout.extend({
Expand All @@ -11,12 +11,18 @@ define([
'click .btn-hang-up': 'hangUp'
},
initialize: function(options) {
this.user = options.user;
this.localStreamView = new StreamViews.LocalStreamView();
this.remoteStreamView = new StreamViews.StreamView();
this.contactsView = new ContactsView({ collection: options.contacts });
this.loginView = new LoginView({ model: this.user });
this.setView('.source', this.localStreamView);
this.setView('.remote', this.remoteStreamView);
this.setView('.contacts-cont', this.contactsView);
this.insertView(this.loginView);
},
login: function() {
this.loginView.remove();
},
playLocalStream: function(stream) {
this.localStreamView.play(stream);
Expand Down
33 changes: 33 additions & 0 deletions prototype/public/scripts/modules/login-view.js
@@ -0,0 +1,33 @@
define([
'text!templates/login.html', 'layoutmanager', '_'
], function(html, Backbone, _) {
'use strict';

var LoginView = Backbone.Layout.extend({
className: 'login',
events: {
submit: 'setName'
},
template: _.template(html),
setName: function(event) {
var result = this.model.set('name', this.$('.username').val(),
{ validate: true });
if (result) {
this.isPending = true;
}
this.render();
event.preventDefault();
},
serialize: function() {
return {
user: this.model.toJSON(),
login: {
error: this.model.validationError,
isPending: this.isPending
}
};
}
});

return LoginView;
});
16 changes: 16 additions & 0 deletions prototype/public/scripts/modules/user.js
@@ -0,0 +1,16 @@
define(['backbone'], function(Backbone) {
'use strict';

var User = Backbone.Model.extend({
nameRegex: /^[0-9a-z\.-]+$/i,
validate: function(attrs) {
if (!attrs || !attrs.name) {
return new Error('No username specified');
} else if (!this.nameRegex.test(attrs.name)) {
return new Error('Invalid username');
}
}
});

return User;
});
23 changes: 23 additions & 0 deletions prototype/public/styles/main.css
Expand Up @@ -46,6 +46,29 @@ body {
cursor: pointer;
padding: 0 0.3em;
}
.login {
color: #eee;
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
} .login .screen {
background-color: #000;
opacity: 0.7;
position: fixed;
width: 100%;
height: 100%;
} .login .dialog {
position: relative;
width: 30%;
margin: 10% auto;
text-align: center;
} .login .dialog .title {
font-size: 1.7em;
font-weight: bold;
margin: 0.7em 0;
}

/**
* For modern browsers
Expand Down
11 changes: 11 additions & 0 deletions prototype/public/templates/login.html
@@ -0,0 +1,11 @@
<div class="screen"></div>
<div class="dialog">
<h1 class="title">Please Log In</h1>
<% if (login.error) { %>
<div class="error"><%= login.error %></div>
<% } %>
<form>
<input type="text" class="username"<%= login.isPending ? ' disabled' : '' %>>
<input type="submit" class="submit" value="Submit"<%= login.isPending ? ' disabled' : '' %>>
</form>
</div>
1 change: 1 addition & 0 deletions prototype/test/client/list_of_tests.js
@@ -1,2 +1,3 @@
define([
'tests/user'
]);
19 changes: 19 additions & 0 deletions prototype/test/client/tests/user.js
@@ -0,0 +1,19 @@
define(['modules/user'], function(User) {
'use strict';

suite('User', function() {
suite('#validate', function() {
var user;
suiteSetup(function() {
user = new User();
});
test('Rejects unspecified string names', function() {
assert.instanceOf(user.validate(), Error);
assert.instanceOf(user.validate({ name: '' }), Error);
});
test('Rejects invalid names', function() {
assert.instanceOf(user.validate({ name: '!@#@#$' }), Error);
});
});
});
});

0 comments on commit c925664

Please sign in to comment.