Skip to content
This repository has been archived by the owner on Apr 3, 2019. It is now read-only.

Commit

Permalink
feat(client): add expired verification link message and a email resen…
Browse files Browse the repository at this point in the history
…d link
  • Loading branch information
zaach committed Jun 19, 2014
1 parent cf525dc commit 12b1fe7
Show file tree
Hide file tree
Showing 6 changed files with 287 additions and 53 deletions.
63 changes: 63 additions & 0 deletions app/scripts/lib/resend-mixin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// helper functions for views with passwords. Meant to be mixed into views.

'use strict';

define([
], function () {
var SHOW_RESEND_IN_MS = 5 * 60 * 1000; // 5 minutes.

return {
_attemptedSubmits: 0,

beforeDestroy: function () {
if (this._displayResendTimeout) {
this.window.clearTimeout(this._displayResendTimeout);
}
},

beforeSubmit: function () {
// See https://github.com/mozilla/fxa-content-server/issues/885.
// The first click of the resend button sends an email.
// The forth click of the resend button sends an email.
// All other clicks are ignored.
// The button is hidden after the forth click for 5 minutes, then
// start the process again.

this._attemptedSubmits++;

this._updateSuccessMessage();
this._updateResendButton();

return this._attemptedSubmits === 1 || this._attemptedSubmits === 4;
},

_updateSuccessMessage: function () {
// if a success message is already being displayed, shake it.
var successEl = this.$('.success:visible');
if (successEl) {
successEl.one('animationend', function () {
successEl.removeClass('shake');
}).addClass('shake');
}
},

_updateResendButton: function () {
var self = this;
// Hide the button after 4 attempts. Redisplay button after a delay.
if (self._attemptedSubmits === 4) {
self.logEvent(self.className + ':too_many_attempts');
self.$('#resend').hide();
self._displayResendTimeout = setTimeout(function () {
self.window.clearTimeout(self._displayResendTimeout);
self._displayResendTimeout = null;
self._attemptedSubmits = 0;
self.$('#resend').show();
}, SHOW_RESEND_IN_MS);
}
}
};
});
21 changes: 21 additions & 0 deletions app/scripts/templates/complete_sign_up.mustache
Original file line number Diff line number Diff line change
@@ -1,3 +1,24 @@
{{#isLinkExpired}}
<header>
<h1 id="fxa-verification-link-expired-header">{{#t}}Verification link expired{{/t}}</h1>
</header>

<section>
<div class="error"></div>
<div class="success">{{#t}}Email resent{{/t}}</div>

<p>
{{#t}}The link you clicked to verify your email is expired.{{/t}}
</p>

{{#canResend}}
<div class="links">
<a id="resend" class="resend-email" href="#">{{#t}}Click here to receive a new verification link{{/t}}</a>
</div>
{{/canResend}}
</section>
{{/isLinkExpired}}

{{#isLinkDamaged}}
<header>
<h1 id="fxa-verification-link-damaged-header">{{#t}}Verification link damaged{{/t}}</h1>
Expand Down
46 changes: 42 additions & 4 deletions app/scripts/views/complete_sign_up.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,20 @@ define([
'stache!templates/complete_sign_up',
'lib/fxa-client',
'lib/auth-errors',
'lib/validate'
'lib/validate',
'lib/resend-mixin',
'lib/session'
],
function (_, FormView, BaseView, CompleteSignUpTemplate, FxaClient, AuthErrors, Validate) {
function (_, FormView, BaseView, CompleteSignUpTemplate, FxaClient, AuthErrors, Validate, ResendMixin, Session) {
var CompleteSignUpView = FormView.extend({
template: CompleteSignUpTemplate,
className: 'complete_sign_up',

events: {
// validateAndSubmit is used to prevent multiple concurrent submissions.
'click #resend': BaseView.preventDefaultThen('validateAndSubmit')
},

beforeRender: function () {
try {
this.importSearchParam('uid');
Expand Down Expand Up @@ -48,8 +55,10 @@ function (_, FormView, BaseView, CompleteSignUpTemplate, FxaClient, AuthErrors,
return false;
})
.then(null, function (err) {
if (AuthErrors.is(err, 'UNKNOWN_ACCOUNT') ||
AuthErrors.is(err, 'INVALID_VERIFICATION_CODE') ||
if (AuthErrors.is(err, 'UNKNOWN_ACCOUNT')) {
self._isLinkExpired = true;
self.logEvent('complete_sign_up:link_expired');
} else if (AuthErrors.is(err, 'INVALID_VERIFICATION_CODE') ||
AuthErrors.is(err, 'INVALID_PARAMETER')) {
// These errors show a link damaged screen
self._isLinkDamaged = true;
Expand All @@ -70,14 +79,43 @@ function (_, FormView, BaseView, CompleteSignUpTemplate, FxaClient, AuthErrors,

context: function () {
var doesLinkValidate = this._doesLinkValidate();
var isLinkExpired = this._isLinkExpired;
// This is only the case if you've signed up in the same browser
// you opened the verification link in.
var canResend = !!Session.sessionToken;

return {
// If the link is invalid, print a special error message.
isLinkDamaged: ! doesLinkValidate,
isLinkExpired: isLinkExpired,
canResend: canResend,
error: this._error
};
},

// This is called when a user follows an expired verification link
// and clicks the "Resend" link.
submit: function () {
var self = this;

self.logEvent('complete_sign_up:resend');
return this.fxaClient.signUpResend()
.then(function () {
self.displaySuccess();
}, function (err) {
if (AuthErrors.is(err, 'INVALID_TOKEN')) {
return self.navigate('signup', {
error: err
});
}

// unexpected error, rethrow for display.
throw err;
});
}
});

_.extend(CompleteSignUpView.prototype, ResendMixin);

return CompleteSignUpView;
});
54 changes: 7 additions & 47 deletions app/scripts/views/confirm.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@
'use strict';

define([
'underscore',
'views/form',
'views/base',
'stache!templates/confirm',
'lib/session',
'lib/auth-errors'
'lib/auth-errors',
'lib/resend-mixin'
],
function (FormView, BaseView, Template, Session, AuthErrors) {
var SHOW_RESEND_IN_MS = 5 * 60 * 1000; // 5 minutes.
function (_, FormView, BaseView, Template, Session, AuthErrors, ResendMixin) {
var VERIFICATION_POLL_IN_MS = 4000; // 4 seconds

var View = FormView.extend({
Expand All @@ -23,9 +24,7 @@ function (FormView, BaseView, Template, Session, AuthErrors) {
VERIFICATION_POLL_IN_MS: VERIFICATION_POLL_IN_MS,

beforeDestroy: function () {
if (this._displayResendTimeout) {
this.window.clearTimeout(this._displayResendTimeout);
}
ResendMixin.beforeDestroy.call(this);

if (this._verificationTimeout) {
this.window.clearTimeout(this._verificationTimeout);
Expand Down Expand Up @@ -78,47 +77,6 @@ function (FormView, BaseView, Template, Session, AuthErrors) {
}
},

_attemptedSubmits: 0,
beforeSubmit: function () {
// See https://github.com/mozilla/fxa-content-server/issues/885.
// The first click of the resend button sends an email.
// The forth click of the resend button sends an email.
// All other clicks are ignored.
// The button is hidden after the forth click for 5 minutes, then
// start the process again.

this._attemptedSubmits++;

this._updateSuccessMessage();
this._updateResendButton();

return this._attemptedSubmits === 1 || this._attemptedSubmits === 4;
},

_updateSuccessMessage: function () {
// if a success message is already being displayed, shake it.
var successEl = this.$('.success:visible');
if (successEl) {
successEl.one('animationend', function () {
successEl.removeClass('shake');
}).addClass('shake');
}
},

_updateResendButton: function () {
var self = this;
// Hide the button after 4 attempts. Redisplay button after a delay.
if (self._attemptedSubmits === 4) {
self.logEvent('confirm:too_many_attempts');
self.$('#resend').hide();
self._displayResendTimeout = setTimeout(function () {
self._displayResendTimeout = null;
self._attemptedSubmits = 0;
self.$('#resend').show();
}, SHOW_RESEND_IN_MS);
}
},

submit: function () {
var self = this;

Expand All @@ -139,5 +97,7 @@ function (FormView, BaseView, Template, Session, AuthErrors) {
}
});

_.extend(View.prototype, ResendMixin);

return View;
});
13 changes: 12 additions & 1 deletion app/tests/spec/views/complete_sign_up.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,14 @@ function (chai, p, View, AuthErrors, Metrics, Constants, RouterMock, WindowMock,
var validCode = TestHelpers.createRandomHexString(Constants.CODE_LENGTH);
var validUid = TestHelpers.createRandomHexString(Constants.UID_LENGTH);

function testShowsExpiredScreen(search) {
windowMock.location.search = search || '?code=' + validCode + '&uid=' + validUid;
return view.render()
.then(function () {
assert.ok(view.$('#fxa-verification-link-expired-header').length);
});
}

function testShowsDamagedScreen(search) {
windowMock.location.search = search || '?code=' + validCode + '&uid=' + validUid;
return view.render()
Expand Down Expand Up @@ -102,7 +110,10 @@ function (chai, p, View, AuthErrors, Metrics, Constants, RouterMock, WindowMock,

it('UNKNOWN_ACCOUNT error displays the verification link damaged screen', function () {
verificationError = AuthErrors.toError('UNKNOWN_ACCOUNT', 'who are you?');
return testShowsDamagedScreen()
return testShowsExpiredScreen()
.then(function () {
testEventLogged('complete_sign_up:link_expired');
})
.then(function () {
assert.isTrue(view.fxaClient.verifyCode.called);
});
Expand Down

0 comments on commit 12b1fe7

Please sign in to comment.