Permalink
Browse files

Extend UI to require user name input

This includes the addition of a "User" model and associated unit tests.
  • Loading branch information...
1 parent adfd2dd commit c925664b1da57c0292fa89af224290b00cef1aa8 @jugglinmike jugglinmike committed Mar 25, 2013
View
@@ -37,6 +37,7 @@ module.exports = function(grunt) {
define: true,
assert: true,
suite: true,
+ suiteSetup: true,
test: true
}
},
@@ -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 = {
@@ -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();
@@ -54,7 +56,7 @@ require([
this.setLocalDescription(sessionDescription);
transport.peerLocationFind(targetUser, {
session: sessionDescription,
- userName: userName
+ userName: user.get('name')
});
},
createOfferFailed,
@@ -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));
+ });
});
@@ -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({
@@ -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);
@@ -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;
+});
@@ -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;
+});
@@ -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
@@ -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,2 +1,3 @@
define([
+ 'tests/user'
]);
@@ -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.