Merge pull request #5468 from HabitRPG/sabrecat/analytics-service
Analytics service
This commit is contained in:
+2
-1
@@ -21,7 +21,8 @@
|
||||
"NEW_RELIC_APPLICATION_ID":"NEW_RELIC_APPLICATION_ID",
|
||||
"NEW_RELIC_API_KEY":"NEW_RELIC_API_KEY",
|
||||
"GA_ID": "GA_ID",
|
||||
"MP_ID": "MP_ID",
|
||||
"MIXPANEL_TOKEN": "MIXPANEL_TOKEN",
|
||||
"AMPLITUDE_KEY": "AMPLITUDE_KEY",
|
||||
"FLAG_REPORT_EMAIL": ["email@mod.com"],
|
||||
"EMAIL_SERVER": {
|
||||
"url": "http://example.com",
|
||||
|
||||
@@ -36,6 +36,7 @@ module.exports = function(config) {
|
||||
'common/dist/scripts/habitrpg-shared.js',
|
||||
|
||||
"test/spec/mocks/translations.js",
|
||||
"test/spec/mocks/sandbox.js",
|
||||
|
||||
"website/public/js/env.js",
|
||||
|
||||
@@ -45,6 +46,7 @@ module.exports = function(config) {
|
||||
"website/public/js/services/notificationServices.js",
|
||||
"common/script/public/userServices.js",
|
||||
"common/script/public/directives.js",
|
||||
"website/public/js/services/analyticsServices.js",
|
||||
"website/public/js/services/groupServices.js",
|
||||
"website/public/js/services/memberServices.js",
|
||||
"website/public/js/services/guideServices.js",
|
||||
|
||||
@@ -5,16 +5,22 @@ describe('Auth Controller', function() {
|
||||
describe('AuthCtrl', function(){
|
||||
var scope, ctrl, user, $httpBackend, $window;
|
||||
|
||||
beforeEach(inject(function(_$httpBackend_, $rootScope, $controller) {
|
||||
$httpBackend = _$httpBackend_;
|
||||
scope = $rootScope.$new();
|
||||
scope.loginUsername = 'user';
|
||||
scope.loginPassword = 'pass';
|
||||
$window = { location: { href: ""}, alert: sandbox.spy() };
|
||||
user = { user: {}, authenticate: sandbox.spy() };
|
||||
beforeEach(function(){
|
||||
module(function($provide) {
|
||||
$provide.value('Analytics', analyticsMock);
|
||||
});
|
||||
|
||||
ctrl = $controller('AuthCtrl', {$scope: scope, $window: $window, User: user});
|
||||
}));
|
||||
inject(function(_$httpBackend_, $rootScope, $controller) {
|
||||
$httpBackend = _$httpBackend_;
|
||||
scope = $rootScope.$new();
|
||||
scope.loginUsername = 'user';
|
||||
scope.loginPassword = 'pass';
|
||||
$window = { location: { href: ""}, alert: sandbox.spy() };
|
||||
user = { user: {}, authenticate: sandbox.spy() };
|
||||
|
||||
ctrl = $controller('AuthCtrl', {$scope: scope, $window: $window, User: user});
|
||||
})
|
||||
});
|
||||
|
||||
it('should log in users with correct uname / pass', function() {
|
||||
$httpBackend.expectPOST('/api/v2/user/auth/local').respond({id: 'abc', token: 'abc'});
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
'use strict'
|
||||
|
||||
var analyticsMock = {
|
||||
login: sandbox.spy(),
|
||||
register: sandbox.spy(),
|
||||
updateUser: sandbox.spy(),
|
||||
track: sandbox.spy()
|
||||
};
|
||||
@@ -1,8 +1,4 @@
|
||||
var sandbox;
|
||||
|
||||
beforeEach(function() {
|
||||
sandbox = sinon.sandbox.create();
|
||||
});
|
||||
var sandbox = sinon.sandbox.create();
|
||||
|
||||
afterEach(function() {
|
||||
sandbox.restore();
|
||||
|
||||
@@ -0,0 +1,236 @@
|
||||
'use strict';
|
||||
|
||||
describe('Analytics Service', function () {
|
||||
var analytics, user;
|
||||
|
||||
beforeEach(function() {
|
||||
user = specHelper.newUser();
|
||||
user.contributor = {};
|
||||
user.purchased = { plan: {} };
|
||||
|
||||
module(function($provide) {
|
||||
$provide.value('User', {user: user});
|
||||
});
|
||||
|
||||
inject(function(Analytics) {
|
||||
analytics = Analytics;
|
||||
});
|
||||
});
|
||||
|
||||
context('functions', function() {
|
||||
|
||||
describe('register', function() {
|
||||
|
||||
beforeEach(function() {
|
||||
sandbox.stub(amplitude, 'setUserId');
|
||||
sandbox.stub(window, 'ga');
|
||||
});
|
||||
|
||||
it('sets up user with Amplitude', function() {
|
||||
analytics.register();
|
||||
expect(amplitude.setUserId).to.have.been.calledOnce;
|
||||
expect(amplitude.setUserId).to.have.been.calledWith(user._id);
|
||||
});
|
||||
|
||||
it('sets up user with Google Analytics', function() {
|
||||
analytics.register();
|
||||
expect(ga).to.have.been.calledOnce;
|
||||
expect(ga).to.have.been.calledWith('set', {userId: user._id});
|
||||
});
|
||||
});
|
||||
|
||||
describe('login', function() {
|
||||
|
||||
beforeEach(function() {
|
||||
sandbox.stub(amplitude, 'setUserId');
|
||||
sandbox.stub(window, 'ga');
|
||||
});
|
||||
|
||||
it('sets up tracking for amplitude', function() {
|
||||
analytics.login();
|
||||
|
||||
expect(amplitude.setUserId).to.have.been.calledOnce;
|
||||
expect(amplitude.setUserId).to.have.been.calledWith(user._id);
|
||||
});
|
||||
|
||||
it('sets up tracking for google analytics', function() {
|
||||
analytics.login();
|
||||
|
||||
expect(ga).to.have.been.calledOnce;
|
||||
expect(ga).to.have.been.calledWith('set', {userId: user._id});
|
||||
});
|
||||
});
|
||||
|
||||
describe('track', function() {
|
||||
|
||||
beforeEach(function() {
|
||||
sandbox.stub(amplitude, 'logEvent');
|
||||
sandbox.stub(window, 'ga');
|
||||
});
|
||||
|
||||
context('successful tracking', function() {
|
||||
|
||||
it('tracks a simple user action with Amplitude', function() {
|
||||
var properties = {'hitType':'event','eventCategory':'behavior','eventAction':'cron'};
|
||||
analytics.track(properties);
|
||||
|
||||
expect(amplitude.logEvent).to.have.been.calledOnce;
|
||||
expect(amplitude.logEvent).to.have.been.calledWith('cron', properties);
|
||||
});
|
||||
|
||||
it('tracks a simple user action with Google Analytics', function() {
|
||||
var properties = {'hitType':'event','eventCategory':'behavior','eventAction':'cron'};
|
||||
analytics.track(properties);
|
||||
|
||||
expect(ga).to.have.been.calledOnce;
|
||||
expect(ga).to.have.been.calledWith('send', properties);
|
||||
});
|
||||
|
||||
it('tracks a user action with additional properties in Amplitude', function() {
|
||||
var properties = {'hitType':'event','eventCategory':'behavior','eventAction':'cron','booleanProperty':true,'numericProperty':17,'stringProperty':'bagel'};
|
||||
analytics.track(properties);
|
||||
|
||||
expect(amplitude.logEvent).to.have.been.calledOnce;
|
||||
expect(amplitude.logEvent).to.have.been.calledWith('cron', properties);
|
||||
});
|
||||
|
||||
it('tracks a user action with additional properties in google analytics', function() {
|
||||
var properties = {'hitType':'event','eventCategory':'behavior','eventAction':'cron','booleanProperty':true,'numericProperty':17,'stringProperty':'bagel'};
|
||||
analytics.track(properties);
|
||||
|
||||
expect(ga).to.have.been.calledOnce;
|
||||
expect(ga).to.have.been.calledWith('send', properties);
|
||||
});
|
||||
});
|
||||
|
||||
context('unsuccessful tracking', function() {
|
||||
|
||||
beforeEach(function() {
|
||||
sandbox.stub(console, 'log');
|
||||
});
|
||||
|
||||
context('events without required properties', function() {
|
||||
beforeEach(function(){
|
||||
analytics.track('action');
|
||||
analytics.track({'hitType':'pageview','eventCategory':'green'});
|
||||
analytics.track({'hitType':'pageview','eventAction':'eat'});
|
||||
analytics.track({'eventCategory':'green','eventAction':'eat'});
|
||||
analytics.track({'hitType':'pageview'});
|
||||
analytics.track({'eventCategory':'green'});
|
||||
analytics.track({'eventAction':'eat'});
|
||||
});
|
||||
|
||||
it('logs errors to console', function() {
|
||||
expect(console.log.callCount).to.eql(7);
|
||||
});
|
||||
|
||||
it('does not call out to Amplitude', function() {
|
||||
expect(amplitude.logEvent).to.not.be.called;
|
||||
});
|
||||
|
||||
it('does not call out to Google Analytics', function() {
|
||||
expect(ga).to.not.be.called;
|
||||
});
|
||||
});
|
||||
|
||||
context('incorrect hit type', function() {
|
||||
beforeEach(function() {
|
||||
analytics.track({'hitType':'moogly','eventCategory':'green','eventAction':'eat'});
|
||||
});
|
||||
|
||||
it('logs error to console', function () {
|
||||
expect(console.log).to.have.been.calledOnce;
|
||||
});
|
||||
|
||||
it('does not call out to Amplitude', function() {
|
||||
expect(amplitude.logEvent).to.not.be.called;
|
||||
});
|
||||
|
||||
it('does not call out to Google Analytics', function() {
|
||||
expect(ga).to.not.be.called;
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('updateUser', function() {
|
||||
|
||||
beforeEach(function() {
|
||||
sandbox.stub(amplitude, 'setUserProperties');
|
||||
sandbox.stub(window, 'ga');
|
||||
});
|
||||
|
||||
context('properties argument provided', function(){
|
||||
var properties = {'userBoolean': false, 'userNumber': -8, 'userString': 'Enlightened'};
|
||||
var expectedProperties = _.cloneDeep(properties);
|
||||
expectedProperties.UUID = 'unique-user-id';
|
||||
expectedProperties.Class = 'wizard';
|
||||
expectedProperties.Experience = 35;
|
||||
expectedProperties.Gold = 43;
|
||||
expectedProperties.Health = 48;
|
||||
expectedProperties.Level = 24;
|
||||
expectedProperties.Mana = 41;
|
||||
|
||||
beforeEach(function() {
|
||||
user._id = 'unique-user-id';
|
||||
user.stats.class = 'wizard';
|
||||
user.stats.exp = 35.7;
|
||||
user.stats.gp = 43.2;
|
||||
user.stats.hp = 47.8;
|
||||
user.stats.lvl = 24;
|
||||
user.stats.mp = 41;
|
||||
|
||||
analytics.updateUser(properties);
|
||||
});
|
||||
|
||||
it('calls Amplitude with provided properties and select user info', function() {
|
||||
expect(amplitude.setUserProperties).to.have.been.calledOnce;
|
||||
expect(amplitude.setUserProperties).to.have.been.calledWith(expectedProperties);
|
||||
});
|
||||
|
||||
it('calls Google Analytics with provided properties and select user info', function() {
|
||||
expect(ga).to.have.been.calledOnce;
|
||||
expect(ga).to.have.been.calledWith('set', expectedProperties);
|
||||
});
|
||||
});
|
||||
|
||||
context('no properties argument provided', function() {
|
||||
var expectedProperties = {
|
||||
UUID: 'unique-user-id',
|
||||
Class: 'wizard',
|
||||
Experience: 35,
|
||||
Gold: 43,
|
||||
Health: 48,
|
||||
Level: 24,
|
||||
Mana: 41,
|
||||
contributorLevel: 1,
|
||||
subscription: 'unique-plan-id'
|
||||
};
|
||||
|
||||
beforeEach(function() {
|
||||
user._id = 'unique-user-id';
|
||||
user.stats.class = 'wizard';
|
||||
user.stats.exp = 35.7;
|
||||
user.stats.gp = 43.2;
|
||||
user.stats.hp = 47.8;
|
||||
user.stats.lvl = 24;
|
||||
user.stats.mp = 41;
|
||||
user.contributor.level = 1;
|
||||
user.purchased.plan.planId = 'unique-plan-id';
|
||||
|
||||
analytics.updateUser();
|
||||
});
|
||||
|
||||
it('calls Amplitude with select user info', function() {
|
||||
expect(amplitude.setUserProperties).to.have.been.calledOnce;
|
||||
expect(amplitude.setUserProperties).to.have.been.calledWith(expectedProperties);
|
||||
});
|
||||
|
||||
it('calls Google Analytics with select user info', function() {
|
||||
expect(ga).to.have.been.calledOnce;
|
||||
expect(ga).to.have.been.calledWith('set', expectedProperties);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -243,7 +243,7 @@ window.habitrpg = angular.module('habitrpg',
|
||||
.state('options.settings.notifications', {
|
||||
url: "/notifications",
|
||||
templateUrl: "partials/options.settings.notifications.html"
|
||||
})
|
||||
});
|
||||
|
||||
var settings = JSON.parse(localStorage.getItem(STORAGE_SETTINGS_ID));
|
||||
if (settings && settings.auth) {
|
||||
@@ -251,4 +251,4 @@ window.habitrpg = angular.module('habitrpg',
|
||||
$httpProvider.defaults.headers.common['x-api-user'] = settings.auth.apiId;
|
||||
$httpProvider.defaults.headers.common['x-api-key'] = settings.auth.apiToken;
|
||||
}
|
||||
}])
|
||||
}]);
|
||||
|
||||
@@ -5,8 +5,9 @@
|
||||
*/
|
||||
|
||||
angular.module('habitrpg')
|
||||
.controller("AuthCtrl", ['$scope', '$rootScope', 'User', '$http', '$location', '$window','ApiUrl', '$modal',
|
||||
function($scope, $rootScope, User, $http, $location, $window, ApiUrl, $modal) {
|
||||
.controller("AuthCtrl", ['$scope', '$rootScope', 'User', '$http', '$location', '$window','ApiUrl', '$modal', 'Analytics',
|
||||
function($scope, $rootScope, User, $http, $location, $window, ApiUrl, $modal, Analytics) {
|
||||
$scope.Analytics = Analytics;
|
||||
|
||||
$scope.logout = function() {
|
||||
localStorage.clear();
|
||||
@@ -47,14 +48,14 @@ angular.module('habitrpg')
|
||||
$http.post(url, scope.registerVals).success(function(data, status, headers, config) {
|
||||
runAuth(data.id, data.apiToken);
|
||||
if (status == 200) {
|
||||
mixpanel.alias(data._id);
|
||||
Analytics.register();
|
||||
if (data.auth.facebook) {
|
||||
mixpanel.register({'authType':'facebook','email':data.auth.facebook._json.email})
|
||||
Analytics.updateUser({'email':data.auth.facebook._json.email,'language':data.preferences.language});
|
||||
Analytics.track({'hitType':'event','eventCategory':'acquisition','eventAction':'register','authType':'facebook'});
|
||||
} else {
|
||||
mixpanel.register({'authType':'email','email':data.auth.local.email})
|
||||
Analytics.updateUser({'email':data.auth.local.email,'language':data.preferences.language});
|
||||
Analytics.track({'hitType':'event','eventCategory':'acquisition','eventAction':'register','authType':'email'});
|
||||
}
|
||||
mixpanel.register({'UUID':data._id,'language':data.preferences.language});
|
||||
mixpanel.track('Registration');
|
||||
}
|
||||
}).error(errorAlert);
|
||||
};
|
||||
@@ -68,15 +69,15 @@ angular.module('habitrpg')
|
||||
.success(function(data, status, headers, config) {
|
||||
runAuth(data.id, data.token);
|
||||
if (status == 200) {
|
||||
mixpanel.identify(data.id);
|
||||
mixpanel.register({'UUID':data._id});
|
||||
mixpanel.track('Login');
|
||||
Analytics.login();
|
||||
Analytics.updateUser();
|
||||
Analytics.track({'hitType':'event','eventCategory':'behavior','eventAction':'login'});
|
||||
}
|
||||
}).error(errorAlert);
|
||||
};
|
||||
|
||||
$scope.playButtonClick = function(){
|
||||
window.ga && ga('send', 'event', 'button', 'click', 'Play');
|
||||
Analytics.track({'hitType':'event','eventCategory':'button','eventAction':'click','eventLabel':'Play'})
|
||||
if (User.authenticated()) {
|
||||
window.location.href = ('/' + window.location.hash);
|
||||
} else {
|
||||
@@ -144,9 +145,9 @@ angular.module('habitrpg')
|
||||
$http.post(ApiUrl.get() + "/api/v2/user/auth/social", auth)
|
||||
.success(function(data, status, headers, config) {
|
||||
if (status == 200) {
|
||||
mixpanel.identify(data.id);
|
||||
mixpanel.register({'UUID':data._id});
|
||||
mixpanel.track('Login');
|
||||
Analytics.login();
|
||||
Analytics.updateUser();
|
||||
Analytics.track({'hitType':'event','eventCategory':'behavior','eventAction':'login'});
|
||||
}
|
||||
runAuth(data.id, data.token);
|
||||
}).error(errorAlert);
|
||||
|
||||
@@ -25,20 +25,11 @@ function($scope, $rootScope, User, $http, Notification, ApiUrl) {
|
||||
// Stripe
|
||||
$.getScript('//checkout.stripe.com/v2/checkout.js');
|
||||
|
||||
// Google Analytics, only in production
|
||||
// Google Content Experiments
|
||||
if (window.env.NODE_ENV === 'production') {
|
||||
// Get experiments API
|
||||
$.getScript('//www.google-analytics.com/cx/api.js?experiment=t-AFggRWQnuJ6Teck_x1-Q', function(){
|
||||
$rootScope.variant = cxApi.chooseVariation();
|
||||
$rootScope.$apply();
|
||||
|
||||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
||||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
||||
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
||||
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
|
||||
ga('create', window.env.GA_ID, {userId:User.user._id});
|
||||
ga('require', 'displayfeatures');
|
||||
ga('send', 'pageview');
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -286,7 +286,8 @@ habitrpg.controller("GroupsCtrl", ['$scope', '$rootScope', 'Shared', 'Groups', '
|
||||
});
|
||||
}])
|
||||
|
||||
.controller('ChatCtrl', ['$scope', 'Groups', 'User', '$http', 'ApiUrl', 'Notification', 'Members', '$rootScope', function($scope, Groups, User, $http, ApiUrl, Notification, Members, $rootScope){
|
||||
.controller('ChatCtrl', ['$scope', 'Groups', 'User', '$http', 'ApiUrl', 'Notification', 'Members', '$rootScope', 'Analytics',
|
||||
function($scope, Groups, User, $http, ApiUrl, Notification, Members, $rootScope, Analytics){
|
||||
$scope.message = {content:''};
|
||||
$scope._sending = false;
|
||||
|
||||
@@ -321,9 +322,9 @@ habitrpg.controller("GroupsCtrl", ['$scope', '$rootScope', 'Shared', 'Groups', '
|
||||
$scope.message.content = '';
|
||||
$scope._sending = false;
|
||||
if (group.privacy == 'public'){
|
||||
mixpanel.track('Group Chat',{'groupType':group.type,'privacy':group.privacy,'groupName':group.name,'message':message})
|
||||
Analytics.track({'hitType':'event','eventCategory':'behavior','eventAction':'group chat','groupType':group.type,'privacy':group.privacy,'groupName':group.name,'message':message});
|
||||
} else {
|
||||
mixpanel.track('Group Chat',{'groupType':group.type,'privacy':group.privacy})
|
||||
Analytics.track({'hitType':'event','eventCategory':'behavior','eventAction':'group chat','groupType':group.type,'privacy':group.privacy});
|
||||
}
|
||||
}, function(err){
|
||||
$scope._sending = false;
|
||||
@@ -417,8 +418,8 @@ habitrpg.controller("GroupsCtrl", ['$scope', '$rootScope', 'Shared', 'Groups', '
|
||||
|
||||
}])
|
||||
|
||||
.controller("GuildsCtrl", ['$scope', 'Groups', 'User', 'Challenges', '$rootScope', '$state', '$location', '$compile',
|
||||
function($scope, Groups, User, Challenges, $rootScope, $state, $location, $compile) {
|
||||
.controller("GuildsCtrl", ['$scope', 'Groups', 'User', 'Challenges', '$rootScope', '$state', '$location', '$compile', 'Analytics',
|
||||
function($scope, Groups, User, Challenges, $rootScope, $state, $location, $compile, Analytics) {
|
||||
$scope.groups = {
|
||||
guilds: Groups.myGuilds(),
|
||||
"public": Groups.publicGuilds()
|
||||
@@ -436,8 +437,8 @@ habitrpg.controller("GroupsCtrl", ['$scope', '$rootScope', 'Shared', 'Groups', '
|
||||
|
||||
if (confirm(window.env.t('confirmGuild'))) {
|
||||
group.$save(function(saved){
|
||||
if (saved.privacy == 'public') {mixpanel.track('Join Group',{'owner':true,'groupType':'guild','privacy':saved.privacy,'groupName':saved.name})}
|
||||
else {mixpanel.track('Join Group',{'owner':true,'groupType':'guild','privacy':saved.privacy})}
|
||||
if (saved.privacy == 'public') {Analytics.track({'hitType':'event','eventCategory':'behavior','eventAction':'join group','owner':true,'groupType':'guild','privacy':saved.privacy,'groupName':saved.name})}
|
||||
else {Analytics.track({'hitType':'event','eventCategory':'behavior','eventAction':'join group','owner':true,'groupType':'guild','privacy':saved.privacy})}
|
||||
$rootScope.hardRedirect('/#/options/groups/guilds/' + saved._id);
|
||||
});
|
||||
}
|
||||
@@ -452,8 +453,8 @@ habitrpg.controller("GroupsCtrl", ['$scope', '$rootScope', 'Shared', 'Groups', '
|
||||
}
|
||||
|
||||
group.$join(function(joined){
|
||||
if (joined.privacy == 'public') {mixpanel.track('Join Group',{'owner':false,'groupType':'guild','privacy':joined.privacy,'groupName':joined.name})}
|
||||
else {mixpanel.track('Join Group',{'owner':false,'groupType':'guild','privacy':joined.privacy})}
|
||||
if (joined.privacy == 'public') {Analytics.track({'hitType':'event','eventCategory':'behavior','eventAction':'join group','owner':false,'groupType':'guild','privacy':joined.privacy,'groupName':joined.name})}
|
||||
else {Analytics.track({'hitType':'event','eventCategory':'behavior','eventAction':'join group','owner':false,'groupType':'guild','privacy':joined.privacy})}
|
||||
$rootScope.hardRedirect('/#/options/groups/guilds/' + joined._id);
|
||||
})
|
||||
}
|
||||
@@ -508,8 +509,8 @@ habitrpg.controller("GroupsCtrl", ['$scope', '$rootScope', 'Shared', 'Groups', '
|
||||
}
|
||||
])
|
||||
|
||||
.controller("PartyCtrl", ['$rootScope','$scope', 'Groups', 'User', 'Challenges', '$state', '$compile',
|
||||
function($rootScope,$scope, Groups, User, Challenges, $state, $compile) {
|
||||
.controller("PartyCtrl", ['$rootScope','$scope', 'Groups', 'User', 'Challenges', '$state', '$compile', 'Analytics',
|
||||
function($rootScope,$scope, Groups, User, Challenges, $state, $compile, Analytics) {
|
||||
$scope.type = 'party';
|
||||
$scope.text = window.env.t('party');
|
||||
$scope.group = $rootScope.party = Groups.party();
|
||||
@@ -519,7 +520,7 @@ habitrpg.controller("GroupsCtrl", ['$scope', '$rootScope', 'Shared', 'Groups', '
|
||||
|
||||
$scope.create = function(group){
|
||||
group.$save(function(){
|
||||
mixpanel.track('Join Group',{'owner':true,'groupType':'party','privacy':'private'});
|
||||
Analytics.track({'hitType':'event','eventCategory':'behavior','eventAction':'join group','owner':true,'groupType':'party','privacy':'private'});
|
||||
$rootScope.hardRedirect('/#/options/groups/party');
|
||||
});
|
||||
}
|
||||
@@ -527,7 +528,7 @@ habitrpg.controller("GroupsCtrl", ['$scope', '$rootScope', 'Shared', 'Groups', '
|
||||
$scope.join = function(party){
|
||||
var group = new Groups.Group({_id: party.id, name: party.name});
|
||||
group.$join(function(){
|
||||
mixpanel.track('Join Group',{'owner':false,'groupType':'party','privacy':'private'});
|
||||
Analytics.track({'hitType':'event','eventCategory':'behavior','eventAction':'join group','owner':false,'groupType':'party','privacy':'private'});
|
||||
$rootScope.hardRedirect('/#/options/groups/party');
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
habitrpg.controller("InventoryCtrl",
|
||||
['$rootScope', '$scope', 'Shared', '$window', 'User', 'Content',
|
||||
function($rootScope, $scope, Shared, $window, User, Content) {
|
||||
['$rootScope', '$scope', 'Shared', '$window', 'User', 'Content', 'Analytics',
|
||||
function($rootScope, $scope, Shared, $window, User, Content, Analytics) {
|
||||
|
||||
var user = User.user;
|
||||
|
||||
@@ -180,7 +180,7 @@ habitrpg.controller("InventoryCtrl",
|
||||
$rootScope.selectedQuest = undefined;
|
||||
}
|
||||
$scope.questInit = function(){
|
||||
mixpanel.track("Quest",{"owner":true,"response":"accept","questName":$scope.selectedQuest.key});
|
||||
Analytics.track({'hitType':'event','eventCategory':'behavior','eventAction':'quest','owner':true,'response':'accept','questName':$scope.selectedQuest.key});
|
||||
$rootScope.party.$questAccept({key:$scope.selectedQuest.key}, function(){
|
||||
$rootScope.party.$get();
|
||||
});
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
'use strict';
|
||||
|
||||
habitrpg.controller('NotificationCtrl',
|
||||
['$scope', '$rootScope', 'Shared', 'Content', 'User', 'Guide', 'Notification',
|
||||
function ($scope, $rootScope, Shared, Content, User, Guide, Notification) {
|
||||
['$scope', '$rootScope', 'Shared', 'Content', 'User', 'Guide', 'Notification', 'Analytics',
|
||||
function ($scope, $rootScope, Shared, Content, User, Guide, Notification, Analytics) {
|
||||
|
||||
$rootScope.$watch('user.stats.hp', function (after, before) {
|
||||
if (after <= 0){
|
||||
@@ -87,7 +87,7 @@ habitrpg.controller('NotificationCtrl',
|
||||
Notification.drop(User.user._tmp.drop.dialog);
|
||||
}
|
||||
$rootScope.playSound('Item_Drop');
|
||||
mixpanel.track("Acquire Item",{'itemName':after.key,'acquireMethod':'Drop'})
|
||||
Analytics.track({'hitType':'event','eventCategory':'behavior','eventAction':'acquire item','itemName':after.key,'acquireMethod':'Drop'});
|
||||
});
|
||||
|
||||
$rootScope.$watch('user.achievements.streak', function(after, before){
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
/* Make user and settings available for everyone through root scope.
|
||||
*/
|
||||
|
||||
habitrpg.controller("RootCtrl", ['$scope', '$rootScope', '$location', 'User', '$http', '$state', '$stateParams', 'Notification', 'Groups', 'Shared', 'Content', '$modal', '$timeout', 'ApiUrl', 'Payments','$sce','$window',
|
||||
function($scope, $rootScope, $location, User, $http, $state, $stateParams, Notification, Groups, Shared, Content, $modal, $timeout, ApiUrl, Payments, $sce, $window) {
|
||||
habitrpg.controller("RootCtrl", ['$scope', '$rootScope', '$location', 'User', '$http', '$state', '$stateParams', 'Notification', 'Groups', 'Shared', 'Content', '$modal', '$timeout', 'ApiUrl', 'Payments','$sce','$window','Analytics',
|
||||
function($scope, $rootScope, $location, User, $http, $state, $stateParams, Notification, Groups, Shared, Content, $modal, $timeout, ApiUrl, Payments, $sce, $window, Analytics) {
|
||||
var user = User.user;
|
||||
|
||||
var initSticky = _.once(function(){
|
||||
@@ -15,7 +15,7 @@ habitrpg.controller("RootCtrl", ['$scope', '$rootScope', '$location', 'User', '$
|
||||
|
||||
$rootScope.$on('$stateChangeSuccess',
|
||||
function(event, toState, toParams, fromState, fromParams){
|
||||
if (!!fromState.name) window.ga && ga('send', 'pageview', {page: '/#/'+toState.name});
|
||||
if (!!fromState.name) Analytics.track({'hitType':'pageview','eventCategory':'navigation','eventAction':'navigate','page':'/#/'+toState.name});
|
||||
// clear inbox when entering or exiting inbox tab
|
||||
if (fromState.name=='options.social.inbox' || toState.name=='options.social.inbox') {
|
||||
User.user.ops.update && User.set({'inbox.newMessages':0});
|
||||
@@ -29,6 +29,7 @@ habitrpg.controller("RootCtrl", ['$scope', '$rootScope', '$location', 'User', '$
|
||||
$rootScope.settings = User.settings;
|
||||
$rootScope.Shared = Shared;
|
||||
$rootScope.Content = Content;
|
||||
$rootScope.Analytics = Analytics;
|
||||
$rootScope.env = window.env;
|
||||
$rootScope.Math = Math;
|
||||
$rootScope.Groups = Groups;
|
||||
@@ -126,7 +127,7 @@ habitrpg.controller("RootCtrl", ['$scope', '$rootScope', '$location', 'User', '$
|
||||
// Otherwise use the proper $modal.open
|
||||
$rootScope.openModal = function(template, options){//controller, scope, keyboard, backdrop){
|
||||
if (!options) options = {};
|
||||
if (options.track) window.ga && ga('send', 'event', 'button', 'click', options.track);
|
||||
if (options.track) Analytics.track(_.merge(options.track,{'hitType':'event','eventCategory':'button','eventAction':'click'}));
|
||||
if(template === 'newStuff') return forceLoadBailey(template, options);
|
||||
return $modal.open({
|
||||
templateUrl: 'modals/' + template + '.html',
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"use strict";
|
||||
|
||||
habitrpg.controller("TasksCtrl", ['$scope', '$rootScope', '$location', 'User','Notification', '$http', 'ApiUrl', '$timeout', 'Shared', 'Guide', 'Tasks',
|
||||
function($scope, $rootScope, $location, User, Notification, $http, ApiUrl, $timeout, Shared, Guide, Tasks) {
|
||||
habitrpg.controller("TasksCtrl", ['$scope', '$rootScope', '$location', 'User','Notification', '$http', 'ApiUrl', '$timeout', 'Shared', 'Guide', 'Tasks', 'Analytics',
|
||||
function($scope, $rootScope, $location, User, Notification, $http, ApiUrl, $timeout, Shared, Guide, Tasks, Analytics) {
|
||||
$scope.obj = User.user; // used for task-lists
|
||||
$scope.user = User.user;
|
||||
|
||||
@@ -25,8 +25,8 @@ habitrpg.controller("TasksCtrl", ['$scope', '$rootScope', '$location', 'User','N
|
||||
else if (direction === 'up') $rootScope.playSound('Plus_Habit');
|
||||
}
|
||||
User.user.ops.score({params:{id: task.id, direction:direction}});
|
||||
mixpanel.register({'Gold':Math.floor(User.user.stats.gp),'Health':Math.ceil(User.user.stats.hp),'Experience':Math.floor(User.user.stats.exp),'Level':User.user.stats.lvl,'Mana':Math.floor(User.user.stats.mp),'Class':User.user.stats.class,'subscription':User.user.purchased.plan.planId,'contributorLevel':User.user.contributor.level,'UUID':User.user._id});
|
||||
mixpanel.track('Score Task',{'taskType':task.type,'direction':direction});
|
||||
Analytics.updateUser();
|
||||
Analytics.track({'hitType':'event','eventCategory':'behavior','eventAction':'score task','taskType':task.type,'direction':direction});
|
||||
};
|
||||
|
||||
function addTask(addTo, listDef, task) {
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
'use strict';
|
||||
|
||||
(function(){
|
||||
var REQUIRED_FIELDS = ['hitType','eventCategory','eventAction'];
|
||||
var ALLOWED_HIT_TYPES = ['pageview','screenview','event','transaction','item','social','exception','timing'];
|
||||
|
||||
angular
|
||||
.module('habitrpg')
|
||||
.factory('Analytics', analyticsFactory);
|
||||
|
||||
analyticsFactory.$inject = [
|
||||
'User'
|
||||
];
|
||||
|
||||
function analyticsFactory(User) {
|
||||
|
||||
var user = User.user;
|
||||
|
||||
// Amplitude
|
||||
var r = window.amplitude || {};
|
||||
r._q = [];
|
||||
function a(window) {r[window] = function() {r._q.push([window].concat(Array.prototype.slice.call(arguments, 0)));}}
|
||||
var i = ["init", "logEvent", "logRevenue", "setUserId", "setUserProperties", "setOptOut", "setVersionName", "setDomain", "setDeviceId", "setGlobalUserProperties"];
|
||||
for (var o = 0; o < i.length; o++) {a(i[o])}
|
||||
window.amplitude = r;
|
||||
amplitude.init(window.env.AMPLITUDE_KEY);
|
||||
|
||||
// Google Analytics (aka Universal Analytics)
|
||||
window['GoogleAnalyticsObject'] = 'ga';
|
||||
window['ga'] = window['ga'] || function() {
|
||||
(window['ga'].q = window['ga'].q || []).push(arguments)
|
||||
}, window['ga'].l = 1 * new Date();
|
||||
ga('create', window.env.GA_ID, 'auto');
|
||||
|
||||
function loadScripts() {
|
||||
// Amplitude
|
||||
var n = document.createElement("script");
|
||||
var s = document.getElementsByTagName("script")[0];
|
||||
n.type = "text/javascript";
|
||||
n.async = true;
|
||||
n.src = "https://d24n15hnbwhuhn.cloudfront.net/libs/amplitude-2.2.0-min.gz.js";
|
||||
s.parentNode.insertBefore(n, s);
|
||||
|
||||
// Google Analytics
|
||||
var a = document.createElement('script');
|
||||
var m = document.getElementsByTagName('script')[0];
|
||||
a.async = 1;
|
||||
a.src = '//www.google-analytics.com/analytics.js';
|
||||
m.parentNode.insertBefore(a, m);
|
||||
}
|
||||
|
||||
function register() {
|
||||
amplitude.setUserId(user._id);
|
||||
ga('set', {'userId':user._id});
|
||||
}
|
||||
|
||||
function login() {
|
||||
amplitude.setUserId(user._id);
|
||||
ga('set', {'userId':user._id});
|
||||
}
|
||||
|
||||
function track(properties) {
|
||||
if(_doesNotHaveRequiredFields(properties)) { return false; }
|
||||
if(_doesNotHaveAllowedHitType(properties)) { return false; }
|
||||
|
||||
amplitude.logEvent(properties.eventAction,properties);
|
||||
ga('send',properties);
|
||||
}
|
||||
|
||||
function updateUser(properties) {
|
||||
properties = properties || {};
|
||||
|
||||
_gatherUserStats(user, properties);
|
||||
|
||||
amplitude.setUserProperties(properties);
|
||||
ga('set',properties);
|
||||
}
|
||||
|
||||
if (window.env.NODE_ENV === 'production') loadScripts();
|
||||
|
||||
return {
|
||||
loadScripts: loadScripts,
|
||||
register: register,
|
||||
login: login,
|
||||
track: track,
|
||||
updateUser: updateUser
|
||||
};
|
||||
}
|
||||
|
||||
function _gatherUserStats(user, properties) {
|
||||
if (user._id) properties.UUID = user._id;
|
||||
if (user.stats) {
|
||||
properties.Class = user.stats.class;
|
||||
properties.Experience = Math.floor(user.stats.exp);
|
||||
properties.Gold = Math.floor(user.stats.gp);
|
||||
properties.Health = Math.ceil(user.stats.hp);
|
||||
properties.Level = user.stats.lvl;
|
||||
properties.Mana = Math.floor(user.stats.mp);
|
||||
}
|
||||
if (user.contributor && user.contributor.level) properties.contributorLevel = user.contributor.level;
|
||||
if (user.purchased && user.purchased.plan.planId) properties.subscription = user.purchased.plan.planId;
|
||||
}
|
||||
|
||||
function _doesNotHaveRequiredFields(properties) {
|
||||
if (!_.isEqual(_.keys(_.pick(properties, REQUIRED_FIELDS)), REQUIRED_FIELDS)) {
|
||||
console.log('Analytics tracking calls must include the following properties: ' + JSON.stringify(REQUIRED_FIELDS));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
function _doesNotHaveAllowedHitType(properties) {
|
||||
if (!_.contains(ALLOWED_HIT_TYPES, properties.hitType)) {
|
||||
console.log('Hit type of Analytics event must be one of the following: ' + JSON.stringify(ALLOWED_HIT_TYPES));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}());
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
*/
|
||||
|
||||
angular.module('habitrpg').factory('Guide',
|
||||
['$rootScope', 'User', '$timeout', '$state',
|
||||
function($rootScope, User, $timeout, $state) {
|
||||
['$rootScope', 'User', '$timeout', '$state', 'Analytics',
|
||||
function($rootScope, User, $timeout, $state, Analytics) {
|
||||
|
||||
var chapters = {
|
||||
intro: [
|
||||
@@ -184,14 +184,13 @@ function($rootScope, User, $timeout, $state) {
|
||||
$state.go(step.state);
|
||||
return $timeout(function(){});
|
||||
}
|
||||
window.ga && ga('send', 'event', 'behavior', 'tour', k, i+1);
|
||||
mixpanel.track('Tutorial',{'tour':k+'-web','step':i+1,'complete':false});
|
||||
Analytics.track({'hitType':'event','eventCategory':'behavior','eventAction':'tutorial','eventLabel':k+'-web','eventValue':i+1,'complete':false})
|
||||
}
|
||||
step.onHide = function(){
|
||||
if (step.final) { // -2 indicates complete
|
||||
var ups={};ups['flags.tour.'+k] = -2;
|
||||
User.set(ups);
|
||||
mixpanel.track('Tutorial',{'tour':k+'-web','step':i+1,'complete':true});
|
||||
Analytics.track({'hitType':'event','eventCategory':'behavior','eventAction':'tutorial','eventLabel':k+'-web','eventValue':i+1,'complete':true})
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -22,10 +22,10 @@ window.habitrpg = angular.module('habitrpg', ['chieffancypants.loadingBar', 'ui.
|
||||
$scope.Math = window.Math;
|
||||
}])
|
||||
|
||||
.controller("PlansCtrl", ['$rootScope',
|
||||
function($rootScope) {
|
||||
.controller("PlansCtrl", ['$rootScope','Analytics',
|
||||
function($rootScope,Analytics) {
|
||||
$rootScope.clickContact = function(){
|
||||
window.ga && ga('send', 'event', 'button', 'click', 'Contact Us (Plans)');
|
||||
Analytics.track({'hitType':'event','eventCategory':'button','eventAction':'click','eventLabel':'Contact Us (Plans)'})
|
||||
}
|
||||
}
|
||||
])
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
"js/services/notificationServices.js",
|
||||
"common/script/public/userServices.js",
|
||||
"common/script/public/directives.js",
|
||||
"js/services/analyticsServices.js",
|
||||
"js/services/groupServices.js",
|
||||
"js/services/memberServices.js",
|
||||
"js/services/guideServices.js",
|
||||
@@ -105,6 +106,7 @@
|
||||
"bower_components/angular-loading-bar/build/loading-bar.js",
|
||||
"js/env.js",
|
||||
"js/static.js",
|
||||
"js/services/analyticsServices.js",
|
||||
"js/services/notificationServices.js",
|
||||
"common/script/public/userServices.js",
|
||||
"js/controllers/authCtrl.js",
|
||||
@@ -131,6 +133,7 @@
|
||||
"bower_components/angular-loading-bar/build/loading-bar.js",
|
||||
"js/env.js",
|
||||
"js/static.js",
|
||||
"js/services/analyticsServices.js",
|
||||
"js/services/notificationServices.js",
|
||||
"common/script/public/userServices.js",
|
||||
"js/controllers/authCtrl.js",
|
||||
|
||||
@@ -180,7 +180,7 @@ module.exports.locals = function(req, res, next) {
|
||||
language.momentLang = ((!isStaticPage && i18n.momentLangs[language.code]) || undefined);
|
||||
|
||||
var tavern = require('./models/group').tavern;
|
||||
var envVars = _.pick(nconf.get(), 'NODE_ENV BASE_URL GA_ID STRIPE_PUB_KEY FACEBOOK_KEY'.split(' '));
|
||||
var envVars = _.pick(nconf.get(), 'NODE_ENV BASE_URL GA_ID STRIPE_PUB_KEY FACEBOOK_KEY AMPLITUDE_KEY'.split(' '));
|
||||
res.locals.habitrpg = _.merge(envVars, {
|
||||
IS_MOBILE: /Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(req.header('User-Agent')),
|
||||
getManifestFiles: getManifestFiles,
|
||||
|
||||
@@ -21,11 +21,6 @@ html(ng-app="habitrpg", ng-controller="RootCtrl", ng-class='{"applying-action":a
|
||||
script(type='text/javascript').
|
||||
window.env = !{JSON.stringify(env)};
|
||||
|
||||
script(type='text/javascript').
|
||||
(function(f,b){if(!b.__SV){var a,e,i,g;window.mixpanel=b;b._i=[];b.init=function(a,e,d){function f(b,h){var a=h.split(".");2==a.length&&(b=b[a[0]],h=a[1]);b[h]=function(){b.push([h].concat(Array.prototype.slice.call(arguments,0)))}}var c=b;"undefined"!==typeof d?c=b[d]=[]:d="mixpanel";c.people=c.people||[];c.toString=function(b){var a="mixpanel";"mixpanel"!==d&&(a+="."+d);b||(a+=" (stub)");return a};c.people.toString=function(){return c.toString(1)+".people (stub)"};i="disable track track_pageview track_links track_forms register register_once alias unregister identify name_tag set_config people.set people.set_once people.increment people.append people.union people.track_charge people.clear_charges people.delete_user".split(" ");
|
||||
for(g=0;g<i.length;g++)f(c,i[g]);b._i.push([a,e,d])};b.__SV=1.2;a=f.createElement("script");a.type="text/javascript";a.async=!0;a.src="undefined"!==typeof MIXPANEL_CUSTOM_LIB_URL?MIXPANEL_CUSTOM_LIB_URL:"//cdn.mxpnl.com/libs/mixpanel-2-latest.min.js";e=f.getElementsByTagName("script")[0];e.parentNode.insertBefore(a,e)}})(document,window.mixpanel||[]);
|
||||
mixpanel.init(window.env.MP_ID);
|
||||
|
||||
!= env.getManifestFiles("app")
|
||||
|
||||
//webfonts
|
||||
|
||||
@@ -33,20 +33,13 @@ html(ng-app='habitrpg', ng-controller='RootCtrl')
|
||||
script(type='text/javascript', src='https://cdnjs.cloudflare.com/ajax/libs/angular-ui-bootstrap/0.12.1/ui-bootstrap.min.js')
|
||||
script(type='text/javascript', src='https://cdnjs.cloudflare.com/ajax/libs/angular-ui-bootstrap/0.12.1/ui-bootstrap-tpls.min.js')
|
||||
|
||||
script(type='text/javascript').
|
||||
(function (f, b) {if (!b.__SV) {var a, e, i, g;window.mixpanel = b;b._i = [];b.init = function (a, e, d) {function f(b, h) {var a = h.split(".");2 == a.length && (b = b[a[0]], h = a[1]);b[h] = function () {b.push([h].concat(Array.prototype.slice.call(arguments, 0)))}}var c = b;"undefined" !== typeof d ? c = b[d] = [] : d = "mixpanel";c.people = c.people || [];c.toString = function (b) {var a = "mixpanel";"mixpanel" !== d && (a += "." + d);b || (a += " (stub)");return a};c.people.toString = function () {return c.toString(1) + ".people (stub)"};i = "disable track track_pageview track_links track_forms register register_once alias unregister identify name_tag set_config people.set people.set_once people.increment people.append people.union people.track_charge people.clear_charges people.delete_user".split(" ");
|
||||
for (g = 0; g < i.length; g++)f(c, i[g]);b._i.push([a, e, d])};b.__SV = 1.2;a = f.createElement("script");a.type = "text/javascript";a.async = !0;a.src = "undefined" !== typeof MIXPANEL_CUSTOM_LIB_URL ? MIXPANEL_CUSTOM_LIB_URL : "//cdn.mxpnl.com/libs/mixpanel-2-latest.min.js";e = f.getElementsByTagName("script")[0];e.parentNode.insertBefore(a, e)}})(document, window.mixpanel || []);
|
||||
mixpanel.init(window.env.MP_ID);
|
||||
|
||||
script(type='text/javascript').
|
||||
mixpanel.track("Landing Page");
|
||||
|
||||
body(ng-controller='AuthCtrl')
|
||||
include ./login-modal
|
||||
include ../shared/header/avatar
|
||||
include ../shared/mixins
|
||||
include ../shared/modals/members
|
||||
.mobile-container
|
||||
div(ng-init='Analytics.track({"hitType":"pageview","eventCategory":"page","eventAction":"landing page","page":"/static/front"});')
|
||||
header#header
|
||||
nav.navbar.navbar-default.navbar-static-top
|
||||
.container-fluid
|
||||
|
||||
@@ -27,11 +27,6 @@ html(ng-app='habitrpg')
|
||||
//FIXME for some reason this won't load when in footerCtrl.js#deferredScripts()
|
||||
script(type="text/javascript", src="//s7.addthis.com/js/300/addthis_widget.js#pubid=ra-5016f6cc44ad68a4", async="async")
|
||||
|
||||
script(type='text/javascript').
|
||||
(function (f, b) {if (!b.__SV) {var a, e, i, g;window.mixpanel = b;b._i = [];b.init = function (a, e, d) {function f(b, h) {var a = h.split(".");2 == a.length && (b = b[a[0]], h = a[1]);b[h] = function () {b.push([h].concat(Array.prototype.slice.call(arguments, 0)))}}var c = b;"undefined" !== typeof d ? c = b[d] = [] : d = "mixpanel";c.people = c.people || [];c.toString = function (b) {var a = "mixpanel";"mixpanel" !== d && (a += "." + d);b || (a += " (stub)");return a};c.people.toString = function () {return c.toString(1) + ".people (stub)"};i = "disable track track_pageview track_links track_forms register register_once alias unregister identify name_tag set_config people.set people.set_once people.increment people.append people.union people.track_charge people.clear_charges people.delete_user".split(" ");
|
||||
for (g = 0; g < i.length; g++)f(c, i[g]);b._i.push([a, e, d])};b.__SV = 1.2;a = f.createElement("script");a.type = "text/javascript";a.async = !0;a.src = "undefined" !== typeof MIXPANEL_CUSTOM_LIB_URL ? MIXPANEL_CUSTOM_LIB_URL : "//cdn.mxpnl.com/libs/mixpanel-2-latest.min.js";e = f.getElementsByTagName("script")[0];e.parentNode.insertBefore(a, e)}})(document, window.mixpanel || []);
|
||||
mixpanel.init(window.env.MP_ID);
|
||||
|
||||
!= env.getManifestFiles("static")
|
||||
|
||||
body
|
||||
|
||||
Reference in New Issue
Block a user