Skip to content

Commit 33f3d1d

Browse files
authored
Merge pull request scratchfoundation#4645 from cwillisf/add-telemetry-hooks
Add telemetry hooks
2 parents 551b7b9 + 5051ba2 commit 33f3d1d

File tree

5 files changed

+93
-11
lines changed

5 files changed

+93
-11
lines changed

src/components/gui/gui.jsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ const GUIComponent = props => {
9797
onActivateTab,
9898
onClickLogo,
9999
onExtensionButtonClick,
100+
onProjectTelemetryEvent,
100101
onRequestCloseBackdropLibrary,
101102
onRequestCloseCostumeLibrary,
102103
onRequestCloseTelemetryModal,
@@ -217,6 +218,7 @@ const GUIComponent = props => {
217218
onCloseAccountNav={onCloseAccountNav}
218219
onLogOut={onLogOut}
219220
onOpenRegistration={onOpenRegistration}
221+
onProjectTelemetryEvent={onProjectTelemetryEvent}
220222
onSeeCommunity={onSeeCommunity}
221223
onShare={onShare}
222224
onToggleLoginOpen={onToggleLoginOpen}

src/components/menu-bar/menu-bar.jsx

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import bindAll from 'lodash.bindall';
66
import bowser from 'bowser';
77
import React from 'react';
88

9+
import VM from 'scratch-vm';
10+
911
import Box from '../box/box.jsx';
1012
import Button from '../button/button.jsx';
1113
import CommunityButton from './community-button.jsx';
@@ -55,6 +57,8 @@ import {
5557
loginMenuOpen
5658
} from '../../reducers/menus';
5759

60+
import collectMetadata from '../../lib/collect-metadata';
61+
5862
import styles from './menu-bar.css';
5963

6064
import helpIcon from '../../lib/assets/icon--tutorials.svg';
@@ -146,10 +150,10 @@ class MenuBar extends React.Component {
146150
'handleClickSaveAsCopy',
147151
'handleClickSeeCommunity',
148152
'handleClickShare',
149-
'handleCloseFileMenuAndThen',
150153
'handleKeyPress',
151154
'handleLanguageMouseUp',
152155
'handleRestoreOption',
156+
'handleSaveToComputer',
153157
'restoreOptionMessage'
154158
]);
155159
}
@@ -216,19 +220,23 @@ class MenuBar extends React.Component {
216220
this.props.onRequestCloseEdit();
217221
};
218222
}
219-
handleCloseFileMenuAndThen (fn) {
220-
return () => {
221-
this.props.onRequestCloseFile();
222-
fn();
223-
};
224-
}
225223
handleKeyPress (event) {
226224
const modifier = bowser.mac ? event.metaKey : event.ctrlKey;
227225
if (modifier && event.key === 's') {
228226
this.props.onClickSave();
229227
event.preventDefault();
230228
}
231229
}
230+
handleSaveToComputer (downloadProjectCallback) {
231+
return () => {
232+
this.props.onRequestCloseFile();
233+
downloadProjectCallback();
234+
if (this.props.onProjectTelemetryEvent) {
235+
const metadata = collectMetadata(this.props.vm, this.props.projectTitle, this.props.locale);
236+
this.props.onProjectTelemetryEvent('projectDidSave', metadata);
237+
}
238+
};
239+
}
232240
handleLanguageMouseUp (e) {
233241
if (!this.props.languageMenuOpen) {
234242
this.props.onClickLanguage(e);
@@ -402,10 +410,10 @@ class MenuBar extends React.Component {
402410
</MenuItem>
403411
)}
404412
</SBFileUploader>
405-
<SB3Downloader>{(className, downloadProject) => (
413+
<SB3Downloader>{(className, downloadProjectCallback) => (
406414
<MenuItem
407415
className={className}
408-
onClick={this.handleCloseFileMenuAndThen(downloadProject)}
416+
onClick={this.handleSaveToComputer(downloadProjectCallback)}
409417
>
410418
<FormattedMessage
411419
defaultMessage="Save to your computer"
@@ -743,6 +751,7 @@ MenuBar.propTypes = {
743751
onLogOut: PropTypes.func,
744752
onOpenRegistration: PropTypes.func,
745753
onOpenTipLibrary: PropTypes.func,
754+
onProjectTelemetryEvent: PropTypes.func,
746755
onRequestCloseAccount: PropTypes.func,
747756
onRequestCloseEdit: PropTypes.func,
748757
onRequestCloseFile: PropTypes.func,
@@ -757,7 +766,8 @@ MenuBar.propTypes = {
757766
renderLogin: PropTypes.func,
758767
sessionExists: PropTypes.bool,
759768
showComingSoon: PropTypes.bool,
760-
username: PropTypes.string
769+
username: PropTypes.string,
770+
vm: PropTypes.instanceOf(VM).isRequired
761771
};
762772

763773
MenuBar.defaultProps = {
@@ -775,11 +785,13 @@ const mapStateToProps = state => {
775785
isUpdating: getIsUpdating(loadingState),
776786
isShowingProject: getIsShowingProject(loadingState),
777787
languageMenuOpen: languageMenuOpen(state),
788+
locale: state.locales.locale,
778789
loginMenuOpen: loginMenuOpen(state),
779790
projectChanged: state.scratchGui.projectChanged,
780791
projectTitle: state.scratchGui.projectTitle,
781792
sessionExists: state.session && typeof state.session.session !== 'undefined',
782-
username: user ? user.username : null
793+
username: user ? user.username : null,
794+
vm: state.scratchGui.vm
783795
};
784796
};
785797

src/lib/collect-metadata.js

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/**
2+
* Report a telemetry event.
3+
* @param {string} event - one of `projectWasCreated`, `projectDidLoad`, `projectDidSave`, `projectWasUploaded`
4+
*/
5+
// TODO make a telemetry HOC and move this stuff there
6+
const collectMetadata = function (vm, projectName = '', locale = '') {
7+
// TODO move most or all of this into a collectMetadata() method on the VM/Runtime
8+
const metadata = {
9+
projectName: projectName,
10+
language: locale,
11+
spriteCount: 0,
12+
blocksCount: 0,
13+
costumesCount: 0,
14+
listsCount: 0,
15+
scriptCount: 0,
16+
soundsCount: 0,
17+
variablesCount: 0
18+
};
19+
20+
for (const target of vm.runtime.targets) {
21+
++metadata.spriteCount;
22+
metadata.blocksCount += Object.keys(target.sprite.blocks._blocks).length;
23+
metadata.costumesCount += target.sprite.costumes_.length;
24+
metadata.scriptCount += target.sprite.blocks._scripts.length;
25+
metadata.soundsCount += target.sprite.sounds.length;
26+
for (const variableName in target.variables) {
27+
const variable = target.variables[variableName];
28+
if (variable.type === 'list') {
29+
++metadata.listsCount;
30+
} else {
31+
++metadata.variablesCount;
32+
}
33+
}
34+
}
35+
36+
return metadata;
37+
};
38+
39+
export default collectMetadata;

src/lib/project-saver-hoc.jsx

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {connect} from 'react-redux';
66
import VM from 'scratch-vm';
77
import xhr from 'xhr';
88

9+
import collectMetadata from '../lib/collect-metadata';
910
import log from '../lib/log';
1011
import storage from '../lib/storage';
1112
import dataURItoBlob from '../lib/data-uri-to-blob';
@@ -25,6 +26,7 @@ import {
2526
getIsAnyCreatingNewState,
2627
getIsCreatingCopy,
2728
getIsCreatingNew,
29+
getIsLoading,
2830
getIsManualUpdating,
2931
getIsRemixing,
3032
getIsShowingWithId,
@@ -61,6 +63,13 @@ const ProjectSaverHOC = function (WrappedComponent) {
6163
this.props.onSetProjectThumbnailer(this.getProjectThumbnail);
6264
}
6365
componentDidUpdate (prevProps) {
66+
if (!this.props.isAnyCreatingNewState && prevProps.isAnyCreatingNewState) {
67+
this.reportTelemetryEvent('projectWasCreated');
68+
}
69+
if (!this.props.isLoading && prevProps.isLoading) {
70+
this.reportTelemetryEvent('projectDidLoad');
71+
}
72+
6473
if (this.props.projectChanged && !prevProps.projectChanged) {
6574
this.scheduleAutoSave();
6675
}
@@ -305,6 +314,18 @@ const ProjectSaverHOC = function (WrappedComponent) {
305314
this.props.vm.renderer.draw();
306315
}
307316

317+
/**
318+
* Report a telemetry event.
319+
* @param {string} event - one of `projectWasCreated`, `projectDidLoad`, `projectDidSave`, `projectWasUploaded`
320+
*/
321+
// TODO make a telemetry HOC and move this stuff there
322+
reportTelemetryEvent (event) {
323+
if (this.props.onProjectTelemetryEvent) {
324+
const metadata = collectMetadata(this.props.vm, this.props.reduxProjectTitle, this.props.locale);
325+
this.props.onProjectTelemetryEvent(event, metadata);
326+
}
327+
}
328+
308329
render () {
309330
const {
310331
/* eslint-disable no-unused-vars */
@@ -314,6 +335,7 @@ const ProjectSaverHOC = function (WrappedComponent) {
314335
isCreatingNew,
315336
projectChanged,
316337
isAnyCreatingNewState,
338+
isLoading,
317339
isManualUpdating,
318340
isRemixing,
319341
isShowingSaveable,
@@ -359,6 +381,7 @@ const ProjectSaverHOC = function (WrappedComponent) {
359381
isAnyCreatingNewState: PropTypes.bool,
360382
isCreatingCopy: PropTypes.bool,
361383
isCreatingNew: PropTypes.bool,
384+
isLoading: PropTypes.bool,
362385
isManualUpdating: PropTypes.bool,
363386
isRemixing: PropTypes.bool,
364387
isShared: PropTypes.bool,
@@ -371,6 +394,7 @@ const ProjectSaverHOC = function (WrappedComponent) {
371394
onCreateProject: PropTypes.func,
372395
onCreatedProject: PropTypes.func,
373396
onProjectError: PropTypes.func,
397+
onProjectTelemetryEvent: PropTypes.func,
374398
onRemixing: PropTypes.func,
375399
onShowAlert: PropTypes.func,
376400
onShowCopySuccessAlert: PropTypes.func,
@@ -397,6 +421,7 @@ const ProjectSaverHOC = function (WrappedComponent) {
397421
return {
398422
autoSaveTimeoutId: state.scratchGui.timeout.autoSaveTimeoutId,
399423
isAnyCreatingNewState: getIsAnyCreatingNewState(loadingState),
424+
isLoading: getIsLoading(loadingState),
400425
isCreatingCopy: getIsCreatingCopy(loadingState),
401426
isCreatingNew: getIsCreatingNew(loadingState),
402427
isRemixing: getIsRemixing(loadingState),
@@ -406,6 +431,7 @@ const ProjectSaverHOC = function (WrappedComponent) {
406431
isUpdating: getIsUpdating(loadingState),
407432
isManualUpdating: getIsManualUpdating(loadingState),
408433
loadingState: loadingState,
434+
locale: state.locales.locale,
409435
projectChanged: state.scratchGui.projectChanged,
410436
reduxProjectId: state.scratchGui.projectState.projectId,
411437
reduxProjectTitle: state.scratchGui.projectTitle,

test/unit/util/project-saver-hoc.test.jsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ describe('projectSaverHOC', () => {
2222
timeout: {
2323
autoSaveTimeoutId: null
2424
}
25+
},
26+
locales: {
27+
locale: 'en'
2528
}
2629
});
2730
vm = new VM();

0 commit comments

Comments
 (0)