From 5f2f95a6dbce939024ff882f1101f8fd9bde0150 Mon Sep 17 00:00:00 2001 From: Christopher Willis-Ford Date: Fri, 21 Dec 2018 01:46:25 -0800 Subject: [PATCH 1/5] HACK: report telemetry events if telemetry is active --- src/lib/project-saver-hoc.jsx | 53 +++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/src/lib/project-saver-hoc.jsx b/src/lib/project-saver-hoc.jsx index 6e6848e92..f57c1039b 100644 --- a/src/lib/project-saver-hoc.jsx +++ b/src/lib/project-saver-hoc.jsx @@ -25,6 +25,7 @@ import { getIsAnyCreatingNewState, getIsCreatingCopy, getIsCreatingNew, + getIsLoading, getIsManualUpdating, getIsRemixing, getIsShowingWithId, @@ -59,6 +60,13 @@ const ProjectSaverHOC = function (WrappedComponent) { } } componentDidUpdate (prevProps) { + if (!this.props.isAnyCreatingNewState && prevProps.isAnyCreatingNewState) { + this.reportTelemetryEvent('projectWasCreated'); + } + if (!this.props.isLoading && prevProps.isLoading) { + this.reportTelemetryEvent('projectDidLoad'); + } + if (this.props.projectChanged && !prevProps.projectChanged) { this.scheduleAutoSave(); } @@ -289,6 +297,46 @@ const ProjectSaverHOC = function (WrappedComponent) { } } + /** + * Report a telemetry event. + * @param {string} event - one of `projectWasCreated`, `projectDidLoad`, `projectDidSave`, `projectWasUploaded` + */ + //TODO make a telemetry HOC and move this stuff there + reportTelemetryEvent (event) { + if (this.props.onProjectTelemetryEvent) { + //TODO move most or all of this into a collectMetadata() method on the VM/Runtime + const metadata = { + projectName: this.props.reduxProjectTitle, + language: this.props.locale, + spriteCount: 0, + blocksCount: 0, + costumesCount: 0, + listsCount: 0, + scriptCount: 0, + soundsCount: 0, + variablesCount: 0 + }; + + for (const target of this.props.vm.runtime.targets) { + ++metadata.spriteCount; + metadata.blocksCount += Object.keys(target.sprite.blocks._blocks).length; + metadata.costumesCount += target.sprite.costumes_.length; + metadata.scriptCount += target.sprite.blocks._scripts.length; + metadata.soundsCount += target.sprite.sounds.length; + for (const variableName in target.variables) { + const variable = target.variables[variableName]; + if (variable.type === 'list') { + ++metadata.listsCount; + } else { + ++metadata.variablesCount; + } + } + } + + this.props.onProjectTelemetryEvent(event, metadata); + } + } + render () { const { /* eslint-disable no-unused-vars */ @@ -298,6 +346,7 @@ const ProjectSaverHOC = function (WrappedComponent) { isCreatingNew, projectChanged, isAnyCreatingNewState, + isLoading, isManualUpdating, isRemixing, isShowingSaveable, @@ -342,6 +391,7 @@ const ProjectSaverHOC = function (WrappedComponent) { isAnyCreatingNewState: PropTypes.bool, isCreatingCopy: PropTypes.bool, isCreatingNew: PropTypes.bool, + isLoading: PropTypes.bool, isManualUpdating: PropTypes.bool, isRemixing: PropTypes.bool, isShared: PropTypes.bool, @@ -354,6 +404,7 @@ const ProjectSaverHOC = function (WrappedComponent) { onCreateProject: PropTypes.func, onCreatedProject: PropTypes.func, onProjectError: PropTypes.func, + onProjectTelemetryEvent: PropTypes.func, onRemixing: PropTypes.func, onShowAlert: PropTypes.func, onShowCopySuccessAlert: PropTypes.func, @@ -379,6 +430,7 @@ const ProjectSaverHOC = function (WrappedComponent) { return { autoSaveTimeoutId: state.scratchGui.timeout.autoSaveTimeoutId, isAnyCreatingNewState: getIsAnyCreatingNewState(loadingState), + isLoading: getIsLoading(loadingState), isCreatingCopy: getIsCreatingCopy(loadingState), isCreatingNew: getIsCreatingNew(loadingState), isRemixing: getIsRemixing(loadingState), @@ -388,6 +440,7 @@ const ProjectSaverHOC = function (WrappedComponent) { isUpdating: getIsUpdating(loadingState), isManualUpdating: getIsManualUpdating(loadingState), loadingState: loadingState, + locale: state.locales.locale, projectChanged: state.scratchGui.projectChanged, reduxProjectId: state.scratchGui.projectState.projectId, reduxProjectTitle: state.scratchGui.projectTitle, From f6dc2726b559098dc25db28922d73001a57d5ade Mon Sep 17 00:00:00 2001 From: Christopher Willis-Ford Date: Fri, 28 Dec 2018 03:36:30 -0800 Subject: [PATCH 2/5] Implement projectDidSave telemetry event --- src/components/gui/gui.jsx | 2 ++ src/components/menu-bar/menu-bar.jsx | 25 +++++++++++++++--- src/lib/collect-metadata.js | 39 ++++++++++++++++++++++++++++ src/lib/project-saver-hoc.jsx | 31 ++-------------------- 4 files changed, 65 insertions(+), 32 deletions(-) create mode 100644 src/lib/collect-metadata.js diff --git a/src/components/gui/gui.jsx b/src/components/gui/gui.jsx index a203310b1..829215b39 100644 --- a/src/components/gui/gui.jsx +++ b/src/components/gui/gui.jsx @@ -99,6 +99,7 @@ const GUIComponent = props => { onActivateTab, onClickLogo, onExtensionButtonClick, + onProjectTelemetryEvent, onRequestCloseBackdropLibrary, onRequestCloseCostumeLibrary, onRequestCloseTelemetryModal, @@ -225,6 +226,7 @@ const GUIComponent = props => { onCloseAccountNav={onCloseAccountNav} onLogOut={onLogOut} onOpenRegistration={onOpenRegistration} + onProjectTelemetryEvent={onProjectTelemetryEvent} onSeeCommunity={onSeeCommunity} onShare={onShare} onToggleLoginOpen={onToggleLoginOpen} diff --git a/src/components/menu-bar/menu-bar.jsx b/src/components/menu-bar/menu-bar.jsx index 825f6096f..671f94b8e 100644 --- a/src/components/menu-bar/menu-bar.jsx +++ b/src/components/menu-bar/menu-bar.jsx @@ -5,6 +5,8 @@ import PropTypes from 'prop-types'; import bindAll from 'lodash.bindall'; import React from 'react'; +import VM from 'scratch-vm'; + import Box from '../box/box.jsx'; import Button from '../button/button.jsx'; import CommunityButton from './community-button.jsx'; @@ -54,6 +56,8 @@ import { loginMenuOpen } from '../../reducers/menus'; +import collectMetadata from '../../lib/collect-metadata'; + import styles from './menu-bar.css'; import helpIcon from '../../lib/assets/icon--tutorials.svg'; @@ -153,6 +157,7 @@ class MenuBar extends React.Component { 'handleCloseFileMenuAndThen', 'handleLanguageMouseUp', 'handleRestoreOption', + 'handleSaveToComputer', 'restoreOptionMessage' ]); } @@ -219,6 +224,16 @@ class MenuBar extends React.Component { fn(); }; } + handleSaveToComputer (downloadProject) { + return () => { + this.props.onRequestCloseFile(); + downloadProject(); + if (this.props.onProjectTelemetryEvent) { + const metadata = collectMetadata(this.props.vm, this.props.projectTitle, this.props.locale); + this.props.onProjectTelemetryEvent('projectDidSave', metadata); + } + }; + } handleLanguageMouseUp (e) { if (!this.props.languageMenuOpen) { this.props.onClickLanguage(e); @@ -395,7 +410,7 @@ class MenuBar extends React.Component { {(className, downloadProject) => ( { isUpdating: getIsUpdating(loadingState), isShowingProject: getIsShowingProject(loadingState), languageMenuOpen: languageMenuOpen(state), + locale: state.locales.locale, loginMenuOpen: loginMenuOpen(state), projectChanged: state.scratchGui.projectChanged, projectTitle: state.scratchGui.projectTitle, sessionExists: state.session && typeof state.session.session !== 'undefined', - username: user ? user.username : null + username: user ? user.username : null, + vm: state.scratchGui.vm }; }; diff --git a/src/lib/collect-metadata.js b/src/lib/collect-metadata.js new file mode 100644 index 000000000..0623aa96d --- /dev/null +++ b/src/lib/collect-metadata.js @@ -0,0 +1,39 @@ +/** + * Report a telemetry event. + * @param {string} event - one of `projectWasCreated`, `projectDidLoad`, `projectDidSave`, `projectWasUploaded` + */ +//TODO make a telemetry HOC and move this stuff there +const collectMetadata = function (vm, projectName = '', locale = '') { + //TODO move most or all of this into a collectMetadata() method on the VM/Runtime + const metadata = { + projectName: projectName, + language: locale, + spriteCount: 0, + blocksCount: 0, + costumesCount: 0, + listsCount: 0, + scriptCount: 0, + soundsCount: 0, + variablesCount: 0 + }; + + for (const target of vm.runtime.targets) { + ++metadata.spriteCount; + metadata.blocksCount += Object.keys(target.sprite.blocks._blocks).length; + metadata.costumesCount += target.sprite.costumes_.length; + metadata.scriptCount += target.sprite.blocks._scripts.length; + metadata.soundsCount += target.sprite.sounds.length; + for (const variableName in target.variables) { + const variable = target.variables[variableName]; + if (variable.type === 'list') { + ++metadata.listsCount; + } else { + ++metadata.variablesCount; + } + } + } + + return metadata; +}; + +export default collectMetadata; diff --git a/src/lib/project-saver-hoc.jsx b/src/lib/project-saver-hoc.jsx index f57c1039b..addfd6eb0 100644 --- a/src/lib/project-saver-hoc.jsx +++ b/src/lib/project-saver-hoc.jsx @@ -6,6 +6,7 @@ import {connect} from 'react-redux'; import VM from 'scratch-vm'; import xhr from 'xhr'; +import collectMetadata from '../lib/collect-metadata'; import log from '../lib/log'; import storage from '../lib/storage'; import dataURItoBlob from '../lib/data-uri-to-blob'; @@ -304,35 +305,7 @@ const ProjectSaverHOC = function (WrappedComponent) { //TODO make a telemetry HOC and move this stuff there reportTelemetryEvent (event) { if (this.props.onProjectTelemetryEvent) { - //TODO move most or all of this into a collectMetadata() method on the VM/Runtime - const metadata = { - projectName: this.props.reduxProjectTitle, - language: this.props.locale, - spriteCount: 0, - blocksCount: 0, - costumesCount: 0, - listsCount: 0, - scriptCount: 0, - soundsCount: 0, - variablesCount: 0 - }; - - for (const target of this.props.vm.runtime.targets) { - ++metadata.spriteCount; - metadata.blocksCount += Object.keys(target.sprite.blocks._blocks).length; - metadata.costumesCount += target.sprite.costumes_.length; - metadata.scriptCount += target.sprite.blocks._scripts.length; - metadata.soundsCount += target.sprite.sounds.length; - for (const variableName in target.variables) { - const variable = target.variables[variableName]; - if (variable.type === 'list') { - ++metadata.listsCount; - } else { - ++metadata.variablesCount; - } - } - } - + const metadata = collectMetadata(this.props.vm, this.props.reduxProjectTitle, this.props.locale); this.props.onProjectTelemetryEvent(event, metadata); } } From f0ba4180c739fb8a862c4741690ebe22c2797887 Mon Sep 17 00:00:00 2001 From: Christopher Willis-Ford Date: Mon, 4 Mar 2019 16:33:27 -0800 Subject: [PATCH 3/5] Fix spacing in comments --- src/lib/collect-metadata.js | 4 ++-- src/lib/project-saver-hoc.jsx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/collect-metadata.js b/src/lib/collect-metadata.js index 0623aa96d..d71b4448b 100644 --- a/src/lib/collect-metadata.js +++ b/src/lib/collect-metadata.js @@ -2,9 +2,9 @@ * Report a telemetry event. * @param {string} event - one of `projectWasCreated`, `projectDidLoad`, `projectDidSave`, `projectWasUploaded` */ -//TODO make a telemetry HOC and move this stuff there +// TODO make a telemetry HOC and move this stuff there const collectMetadata = function (vm, projectName = '', locale = '') { - //TODO move most or all of this into a collectMetadata() method on the VM/Runtime + // TODO move most or all of this into a collectMetadata() method on the VM/Runtime const metadata = { projectName: projectName, language: locale, diff --git a/src/lib/project-saver-hoc.jsx b/src/lib/project-saver-hoc.jsx index 2eb837963..2cee625ec 100644 --- a/src/lib/project-saver-hoc.jsx +++ b/src/lib/project-saver-hoc.jsx @@ -318,7 +318,7 @@ const ProjectSaverHOC = function (WrappedComponent) { * Report a telemetry event. * @param {string} event - one of `projectWasCreated`, `projectDidLoad`, `projectDidSave`, `projectWasUploaded` */ - //TODO make a telemetry HOC and move this stuff there + // TODO make a telemetry HOC and move this stuff there reportTelemetryEvent (event) { if (this.props.onProjectTelemetryEvent) { const metadata = collectMetadata(this.props.vm, this.props.reduxProjectTitle, this.props.locale); From 3e33c10b04e7d314c49a5e9af36ad0d9c3473a94 Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Thu, 7 Mar 2019 14:12:20 +0100 Subject: [PATCH 4/5] Fix mock store for project-saver-hoc test Account for the addition of locale prop expected in mapStateToProps. Note: if/when the telemetry code gets moved into its own hoc, the prop should get removed, and the test should get updated as well. --- test/unit/util/project-saver-hoc.test.jsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/unit/util/project-saver-hoc.test.jsx b/test/unit/util/project-saver-hoc.test.jsx index 62176a7c5..1336daef0 100644 --- a/test/unit/util/project-saver-hoc.test.jsx +++ b/test/unit/util/project-saver-hoc.test.jsx @@ -22,6 +22,9 @@ describe('projectSaverHOC', () => { timeout: { autoSaveTimeoutId: null } + }, + locales: { + locale: 'en' } }); vm = new VM(); From 5051ba2105b58b5f4789b35ca62c5bfebd54b24b Mon Sep 17 00:00:00 2001 From: Christopher Willis-Ford Date: Thu, 14 Mar 2019 10:35:39 -0700 Subject: [PATCH 5/5] Code review: remove dead code, clarify arg name --- src/components/menu-bar/menu-bar.jsx | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/src/components/menu-bar/menu-bar.jsx b/src/components/menu-bar/menu-bar.jsx index 966aa5271..972809d2d 100644 --- a/src/components/menu-bar/menu-bar.jsx +++ b/src/components/menu-bar/menu-bar.jsx @@ -150,7 +150,6 @@ class MenuBar extends React.Component { 'handleClickSaveAsCopy', 'handleClickSeeCommunity', 'handleClickShare', - 'handleCloseFileMenuAndThen', 'handleKeyPress', 'handleLanguageMouseUp', 'handleRestoreOption', @@ -221,12 +220,6 @@ class MenuBar extends React.Component { this.props.onRequestCloseEdit(); }; } - handleCloseFileMenuAndThen (fn) { - return () => { - this.props.onRequestCloseFile(); - fn(); - }; - } handleKeyPress (event) { const modifier = bowser.mac ? event.metaKey : event.ctrlKey; if (modifier && event.key === 's') { @@ -234,10 +227,10 @@ class MenuBar extends React.Component { event.preventDefault(); } } - handleSaveToComputer (downloadProject) { + handleSaveToComputer (downloadProjectCallback) { return () => { this.props.onRequestCloseFile(); - downloadProject(); + downloadProjectCallback(); if (this.props.onProjectTelemetryEvent) { const metadata = collectMetadata(this.props.vm, this.props.projectTitle, this.props.locale); this.props.onProjectTelemetryEvent('projectDidSave', metadata); @@ -417,10 +410,10 @@ class MenuBar extends React.Component { )} - {(className, downloadProject) => ( + {(className, downloadProjectCallback) => (