Client Analytics (#9023)

* start to refactor analytics and some mixins

* wip

* wip

* wip

* more analytics

* more analytics

* more anlytics

* fix analytics module

* finish analytics

* fix env

* vue casing
This commit is contained in:
Matteo Pagliazzi
2017-09-08 21:23:58 +02:00
committed by GitHub
parent 6e70f27bc6
commit 1dc1923d7b
26 changed files with 402 additions and 148 deletions
@@ -60,6 +60,7 @@ import bModal from 'bootstrap-vue/lib/components/modal';
import Avatar from '../avatar';
import { mapState } from 'client/libs/store';
import * as Analytics from 'client/libs/analytics';
import percent from '../../../common/script/libs/percent';
import {maxHealth} from '../../../common/script/index';
@@ -84,6 +85,14 @@ export default {
return `${Math.ceil(this.user.stats.hp)} / ${this.maxHealth}`;
},
},
mounted () {
Analytics.track({
hitType: 'event',
eventCategory: 'button',
eventAction: 'click',
eventLabel: 'Health Warning',
});
},
methods: {
close () {
this.$root.$emit('hide::modal', 'low-health');
+7
View File
@@ -219,6 +219,7 @@
import axios from 'axios';
import moment from 'moment';
import { mapState } from 'client/libs/store';
import * as Analytics from 'client/libs/analytics';
import gryphon from 'assets/svg/gryphon.svg';
import twitter from 'assets/svg/twitter.svg';
@@ -331,6 +332,12 @@ export default {
this.$root.$emit('show::modal', 'modify-inventory');
},
donate () {
Analytics.track({
hitType: 'event',
eventCategory: 'button',
eventAction: 'click',
eventLabel: 'Gems > Donate',
});
this.$root.$emit('show::modal', 'buy-gems');
},
},
+7
View File
@@ -224,6 +224,7 @@ div
<script>
import { mapState, mapGetters } from 'client/libs/store';
import * as Analytics from 'client/libs/analytics';
import gemIcon from 'assets/svg/gem.svg';
import goldIcon from 'assets/svg/gold.svg';
import userIcon from 'assets/svg/user.svg';
@@ -289,6 +290,12 @@ export default {
this.$root.$emit('show::modal', 'create-party-modal');
},
showBuyGemsModal () {
Analytics.track({
hitType: 'event',
eventCategory: 'button',
eventAction: 'click',
eventLabel: 'Gems > Toolbar',
});
this.$root.$emit('show::modal', 'buy-gems');
},
},
@@ -290,6 +290,9 @@ export default {
passwordConfirm: this.passwordConfirm,
});
// @TODO do not reload entire page
// problem is that app.vue created hook should be called again
// after user is logged in / just signed up
window.location.href = '/';
},
async login () {
@@ -299,6 +302,9 @@ export default {
password: this.password,
});
// @TODO do not reload entire page
// problem is that app.vue created hook should be called again
// after user is logged in / just signed up
window.location.href = '/';
},
async socialAuth (network) {
@@ -314,6 +320,9 @@ export default {
auth,
});
// @TODO do not reload entire page
// problem is that app.vue created hook should be called again
// after user is logged in / just signed up
window.location.href = '/';
},
handleSubmit () {
@@ -55,6 +55,8 @@
.col-12.text-center.submit-button-wrapper
.alert.alert-warning(v-if='insufficientGemsForTavernChallenge')
You do not have enough gems to create a Tavern challenge
// @TODO if buy gems button is added, add analytics tracking to it
// see https://github.com/HabitRPG/habitica/blob/develop/website/views/options/social/challenges.jade#L134
button.btn.btn-primary(v-once, v-if='creating', @click='createChallenge()') {{$t('createChallengeAddTasks')}}
button.btn.btn-primary(v-once, v-if='!creating', @click='updateChallenge()') {{$t('updateChallenge')}}
.col-12.text-center
@@ -17,7 +17,7 @@ b-modal#create-party-modal(title="Empty", size='lg', hide-footer=true)
.join-party
h3(v-once) {{$t('wantToJoinPartyTitle')}}
p(v-once) {{$t('wantToJoinPartyDescription')}}
button.btn.btn-primary(v-once, @click='shareUserIdShown = !shareUserIdShown') {{$t('shartUserId')}}
button.btn.btn-primary(v-once, @click='shareUserId()') {{$t('shartUserId')}}
.share-userid-options(v-if="shareUserIdShown")
.option-item(v-once)
.svg-icon(v-html="icons.copy")
@@ -137,6 +137,7 @@ b-modal#create-party-modal(title="Empty", size='lg', hide-footer=true)
<script>
import { mapState } from 'client/libs/store';
import * as Analytics from 'client/libs/analytics';
import bModal from 'bootstrap-vue/lib/components/modal';
@@ -166,6 +167,15 @@ export default {
...mapState({user: 'user.data'}),
},
methods: {
shareUserId () {
Analytics.track({
hitType: 'event',
eventCategory: 'button',
eventAction: 'click',
eventLabel: 'Health Warning',
});
this.shareUserIdShown = !this.shareUserIdShown;
},
async createParty () {
let group = {
type: 'party',
@@ -174,9 +184,14 @@ export default {
let party = await this.$store.dispatch('guilds:create', {group});
this.$store.state.party.data = party;
this.user.party._id = party._id;
Analytics.updateUser({
partyID: party._id,
partySize: 1,
});
this.$root.$emit('hide::modal', 'create-party-modal');
this.$router.push('/party');
// @TODO: Analytics.updateUser({'partyID': $scope.group ._id, 'partySize': 1});
},
},
};
+20 -5
View File
@@ -409,6 +409,7 @@ import extend from 'lodash/extend';
import groupUtilities from 'client/mixins/groupsUtilities';
import styleHelper from 'client/mixins/styleHelper';
import { mapState } from 'client/libs/store';
import * as Analytics from 'client/libs/analytics';
import membersModal from './membersModal';
import startQuestModal from './startQuestModal';
import quests from 'common/script/content/quests';
@@ -636,7 +637,7 @@ export default {
},
async sendMessage () {
let response = await this.$store.dispatch('chat:postChat', {
groupId: this.group._id,
group: this.group._id,
message: this.newMessage,
});
this.group.chat.unshift(response.message);
@@ -699,7 +700,13 @@ export default {
this.user.guilds.push(this.group._id);
},
clickLeave () {
// Analytics.track({'hitType':'event','eventCategory':'button','eventAction':'click','eventLabel':'Leave Party'});
Analytics.track({
hitType: 'event',
eventCategory: 'button',
eventAction: 'click',
eventLabel: 'Leave Party',
});
// @TODO: Get challenges and ask to keep or remove
if (!confirm('Are you sure you want to leave?')) return;
let keep = true;
@@ -714,12 +721,14 @@ export default {
keepChallenges,
};
if (this.isParty) data.type = 'party';
if (this.isParty) {
data.type = 'party';
Analytics.updateUser({partySize: null, partyID: null});
}
await this.$store.dispatch('guilds:leave', data);
// @TODO: Implement
// Analytics.updateUser({'partySize':null,'partyID':null});
// User.sync().then(function () {
// $rootScope.hardRedirect('/#/options/groups/party');
// });
@@ -743,7 +752,13 @@ export default {
await this.$store.dispatch('guilds:join', {groupId: this.group._id});
},
clickStartQuest () {
// Analytics.track({'hitType':'event','eventCategory':'button','eventAction':'click','eventLabel':'Start a Quest'});
Analytics.track({
hitType: 'event',
eventCategory: 'button',
eventAction: 'click',
eventLabel: 'Start a Quest',
});
let hasQuests = find(this.user.items.quests, (quest) => {
return quest > 0;
});
@@ -375,7 +375,14 @@ export default {
// @TODO: Add proper notifications
alert('Not enough gems');
return;
// @TODO return $rootScope.openModal('buyGems', {track:"Gems > Create Group"});
// @TODO return $rootScope.openModal('buyGems', {track:"Gems > Gems > Create Group"});
// @TODO when modal is implemented, enable analytics
/* Analytics.track({
hitType: 'event',
eventCategory: 'button',
eventAction: 'click',
eventLabel: 'Health Warning',
}); */
}
if (!this.workingGroup.name || !this.workingGroup.description) {
@@ -55,8 +55,12 @@ import filter from 'lodash/filter';
import map from 'lodash/map';
import bModal from 'bootstrap-vue/lib/components/modal';
import notifications from 'client/mixins/notifications';
import * as Analytics from 'client/libs/analytics';
export default {
components: {
bModal,
},
mixins: [notifications],
props: ['group'],
data () {
@@ -65,8 +69,13 @@ export default {
emails: [],
};
},
components: {
bModal,
mounted () {
Analytics.track({
hitType: 'event',
eventCategory: 'button',
eventAction: 'click',
eventLabel: 'Invite Friends',
});
},
computed: {
...mapState({user: 'user.data'}),
@@ -159,6 +159,7 @@
<script>
import { mapState } from 'client/libs/store';
import * as Analytics from 'client/libs/analytics';
import bModal from 'bootstrap-vue/lib/components/modal';
import quests from 'common/script/content/quests';
@@ -208,10 +209,15 @@ export default {
this.selectedQuest = quest;
},
async questInit () {
let key = this.selectedQuest;
// Analytics.updateUser({'partyID': party._id, 'partySize': party.memberCount});
let response = await this.$store.dispatch('guilds:inviteToQuest', {groupId: this.group._id, key});
let quest = response.data.data;
Analytics.updateUser({
partyID: this.group._id,
partySize: this.group.memberCount,
});
const key = this.selectedQuest;
const response = await this.$store.dispatch('guilds:inviteToQuest', {groupId: this.group._id, key});
const quest = response.data.data;
this.$store.party.quest = quest;
this.$root.$emit('hide::modal', 'start-quest-modal');
},
@@ -137,6 +137,7 @@ import isEmpty from 'lodash/isEmpty';
import map from 'lodash/map';
import { mapState } from 'client/libs/store';
import * as Analytics from 'client/libs/analytics';
import quests from 'common/script/content/quests';
import notificationsIcon from 'assets/svg/notifications.svg';
@@ -299,7 +300,10 @@ export default {
if (type === 'party') {
// @TODO: pretty sure mutability is wrong. Need to check React docs
// @TODO mutation to store data should only happen through actions
this.user.invitations.parties.splice(index, 1);
Analytics.updateUser({partyID: group.id});
} else {
this.user.invitations.guilds.splice(index, 1);
}
@@ -181,6 +181,7 @@
<script>
import bModal from 'bootstrap-vue/lib/components/modal';
import * as Analytics from 'client/libs/analytics';
import svgClose from 'assets/svg/close.svg';
import svgGold from 'assets/svg/gold.svg';
@@ -266,6 +267,14 @@
this.hideDialog();
},
purchaseGems () {
if (this.item.key === 'rebirth_orb') {
Analytics.track({
hitType: 'event',
eventCategory: 'button',
eventAction: 'click',
eventLabel: 'Gems > Rebirth',
});
}
this.$root.$emit('show::modal', 'buy-gems');
},
togglePinned () {
@@ -19,6 +19,7 @@
.row.row-margin(style="font-size: 2rem;")
span {{ $t('enterprisePlansDescription') }}
.row.row-margin
// TODO
a.btn.btn-primary.btn-lg.btn-block(:href="'mailto:vicky@habitica.com?subject=' + $t('enterprisePlansEmailSubject')") {{ $t('enterprisePlansButton') }}
br
@@ -28,3 +29,22 @@
.row.row-margin
a.btn.btn-primary.btn-lg.btn-block(href="https://docs.google.com/forms/d/e/1FAIpQLSerMKkaCg3UcgpcMvBJtlNgnF9DNY8sxCebpAT-GHeDAQASPQ/viewform?usp=sf_link") {{ $t('familyPlansButton') }}
</template>
<script>
import * as Analytics from 'client/libs/analytics';
export default {
methods: {
contactUs () {
Analytics.track({
hitType: 'event',
eventCategory: 'button',
eventAction: 'click',
eventLabel: 'Contact Us (Plans)',
});
window.location.href = `mailto:vicky@habitica.com?subject=${this.$t('enterprisePlansEmailSubject')}`;
},
},
};
</script>
+23 -14
View File
@@ -91,20 +91,29 @@
</style>
<script>
import logo from 'assets/svg/logo.svg';
import logo from 'assets/svg/logo.svg';
import * as Analytics from 'client/libs/analytics';
export default {
data () {
return {
icons: Object.freeze({
logo,
}),
};
export default {
data () {
return {
icons: Object.freeze({
logo,
}),
};
},
methods: {
playButtonClick () {
// @TODO duplicate of code in home.vue
Analytics.track({
hitType: 'event',
eventCategory: 'button',
eventAction: 'click',
eventLabel: 'Play',
});
this.$router.push('/register');
},
methods: {
playButtonClick () {
this.$router.push('/register');
},
},
};
},
};
</script>
@@ -602,6 +602,7 @@
<script>
import AppFooter from 'client/components/appFooter';
import StaticHeader from './header.vue';
import * as Analytics from 'client/libs/analytics';
export default {
components: {
@@ -614,10 +615,21 @@
};
},
mounted () {
// Analytics.track({"hitType":"pageview","eventCategory":"page","eventAction":"landing page","page":"/home"});
Analytics.track({
hitType: 'pageview',
eventCategory: 'page',
eventAction: 'landing page',
page: '/home',
});
},
methods: {
playButtonClick () {
Analytics.track({
hitType: 'event',
eventCategory: 'button',
eventAction: 'click',
eventLabel: 'Play',
});
this.$router.push('/register');
},
},
+2
View File
@@ -298,6 +298,7 @@ import moment from 'moment';
import axios from 'axios';
import scoreTask from 'common/script/ops/scoreTask';
import Vue from 'vue';
import * as Analytics from 'client/libs/analytics';
import positiveIcon from 'assets/svg/positive.svg';
import negativeIcon from 'assets/svg/negative.svg';
@@ -435,6 +436,7 @@ export default {
if (task.group.approval.required) task.group.approval.requested = true;
Analytics.updateUser();
const response = await axios.post(`/api/v3/tasks/${task._id}/score/${direction}`);
const tmp = response.data.data._tmp || {}; // used to notify drops, critical hits and other bonuses
const crit = tmp.crit;