Make the pricing page ask for signup and, if completed, redirect to the proper page to subscribe to a plan. Also fixes a redirect issue with Github signin on the new org page

This commit is contained in:
Joseph Schorr 2013-12-11 17:50:48 -05:00
parent 91f4464cb6
commit abe6db334d
5 changed files with 109 additions and 24 deletions

View file

@ -11,9 +11,7 @@
<span class="inner-text">OR</span> <span class="inner-text">OR</span>
</span> </span>
<a id="github-signin-link" <a id="github-signin-link" class="btn btn-primary btn-lg btn-block" href="javascript:void(0)" ng-click="showGithub()">
href="https://github.com/login/oauth/authorize?client_id={{ githubClientId }}&scope=user:email{{ mixpanelDistinctIdClause }}"
class="btn btn-primary btn-lg btn-block">
<i class="fa fa-github fa-lg"></i> Sign In with GitHub <i class="fa fa-github fa-lg"></i> Sign In with GitHub
</a> </a>
</form> </form>

View file

@ -174,7 +174,8 @@ quayApp = angular.module('quay', ['ngRoute', 'restangular', 'angularMoment', 'an
return keyService; return keyService;
}]); }]);
$provide.factory('PlanService', ['Restangular', 'KeyService', 'UserService', function(Restangular, KeyService, UserService) { $provide.factory('PlanService', ['Restangular', 'KeyService', 'UserService', '$cookieStore',
function(Restangular, KeyService, UserService, $cookieStore) {
var plans = null; var plans = null;
var planDict = {}; var planDict = {};
var planService = {}; var planService = {};
@ -193,6 +194,29 @@ quayApp = angular.module('quay', ['ngRoute', 'restangular', 'angularMoment', 'an
} }
}; };
planService.notePlan = function(planId) {
$cookieStore.put('quay.notedplan', planId);
};
planService.handleNotedPlan = function() {
var planId = planService.getAndResetNotedPlan();
if (!planId) { return; }
planService.isBusinessPlan(planId, function(bus) {
if (bus) {
document.location = '/organizations/new/?plan=' + planId;
} else {
document.location = '/user?plan=' + planId;
}
});
};
planService.getAndResetNotedPlan = function() {
var planId = $cookieStore.get('quay.notedplan');
$cookieStore.put('quay.notedplan', '');
return planId;
};
planService.handleCardError = function(resp) { planService.handleCardError = function(resp) {
if (!planService.isCardError(resp)) { return; } if (!planService.isCardError(resp)) { return; }
@ -249,6 +273,20 @@ quayApp = angular.module('quay', ['ngRoute', 'restangular', 'angularMoment', 'an
}); });
}; };
planService.isBusinessPlan = function(planId, callback) {
planService.verifyLoaded(function() {
planSource = plans.business;
for (var i = 0; i < planSource.length; i++) {
var plan = planSource[i];
if (plan.stripeId == planId) {
callback(true);
return;
}
}
callback(false);
});
};
planService.getPlans = function(callback) { planService.getPlans = function(callback) {
planService.verifyLoaded(callback); planService.verifyLoaded(callback);
}; };
@ -602,31 +640,57 @@ quayApp.directive('signinForm', function () {
transclude: true, transclude: true,
restrict: 'C', restrict: 'C',
scope: { scope: {
'redirectUrl': '=redirectUrl' 'redirectUrl': '=redirectUrl',
'signInStarted': '&signInStarted',
'signedIn': '&signedIn'
}, },
controller: function($scope, $location, $timeout, Restangular, KeyService, UserService) { controller: function($scope, $location, $timeout, Restangular, KeyService, UserService) {
$scope.githubClientId = KeyService.githubClientId; $scope.showGithub = function() {
$scope.markStarted();
var appendMixpanelId = function() { var mixpanelDistinctIdClause = '';
if (mixpanel.get_distinct_id !== undefined) { if (mixpanel.get_distinct_id !== undefined) {
$scope.mixpanelDistinctIdClause = "&state=" + mixpanel.get_distinct_id(); $scope.mixpanelDistinctIdClause = "&state=" + encodeURIComponent(mixpanel.get_distinct_id());
} else {
// Mixpanel not yet loaded, try again later
$timeout(appendMixpanelId, 200);
} }
// Needed to ensure that UI work done by the started callback is finished before the location
// changes.
$timeout(function() {
var url = 'https://github.com/login/oauth/authorize?client_id=' + encodeURIComponent(KeyService.githubClientId) +
'&scope=user:email' + mixpanelDistinctIdClause;
document.location = url;
}, 250);
}; };
appendMixpanelId(); $scope.markStarted = function() {
if ($scope.signInStarted != null) {
$scope.signInStarted();
}
};
$scope.signin = function() { $scope.signin = function() {
$scope.markStarted();
var signinPost = Restangular.one('signin'); var signinPost = Restangular.one('signin');
signinPost.customPOST($scope.user).then(function() { signinPost.customPOST($scope.user).then(function() {
$scope.needsEmailVerification = false; $scope.needsEmailVerification = false;
$scope.invalidCredentials = false; $scope.invalidCredentials = false;
// Redirect to the specified page or the landing page if ($scope.signedIn != null) {
$scope.signedIn();
}
UserService.load(); UserService.load();
$location.path($scope.redirectUrl ? $scope.redirectUrl : '/');
// Redirect to the specified page or the landing page
// Note: The timeout of 500ms is needed to ensure dialogs containing sign in
// forms get removed before the location changes.
$timeout(function() {
if ($scope.redirectUrl == $location.path()) {
return;
}
$location.path($scope.redirectUrl ? $scope.redirectUrl : '/');
}, 500);
}, function(result) { }, function(result) {
$scope.needsEmailVerification = result.data.needsEmailVerification; $scope.needsEmailVerification = result.data.needsEmailVerification;
$scope.invalidCredentials = result.data.invalidCredentials; $scope.invalidCredentials = result.data.invalidCredentials;
@ -1850,8 +1914,9 @@ quayApp.directive('ngBlur', function() {
}; };
}); });
quayApp.run(['$location', '$rootScope', 'Restangular', 'UserService', '$http', '$cookieStore', '$timeout', quayApp.run(['$location', '$rootScope', 'Restangular', 'UserService', 'PlanService', '$http', '$cookieStore', '$timeout',
function($location, $rootScope, Restangular, UserService, $http, $cookieStore, $timeout) { function($location, $rootScope, Restangular, UserService, PlanService, $http, $cookieStore, $timeout) {
// Handle session expiration.
Restangular.setErrorInterceptor(function(response) { Restangular.setErrorInterceptor(function(response) {
if (response.status == 401) { if (response.status == 401) {
$('#sessionexpiredModal').modal({}); $('#sessionexpiredModal').modal({});
@ -1861,6 +1926,9 @@ quayApp.run(['$location', '$rootScope', 'Restangular', 'UserService', '$http', '
return true; return true;
}); });
// Check if we need to redirect based on a previously chosen plan.
PlanService.handleNotedPlan();
var changeTab = function(activeTab) { var changeTab = function(activeTab) {
$timeout(function() { $timeout(function() {
$('a[data-toggle="tab"]').each(function(index) { $('a[data-toggle="tab"]').each(function(index) {

View file

@ -38,10 +38,17 @@ function PlansCtrl($scope, $location, UserService, PlanService) {
$scope.user = currentUser; $scope.user = currentUser;
}, true); }, true);
$scope.signedIn = function() {
$('#signinModal').modal('hide');
PlanService.handleNotedPlan();
};
$scope.buyNow = function(plan) { $scope.buyNow = function(plan) {
if ($scope.user && !$scope.user.anonymous) { if ($scope.user && !$scope.user.anonymous) {
document.location = '/user?plan=' + plan; document.location = '/user?plan=' + plan;
} else { } else {
PlanService.notePlan(plan);
$('#signinModal').modal({}); $('#signinModal').modal({});
} }
}; };
@ -50,6 +57,7 @@ function PlansCtrl($scope, $location, UserService, PlanService) {
if ($scope.user && !$scope.user.anonymous) { if ($scope.user && !$scope.user.anonymous) {
document.location = '/organizations/new/?plan=' + plan; document.location = '/organizations/new/?plan=' + plan;
} else { } else {
PlanService.notePlan(plan);
$('#signinModal').modal({}); $('#signinModal').modal({});
} }
}; };
@ -106,7 +114,7 @@ function RepoListCtrl($scope, Restangular, UserService) {
}); });
} }
function LandingCtrl($scope, $timeout, $location, Restangular, UserService, KeyService) { function LandingCtrl($scope, $timeout, $location, Restangular, UserService, KeyService, PlanService) {
$scope.namespace = null; $scope.namespace = null;
$scope.$watch('namespace', function(namespace) { $scope.$watch('namespace', function(namespace) {
@ -1270,7 +1278,7 @@ function OrgsCtrl($scope, UserService) {
function NewOrgCtrl($scope, $routeParams, $timeout, $location, UserService, PlanService, Restangular) { function NewOrgCtrl($scope, $routeParams, $timeout, $location, UserService, PlanService, Restangular) {
$scope.loading = true; $scope.loading = true;
$scope.$watch( function () { return UserService.currentUser(); }, function (currentUser) { $scope.$watch(function () { return UserService.currentUser(); }, function (currentUser) {
$scope.user = currentUser; $scope.user = currentUser;
$scope.loading = false; $scope.loading = false;
}, true); }, true);
@ -1288,6 +1296,16 @@ function NewOrgCtrl($scope, $routeParams, $timeout, $location, UserService, Plan
} }
}); });
$scope.signedIn = function() {
PlanService.handleNotedPlan();
};
$scope.signinStarted = function() {
PlanService.getMinimumPlan(1, true, function(plan) {
PlanService.notePlan(plan.stripeId);
});
};
$scope.setPlan = function(plan) { $scope.setPlan = function(plan) {
$scope.currentPlan = plan; $scope.currentPlan = plan;
}; };

View file

@ -43,7 +43,8 @@
<h4 class="panel-title">Sign In</h4> <h4 class="panel-title">Sign In</h4>
</div> </div>
<div class="panel-body"> <div class="panel-body">
<div class="signin-form" redirect-url="'/organizations/new'"></div> <div class="signin-form" redirect-url="'/organizations/new'" sign-in-started="signinStarted()"
signed-in="signedIn()"></div>
</div> </div>
</div> </div>
</div> </div>

View file

@ -83,7 +83,7 @@
<h4 class="modal-title">Please Sign In</h4> <h4 class="modal-title">Please Sign In</h4>
</div> </div>
<div class="modal-body"> <div class="modal-body">
Please sign into Quay in order to continue <div class="signin-form" signed-in="signedIn()" redirect-url="'/plans/'"></div>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button> <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>