From ee80e07ade3374d6514bf1ccaff9220922c396ba Mon Sep 17 00:00:00 2001 From: Christopher Willis-Ford Date: Wed, 19 Dec 2018 18:31:13 -0800 Subject: [PATCH] Add telemetry opt-in/opt-out dialog The playground now displays this on startup when in Scratch Desktop simulation mode. --- src/components/gui/gui.jsx | 17 +++ .../telemetry-modal-header.png | Bin 0 -> 14068 bytes .../telemetry-modal/telemetry-modal.css | 82 +++++++++++ .../telemetry-modal/telemetry-modal.jsx | 136 ++++++++++++++++++ src/containers/gui.jsx | 10 +- src/lib/app-state-hoc.jsx | 6 +- src/playground/render-gui.jsx | 19 +++ src/reducers/gui.js | 15 +- src/reducers/modals.js | 10 ++ 9 files changed, 290 insertions(+), 5 deletions(-) create mode 100644 src/components/telemetry-modal/telemetry-modal-header.png create mode 100644 src/components/telemetry-modal/telemetry-modal.css create mode 100644 src/components/telemetry-modal/telemetry-modal.jsx diff --git a/src/components/gui/gui.jsx b/src/components/gui/gui.jsx index 3eef5b1a3..14b7c7805 100644 --- a/src/components/gui/gui.jsx +++ b/src/components/gui/gui.jsx @@ -31,6 +31,7 @@ import Cards from '../../containers/cards.jsx'; import Alerts from '../../containers/alerts.jsx'; import DragLayer from '../../containers/drag-layer.jsx'; import ConnectionModal from '../../containers/connection-modal.jsx'; +import TelemetryModal from '../telemetry-modal/telemetry-modal.jsx'; import layout, {STAGE_SIZE_MODES} from '../../lib/layout-constants'; import {resolveStageSize} from '../../lib/screen-utils'; @@ -100,6 +101,7 @@ const GUIComponent = props => { onExtensionButtonClick, onRequestCloseBackdropLibrary, onRequestCloseCostumeLibrary, + onRequestCloseTelemetryModal, onSeeCommunity, onShare, previewInfoVisible, @@ -107,6 +109,8 @@ const GUIComponent = props => { soundsTabVisible, stageSizeMode, targetIsStage, + telemetryModalHandlers, + telemetryModalVisible, tipsLibraryVisible, vm, ...componentProps @@ -151,6 +155,12 @@ const GUIComponent = props => { {previewInfoVisible ? ( ) : null} + {telemetryModalVisible ? ( + + ) : null} {loading ? ( ) : null} @@ -379,6 +389,7 @@ GUIComponent.propTypes = { onOpenRegistration: PropTypes.func, onRequestCloseBackdropLibrary: PropTypes.func, onRequestCloseCostumeLibrary: PropTypes.func, + onRequestCloseTelemetryModal: PropTypes.func, onSeeCommunity: PropTypes.func, onShare: PropTypes.func, onTabSelect: PropTypes.func, @@ -390,6 +401,12 @@ GUIComponent.propTypes = { soundsTabVisible: PropTypes.bool, stageSizeMode: PropTypes.oneOf(Object.keys(STAGE_SIZE_MODES)), targetIsStage: PropTypes.bool, + telemetryModalHandlers: PropTypes.shape({ + onCancel: PropTypes.func, + onOptIn: PropTypes.func.isRequired, + onOptOut: PropTypes.func.isRequired + }), + telemetryModalVisible: PropTypes.bool, tipsLibraryVisible: PropTypes.bool, vm: PropTypes.instanceOf(VM).isRequired }; diff --git a/src/components/telemetry-modal/telemetry-modal-header.png b/src/components/telemetry-modal/telemetry-modal-header.png new file mode 100644 index 0000000000000000000000000000000000000000..b297b108e92bb21a20ffa98425f36d0ad68565a1 GIT binary patch literal 14068 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 literal 0 HcmV?d00001 diff --git a/src/components/telemetry-modal/telemetry-modal.css b/src/components/telemetry-modal/telemetry-modal.css new file mode 100644 index 000000000..18b8d1ed8 --- /dev/null +++ b/src/components/telemetry-modal/telemetry-modal.css @@ -0,0 +1,82 @@ +@import "../../css/colors.css"; +@import "../../css/units.css"; +@import "../../css/typography.css"; +@import "../../css/z-index.css"; + +.modal-overlay { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + z-index: $z-index-modal; + background-color: $ui-modal-overlay; +} + +.modal-content { + margin: 100px auto; + outline: none; + border: .25rem solid $ui-white-transparent; + padding: 0; + border-radius: $space; + user-select: none; + width: 640px; + + color: $text-primary; + overflow: hidden; +} + +.illustration { + width: 100%; + height: 128px; + background-color: $motion-primary; + background-image: url('./telemetry-modal-header.png'); + background-size: cover; +} + +.body { + background: $ui-white; + padding: 1.5rem 2.25rem; + text-align: left; +} + +.privacy-policy-link { + color: $motion-primary; + text-decoration: none; +} + +/* Confirmation buttons at the bottom of the modal */ +.button-row { + margin: 1.5rem 0; + font-weight: bolder; + text-align: right; + display: flex; + justify-content: center; +} + +.button-row button { + border: 1px solid $motion-primary; + border-radius: 0.25rem; + padding: 0.5rem 1.5rem; + background: white; + font-weight: bold; + font-size: .875rem; + cursor: pointer; +} + +.button-row button.opt-in { + background: $motion-primary; + color: white; +} + +.button-row button.opt-out { + color: $motion-primary; +} + +[dir="ltr"] .button-row button + button { + margin-left: 0.5rem; +} + +[dir="rtl"] .button-row button + button { + margin-right: 0.5rem; +} diff --git a/src/components/telemetry-modal/telemetry-modal.jsx b/src/components/telemetry-modal/telemetry-modal.jsx new file mode 100644 index 000000000..b9e805aaf --- /dev/null +++ b/src/components/telemetry-modal/telemetry-modal.jsx @@ -0,0 +1,136 @@ +import bindAll from 'lodash.bindall'; +import PropTypes from 'prop-types'; +import React from 'react'; +import {defineMessages, injectIntl, intlShape, FormattedMessage} from 'react-intl'; +import ReactModal from 'react-modal'; + +import Box from '../box/box.jsx'; + +import styles from './telemetry-modal.css'; + +const messages = defineMessages({ + label: { + id: 'gui.telemetryOptIn.label', + defaultMessage: 'Report statistics to improve Scratch', + description: 'Scratch 3.0 telemetry modal label - for accessibility' + }, + bodyText1: { + defaultMessage: 'The Scratch Team is always looking to better understand how Scratch is used around the ' + + 'world. To help support this effort, you can allow Scratch to automatically send usage information to ' + + 'the Scratch Team.', + description: 'First paragraph of body text for telemetry opt-in modal', + id: 'gui.telemetryOptIn.body1' + }, + bodyText2: { + defaultMessage: 'The information we collect includes language selection, blocks usage, and some events like ' + + 'saving, loading, and uploading a project. We DO NOT collect any personal information. Please see our ' + + '{privacyPolicyLink} for more information.', + description: 'First paragraph of body text for telemetry opt-in modal', + id: 'gui.telemetryOptIn.body2' + }, + privacyPolicyLink: { + defaultMessage: 'Privacy Policy', + description: 'Link to the Scratch privacy policy', + id: 'gui.telemetryOptIn.privacyPolicyLink' + }, + noButton: { + defaultMessage: 'No, thanks', + description: 'Text for telemetry modal opt-out button', + id: 'gui.telemetryOptIn.buttonTextNo' + }, + noTooltip: { + defaultMessage: 'Disable telemetry', + description: 'Tooltip for telemetry modal opt-out button', + id: 'gui.telemetryOptIn.buttonTooltipNo' + }, + yesButton: { + defaultMessage: "Yes, I'd like to help improve Scratch", + description: 'Text for telemetry modal opt-in button', + id: 'gui.telemetryOptIn.buttonTextYes' + }, + yesTooltip: { + defaultMessage: 'Enable telemetry', + description: 'Tooltip for telemetry modal opt-in button', + id: 'gui.telemetryOptIn.buttonTooltipYes' + } +}); + +class TelemetryModal extends React.PureComponent { + constructor (props) { + super(props); + bindAll(this, [ + 'handleCancel', + 'handleOptIn', + 'handleOptOut' + ]); + } + handleCancel () { + this.props.onRequestClose(); + if (this.props.onCancel) { + this.props.onCancel(); + } + } + handleOptIn () { + this.props.onRequestClose(); + this.props.onOptIn(); + } + handleOptOut () { + this.props.onRequestClose(); + this.props.onOptOut(); + } + render () { + return ( +
+ + + +

+

+ + ) + }} + />

+ + + + +
+
+
); + } +} + +TelemetryModal.propTypes = { + intl: intlShape.isRequired, + isRtl: PropTypes.bool, + onCancel: PropTypes.func, + onOptIn: PropTypes.func.isRequired, + onOptOut: PropTypes.func.isRequired, + onRequestClose: PropTypes.func +}; + +export default injectIntl(TelemetryModal); diff --git a/src/containers/gui.jsx b/src/containers/gui.jsx index 025a73966..4011ba271 100644 --- a/src/containers/gui.jsx +++ b/src/containers/gui.jsx @@ -7,7 +7,6 @@ import VM from 'scratch-vm'; import {defineMessages, injectIntl, intlShape} from 'react-intl'; import ErrorBoundaryHOC from '../lib/error-boundary-hoc.jsx'; -import {openExtensionLibrary} from '../reducers/modals'; import { getIsError, getIsShowingProject @@ -22,7 +21,9 @@ import { import { closeCostumeLibrary, - closeBackdropLibrary + closeBackdropLibrary, + closeTelemetryModal, + openExtensionLibrary } from '../reducers/modals'; import FontLoaderHOC from '../lib/font-loader-hoc.jsx'; @@ -128,6 +129,7 @@ GUI.propTypes = { projectHost: PropTypes.string, projectId: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), projectTitle: PropTypes.string, + telemetryModalVisible: PropTypes.bool, vm: PropTypes.instanceOf(VM).isRequired }; @@ -157,11 +159,12 @@ const mapStateToProps = state => { loadingStateVisible: state.scratchGui.modals.loadingProject, previewInfoVisible: state.scratchGui.modals.previewInfo, projectId: state.scratchGui.projectState.projectId, + soundsTabVisible: state.scratchGui.editorTab.activeTabIndex === SOUNDS_TAB_INDEX, targetIsStage: ( state.scratchGui.targets.stage && state.scratchGui.targets.stage.id === state.scratchGui.targets.editingTarget ), - soundsTabVisible: state.scratchGui.editorTab.activeTabIndex === SOUNDS_TAB_INDEX, + telemetryModalVisible: state.scratchGui.modals.telemetryModal, tipsLibraryVisible: state.scratchGui.modals.tipsLibrary, vm: state.scratchGui.vm }; @@ -174,6 +177,7 @@ const mapDispatchToProps = dispatch => ({ onActivateSoundsTab: () => dispatch(activateTab(SOUNDS_TAB_INDEX)), onRequestCloseBackdropLibrary: () => dispatch(closeBackdropLibrary()), onRequestCloseCostumeLibrary: () => dispatch(closeCostumeLibrary()), + onRequestCloseTelemetryModal: () => dispatch(closeTelemetryModal()), onUpdateReduxProjectTitle: title => dispatch(setProjectTitle(title)) }); diff --git a/src/lib/app-state-hoc.jsx b/src/lib/app-state-hoc.jsx index 4156cd094..eacf2eab6 100644 --- a/src/lib/app-state-hoc.jsx +++ b/src/lib/app-state-hoc.jsx @@ -51,7 +51,8 @@ const AppStateHOC = function (WrappedComponent, localesOnly) { guiMiddleware, initFullScreen, initPlayer, - initPreviewInfo + initPreviewInfo, + initTelemetryModal } = guiRedux; const {ScratchPaintReducer} = require('scratch-paint'); @@ -63,6 +64,8 @@ const AppStateHOC = function (WrappedComponent, localesOnly) { if (props.isPlayerOnly) { initializedGui = initPlayer(initializedGui); } + } else if (props.showTelemetryModal) { + initializedGui = initTelemetryModal(initializedGui); } else if (props.showPreviewInfo) { initializedGui = initPreviewInfo(initializedGui); } @@ -98,6 +101,7 @@ const AppStateHOC = function (WrappedComponent, localesOnly) { isFullScreen, // eslint-disable-line no-unused-vars isPlayerOnly, // eslint-disable-line no-unused-vars showPreviewInfo, // eslint-disable-line no-unused-vars + showTelemetryModal, // eslint-disable-line no-unused-vars ...componentProps } = this.props; return ( diff --git a/src/playground/render-gui.jsx b/src/playground/render-gui.jsx index 3ae35e4d3..ab67cd2c1 100644 --- a/src/playground/render-gui.jsx +++ b/src/playground/render-gui.jsx @@ -6,11 +6,24 @@ import AppStateHOC from '../lib/app-state-hoc.jsx'; import GUI from '../containers/gui.jsx'; import HashParserHOC from '../lib/hash-parser-hoc.jsx'; import TitledHOC from '../lib/titled-hoc.jsx'; +import log from '../lib/log.js'; const onClickLogo = () => { window.location = 'https://scratch.mit.edu'; }; +const handleTelemetryModalCancel = () => { + log('User canceled telemetry modal'); +}; + +const handleTelemetryModalOptIn = () => { + log('User opted into telemetry'); +}; + +const handleTelemetryModalOptOut = () => { + log('User opted out of telemetry'); +}; + /* * Render the GUI playground. This is a separate function because importing anything * that instantiates the VM causes unsupported browsers to crash @@ -55,7 +68,13 @@ export default appTarget => { simulateScratchDesktop ? :