feat: [UEPR-44] Implemented custom journeys for onboarding
83
package-lock.json
generated
|
@ -11,6 +11,7 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bunyan": "1.8.15",
|
"bunyan": "1.8.15",
|
||||||
"clipboard-copy": "2.0.1",
|
"clipboard-copy": "2.0.1",
|
||||||
|
"driver.js": "^1.3.1",
|
||||||
"express": "4.19.2",
|
"express": "4.19.2",
|
||||||
"express-http-proxy": "1.6.3",
|
"express-http-proxy": "1.6.3",
|
||||||
"lodash.defaults": "4.2.0",
|
"lodash.defaults": "4.2.0",
|
||||||
|
@ -91,7 +92,7 @@
|
||||||
"postcss-loader": "4.3.0",
|
"postcss-loader": "4.3.0",
|
||||||
"postcss-simple-vars": "5.0.2",
|
"postcss-simple-vars": "5.0.2",
|
||||||
"prop-types": "15.8.1",
|
"prop-types": "15.8.1",
|
||||||
"query-string": "5.1.1",
|
"query-string": "9.1.0",
|
||||||
"react": "16.14.0",
|
"react": "16.14.0",
|
||||||
"react-dom": "16.14.0",
|
"react-dom": "16.14.0",
|
||||||
"react-intl": "5.25.1",
|
"react-intl": "5.25.1",
|
||||||
|
@ -8393,13 +8394,12 @@
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/decode-uri-component": {
|
"node_modules/decode-uri-component": {
|
||||||
"version": "0.2.2",
|
"version": "0.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.4.1.tgz",
|
||||||
"integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==",
|
"integrity": "sha512-+8VxcR21HhTy8nOt6jf20w0c9CADrw1O8d+VZ/YzzCt4bJ3uBjw+D1q2osAB8RnpwwaeYBxy0HyKQxD5JBMuuQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10"
|
"node": ">=14.16"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/decompress-response": {
|
"node_modules/decompress-response": {
|
||||||
|
@ -8797,6 +8797,11 @@
|
||||||
"normalize-svg-path": "~0.1.0"
|
"normalize-svg-path": "~0.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/driver.js": {
|
||||||
|
"version": "1.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/driver.js/-/driver.js-1.3.1.tgz",
|
||||||
|
"integrity": "sha512-MvUdXbqSgEsgS/H9KyWb5Rxy0aE6BhOVT4cssi2x2XjmXea6qQfgdx32XKVLLSqTaIw7q/uxU5Xl3NV7+cN6FQ=="
|
||||||
|
},
|
||||||
"node_modules/dtrace-provider": {
|
"node_modules/dtrace-provider": {
|
||||||
"version": "0.8.8",
|
"version": "0.8.8",
|
||||||
"resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.8.8.tgz",
|
"resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.8.8.tgz",
|
||||||
|
@ -10566,6 +10571,18 @@
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/filter-obj": {
|
||||||
|
"version": "5.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-5.1.0.tgz",
|
||||||
|
"integrity": "sha512-qWeTREPoT7I0bifpPUXtxkZJ1XJzxWtfoWWkdVGqa+eCr3SHW/Ocp89o8vLvbUuQnadybJpjOKu4V+RwO6sGng==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.16"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/filtered-vector": {
|
"node_modules/filtered-vector": {
|
||||||
"version": "1.2.5",
|
"version": "1.2.5",
|
||||||
"resolved": "https://registry.npmjs.org/filtered-vector/-/filtered-vector-1.2.5.tgz",
|
"resolved": "https://registry.npmjs.org/filtered-vector/-/filtered-vector-1.2.5.tgz",
|
||||||
|
@ -20282,18 +20299,20 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/query-string": {
|
"node_modules/query-string": {
|
||||||
"version": "5.1.1",
|
"version": "9.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/query-string/-/query-string-9.1.0.tgz",
|
||||||
"integrity": "sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==",
|
"integrity": "sha512-t6dqMECpCkqfyv2FfwVS1xcB6lgXW/0XZSaKdsCNGYkqMO76AFiJEg4vINzoDKcZa6MS7JX+OHIjwh06K5vczw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"decode-uri-component": "^0.2.0",
|
"decode-uri-component": "^0.4.1",
|
||||||
"object-assign": "^4.1.0",
|
"filter-obj": "^5.1.0",
|
||||||
"strict-uri-encode": "^1.0.0"
|
"split-on-first": "^3.0.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=18"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/querystringify": {
|
"node_modules/querystringify": {
|
||||||
|
@ -22647,6 +22666,15 @@
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/scratch-gui/node_modules/decode-uri-component": {
|
||||||
|
"version": "0.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz",
|
||||||
|
"integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/scratch-gui/node_modules/immutable": {
|
"node_modules/scratch-gui/node_modules/immutable": {
|
||||||
"version": "3.8.2",
|
"version": "3.8.2",
|
||||||
"resolved": "https://registry.npmjs.org/immutable/-/immutable-3.8.2.tgz",
|
"resolved": "https://registry.npmjs.org/immutable/-/immutable-3.8.2.tgz",
|
||||||
|
@ -22734,6 +22762,20 @@
|
||||||
"url": "https://opencollective.com/postcss/"
|
"url": "https://opencollective.com/postcss/"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/scratch-gui/node_modules/query-string": {
|
||||||
|
"version": "5.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz",
|
||||||
|
"integrity": "sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"decode-uri-component": "^0.2.0",
|
||||||
|
"object-assign": "^4.1.0",
|
||||||
|
"strict-uri-encode": "^1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/scratch-gui/node_modules/react-intl": {
|
"node_modules/scratch-gui/node_modules/react-intl": {
|
||||||
"version": "2.9.0",
|
"version": "2.9.0",
|
||||||
"resolved": "https://registry.npmjs.org/react-intl/-/react-intl-2.9.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-intl/-/react-intl-2.9.0.tgz",
|
||||||
|
@ -23878,6 +23920,18 @@
|
||||||
"node": "*"
|
"node": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/split-on-first": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-qxQJTx2ryR0Dw0ITYyekNQWpz6f8dGd7vffGNflQQ3Iqj9NJ6qiZ7ELpZsJ/QBhIVAiDfXdag3+Gp8RvWa62AA==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/split-polygon": {
|
"node_modules/split-polygon": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/split-polygon/-/split-polygon-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/split-polygon/-/split-polygon-1.0.0.tgz",
|
||||||
|
@ -24260,7 +24314,6 @@
|
||||||
"resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz",
|
||||||
"integrity": "sha512-R3f198pcvnB+5IpnBlRkphuE9n46WyVl8I39W/ZUTZLz4nqSP/oLYUrcnJrw462Ds8he4YKMov2efsTIw1BDGQ==",
|
"integrity": "sha512-R3f198pcvnB+5IpnBlRkphuE9n46WyVl8I39W/ZUTZLz4nqSP/oLYUrcnJrw462Ds8he4YKMov2efsTIw1BDGQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,6 +46,7 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bunyan": "1.8.15",
|
"bunyan": "1.8.15",
|
||||||
"clipboard-copy": "2.0.1",
|
"clipboard-copy": "2.0.1",
|
||||||
|
"driver.js": "^1.3.1",
|
||||||
"express": "4.19.2",
|
"express": "4.19.2",
|
||||||
"express-http-proxy": "1.6.3",
|
"express-http-proxy": "1.6.3",
|
||||||
"lodash.defaults": "4.2.0",
|
"lodash.defaults": "4.2.0",
|
||||||
|
@ -115,18 +116,18 @@
|
||||||
"lodash.merge": "4.6.2",
|
"lodash.merge": "4.6.2",
|
||||||
"lodash.mergewith": "4.6.2",
|
"lodash.mergewith": "4.6.2",
|
||||||
"lodash.omit": "3.1.0",
|
"lodash.omit": "3.1.0",
|
||||||
"lodash.uniqby": "4.7.0",
|
|
||||||
"lodash.sample": "4.2.1",
|
"lodash.sample": "4.2.1",
|
||||||
|
"lodash.uniqby": "4.7.0",
|
||||||
"mini-css-extract-plugin": "1.6.2",
|
"mini-css-extract-plugin": "1.6.2",
|
||||||
"minilog": "2.1.0",
|
"minilog": "2.1.0",
|
||||||
"pako": "0.2.8",
|
"pako": "0.2.8",
|
||||||
"plotly.js": "1.47.4",
|
"plotly.js": "1.47.4",
|
||||||
"postcss-import": "12.0.1",
|
|
||||||
"postcss": "8.4.40",
|
"postcss": "8.4.40",
|
||||||
|
"postcss-import": "12.0.1",
|
||||||
"postcss-loader": "4.3.0",
|
"postcss-loader": "4.3.0",
|
||||||
"postcss-simple-vars": "5.0.2",
|
"postcss-simple-vars": "5.0.2",
|
||||||
"prop-types": "15.8.1",
|
"prop-types": "15.8.1",
|
||||||
"query-string": "5.1.1",
|
"query-string": "9.1.0",
|
||||||
"react": "16.14.0",
|
"react": "16.14.0",
|
||||||
"react-dom": "16.14.0",
|
"react-dom": "16.14.0",
|
||||||
"react-intl": "5.25.1",
|
"react-intl": "5.25.1",
|
||||||
|
|
67
src/components/journeys/driver-journey/driver-journey.jsx
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
const React = require('react');
|
||||||
|
const {useState, useEffect, isValidElement} = require('react');
|
||||||
|
const {createPortal} = require('react-dom');
|
||||||
|
const PropTypes = require('prop-types');
|
||||||
|
require('driver.js/dist/driver.css');
|
||||||
|
|
||||||
|
const DriverJourney = ({configProps, driverObj}) => {
|
||||||
|
const [renderState, setRenderState] = useState();
|
||||||
|
|
||||||
|
const {steps, ...restConfig} = configProps;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const driverSteps = steps.map((step, index) => {
|
||||||
|
const {sectionComponents = {}, ...popoverProps} = step.popover;
|
||||||
|
return {
|
||||||
|
...step,
|
||||||
|
popover: {
|
||||||
|
...popoverProps,
|
||||||
|
onPopoverRender: popover => {
|
||||||
|
const portalData = [];
|
||||||
|
for (const [section, component] of Object.entries(
|
||||||
|
sectionComponents
|
||||||
|
)) {
|
||||||
|
if (isValidElement(component)) {
|
||||||
|
popover[section].style.display = 'block';
|
||||||
|
popover[section].innerHTML = '';
|
||||||
|
portalData.push({
|
||||||
|
parentElement: popover[section],
|
||||||
|
childElement: component
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setRenderState({components: portalData, stepIndex: index});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
driverObj.setConfig({...restConfig, steps: driverSteps});
|
||||||
|
|
||||||
|
driverObj.drive();
|
||||||
|
}, [driverObj, steps]);
|
||||||
|
|
||||||
|
if (!renderState) return null;
|
||||||
|
if (!steps[renderState.stepIndex]) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{renderState.components.map(obj =>
|
||||||
|
createPortal(obj.childElement, obj.parentElement)
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
DriverJourney.propTypes = {
|
||||||
|
configProps: PropTypes.shape({
|
||||||
|
steps: PropTypes.arrayOf(PropTypes.object)
|
||||||
|
}),
|
||||||
|
driverObj: PropTypes.shape({
|
||||||
|
setConfig: PropTypes.func,
|
||||||
|
drive: PropTypes.func
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = DriverJourney;
|
469
src/components/journeys/editor-journey/editor-journey.jsx
Normal file
|
@ -0,0 +1,469 @@
|
||||||
|
const React = require('react');
|
||||||
|
const {driver} = require('driver.js');
|
||||||
|
const FlexRow = require('../../flex-row/flex-row.jsx');
|
||||||
|
const Button = require('../../forms/button.jsx');
|
||||||
|
const DriverJourney = require('../driver-journey/driver-journey.jsx');
|
||||||
|
const {defineMessages, useIntl} = require('react-intl');
|
||||||
|
const {useMemo, useState} = require('react');
|
||||||
|
const PropTypes = require('prop-types');
|
||||||
|
|
||||||
|
require('./editor-journey.scss');
|
||||||
|
|
||||||
|
const messages = defineMessages({
|
||||||
|
createTitle: {
|
||||||
|
id: 'gui.journey.controls.create',
|
||||||
|
defaultMessage: 'Create',
|
||||||
|
description: 'Create modal title'
|
||||||
|
},
|
||||||
|
projectGenreTitle: {
|
||||||
|
id: 'gui.journey.controls.choose.projectGenre',
|
||||||
|
defaultMessage: 'What do you whant to create?',
|
||||||
|
description: 'Choose project genre modal title'
|
||||||
|
},
|
||||||
|
typeTitle: {
|
||||||
|
id: 'gui.journey.controls.choose.type',
|
||||||
|
defaultMessage: 'Which type?',
|
||||||
|
description: 'Choose project type modal title'
|
||||||
|
},
|
||||||
|
startTitle: {
|
||||||
|
id: 'gui.journey.controls.choose.start',
|
||||||
|
defaultMessage: 'How do you want to start?',
|
||||||
|
description: 'Choose way to start modal title'
|
||||||
|
},
|
||||||
|
gameTitle: {
|
||||||
|
id: 'gui.journey.controls.game',
|
||||||
|
defaultMessage: 'Game',
|
||||||
|
description: 'Game button title'
|
||||||
|
},
|
||||||
|
animiationTitle: {
|
||||||
|
id: 'gui.journey.controls.animation',
|
||||||
|
defaultMessage: 'Animation',
|
||||||
|
description: 'Animation button title'
|
||||||
|
},
|
||||||
|
musicTitle: {
|
||||||
|
id: 'gui.journey.controls.music',
|
||||||
|
defaultMessage: 'Music',
|
||||||
|
description: 'Music button title'
|
||||||
|
},
|
||||||
|
clickerGameTitle: {
|
||||||
|
id: 'gui.journey.controls.game.clicker',
|
||||||
|
defaultMessage: 'Clicker Game',
|
||||||
|
description: 'Clicker game button title'
|
||||||
|
},
|
||||||
|
pongGameTitle: {
|
||||||
|
id: 'gui.journey.controls.game.pong',
|
||||||
|
defaultMessage: 'Pong Game',
|
||||||
|
description: 'Pong game button title'
|
||||||
|
},
|
||||||
|
characterAnimationTitle: {
|
||||||
|
id: 'gui.journey.controls.animation.character',
|
||||||
|
defaultMessage: 'Animate a character',
|
||||||
|
description: 'Animate a character button title'
|
||||||
|
},
|
||||||
|
flyAnimationTitle: {
|
||||||
|
id: 'gui.journey.controls.animation.fly',
|
||||||
|
defaultMessage: 'Make it fly',
|
||||||
|
description: 'Make it fly animation button title'
|
||||||
|
},
|
||||||
|
recordSoundTitle: {
|
||||||
|
id: 'gui.journey.controls.music.record',
|
||||||
|
defaultMessage: 'Record a sound',
|
||||||
|
description: 'Record a sound button title'
|
||||||
|
},
|
||||||
|
makeMusicTitle: {
|
||||||
|
id: 'gui.journey.controls.music.make',
|
||||||
|
defaultMessage: 'Make music',
|
||||||
|
description: 'Make music button title'
|
||||||
|
},
|
||||||
|
tutorialTitle: {
|
||||||
|
id: 'gui.journey.controls.tutorial',
|
||||||
|
defaultMessage: 'Tutorial',
|
||||||
|
description: 'Tutorial button title'
|
||||||
|
},
|
||||||
|
starterProjectTitle: {
|
||||||
|
id: 'gui.journey.controls.starterProject',
|
||||||
|
defaultMessage: 'Starter project',
|
||||||
|
description: 'Starter project button title'
|
||||||
|
},
|
||||||
|
onMyOwnTitle: {
|
||||||
|
id: 'gui.journey.controls.onMyOwn',
|
||||||
|
defaultMessage: 'On my own',
|
||||||
|
description: 'On my own button title'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const projects = {
|
||||||
|
clicker: '10128368',
|
||||||
|
pong: '10128515',
|
||||||
|
animateCharacter: '10128067',
|
||||||
|
makeItFly: '114019829',
|
||||||
|
recordSound: '1031325137',
|
||||||
|
makeMusic: '10012676'
|
||||||
|
};
|
||||||
|
|
||||||
|
const tutorialIds = {
|
||||||
|
clicker: {
|
||||||
|
id: 'Make-A-Game',
|
||||||
|
urlId: 'clicker-game'
|
||||||
|
},
|
||||||
|
pong: {
|
||||||
|
id: 'pong',
|
||||||
|
urlId: 'pong'
|
||||||
|
},
|
||||||
|
animateCharacter: {
|
||||||
|
id: 'Animate-A-Character',
|
||||||
|
urlId: 'animate-a-character'
|
||||||
|
},
|
||||||
|
makeItFly: {
|
||||||
|
id: 'make-it-fly',
|
||||||
|
urlId: 'make-it-fly'
|
||||||
|
},
|
||||||
|
recordSound: {
|
||||||
|
id: 'record-a-sound',
|
||||||
|
urlId: 'record-a-sound'
|
||||||
|
},
|
||||||
|
makeMusic: {
|
||||||
|
id: 'Make-Music',
|
||||||
|
urlId: 'music'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const redirectToProject = projectId => {
|
||||||
|
location.href = `/projects/${projectId}?showJourney=true`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const openTutorial = (onActivateDeck, tutorial, driverObj) => {
|
||||||
|
history.pushState({}, {}, `?tutorial=${tutorial.urlId}`);
|
||||||
|
onActivateDeck(tutorial.id);
|
||||||
|
driverObj.destroy();
|
||||||
|
};
|
||||||
|
|
||||||
|
const ownOptingPicked = (setIsOnOwnOptionPicked, driverObg) => {
|
||||||
|
setIsOnOwnOptionPicked(true);
|
||||||
|
driverObg.destroy();
|
||||||
|
};
|
||||||
|
|
||||||
|
const EditorJourneyDescription = ({title, descriptionData}) => (
|
||||||
|
<>
|
||||||
|
<div className="title">{title}</div>
|
||||||
|
<FlexRow className="description-wrapper">
|
||||||
|
{
|
||||||
|
descriptionData.map((prop, index) => (
|
||||||
|
<FlexRow
|
||||||
|
key={index}
|
||||||
|
className="journey-option"
|
||||||
|
>
|
||||||
|
<img src={prop.imgSrc} />
|
||||||
|
<Button
|
||||||
|
className={'large'}
|
||||||
|
onClick={prop.handleOnClick}
|
||||||
|
>{prop.text}</Button>
|
||||||
|
</FlexRow>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</FlexRow>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
const EditorJourney = ({onActivateDeck, setIsOnOwnOptionPicked}) => {
|
||||||
|
const [driverObj] = useState(() => (
|
||||||
|
driver()
|
||||||
|
));
|
||||||
|
const intl = useIntl();
|
||||||
|
|
||||||
|
const steps = useMemo(
|
||||||
|
() => [{
|
||||||
|
popover: {
|
||||||
|
title: intl.formatMessage(messages.createTitle),
|
||||||
|
showButtons: ['close'],
|
||||||
|
sectionComponents: {
|
||||||
|
description: <EditorJourneyDescription
|
||||||
|
title={intl.formatMessage(messages.projectGenreTitle)}
|
||||||
|
descriptionData={[
|
||||||
|
{
|
||||||
|
imgSrc: '/images/onboarding-journeys/Games-Icon.png',
|
||||||
|
text: intl.formatMessage(messages.gameTitle),
|
||||||
|
handleOnClick: () => driverObj.moveTo(1)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
imgSrc: '/images/onboarding-journeys/Animation-Icon.png',
|
||||||
|
text: intl.formatMessage(messages.animiationTitle),
|
||||||
|
handleOnClick: () => driverObj.moveTo(2)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
imgSrc: '/images/onboarding-journeys/Music-Icon.png',
|
||||||
|
text: intl.formatMessage(messages.musicTitle),
|
||||||
|
handleOnClick: () => driverObj.moveTo(3)
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
popover: {
|
||||||
|
title: intl.formatMessage(messages.createTitle),
|
||||||
|
showButtons: ['close'],
|
||||||
|
sectionComponents: {
|
||||||
|
description: <EditorJourneyDescription
|
||||||
|
title={intl.formatMessage(messages.typeTitle)}
|
||||||
|
descriptionData={[
|
||||||
|
{
|
||||||
|
imgSrc: '/images/onboarding-journeys/Clicker-Game.jpg',
|
||||||
|
text: intl.formatMessage(messages.clickerGameTitle),
|
||||||
|
handleOnClick: () => driverObj.moveTo(4)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
imgSrc: '/images/onboarding-journeys/Pong-Game.jpg',
|
||||||
|
text: intl.formatMessage(messages.pongGameTitle),
|
||||||
|
handleOnClick: () => driverObj.moveTo(5)
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
popover: {
|
||||||
|
title: intl.formatMessage(messages.createTitle),
|
||||||
|
showButtons: ['close'],
|
||||||
|
sectionComponents: {
|
||||||
|
description: <EditorJourneyDescription
|
||||||
|
title={intl.formatMessage(messages.typeTitle)}
|
||||||
|
descriptionData={[
|
||||||
|
{
|
||||||
|
imgSrc: '/images/onboarding-journeys/Character-Animation.jpg',
|
||||||
|
text: intl.formatMessage(messages.characterAnimationTitle),
|
||||||
|
handleOnClick: () => driverObj.moveTo(6)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
imgSrc: '/images/onboarding-journeys/Fly-Animation.jpg',
|
||||||
|
text: intl.formatMessage(messages.flyAnimationTitle),
|
||||||
|
handleOnClick: () => driverObj.moveTo(7)
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
popover: {
|
||||||
|
title: intl.formatMessage(messages.createTitle),
|
||||||
|
showButtons: ['close'],
|
||||||
|
sectionComponents: {
|
||||||
|
description: <EditorJourneyDescription
|
||||||
|
title={intl.formatMessage(messages.typeTitle)}
|
||||||
|
descriptionData={[
|
||||||
|
{
|
||||||
|
imgSrc: '/images/onboarding-journeys/Record-Music.jpg',
|
||||||
|
text: intl.formatMessage(messages.recordSoundTitle),
|
||||||
|
handleOnClick: () => driverObj.moveTo(8)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
imgSrc: '/images/onboarding-journeys/Make-Music.jpg',
|
||||||
|
text: intl.formatMessage(messages.makeMusicTitle),
|
||||||
|
handleOnClick: () => driverObj.moveTo(9)
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
popover: {
|
||||||
|
title: intl.formatMessage(messages.createTitle),
|
||||||
|
showButtons: ['close'],
|
||||||
|
sectionComponents: {
|
||||||
|
description: <EditorJourneyDescription
|
||||||
|
title={intl.formatMessage(messages.startTitle)}
|
||||||
|
descriptionData={[
|
||||||
|
{
|
||||||
|
imgSrc: '/images/onboarding-journeys/Tutorials-Icon.png',
|
||||||
|
text: intl.formatMessage(messages.tutorialTitle),
|
||||||
|
handleOnClick: () => openTutorial(onActivateDeck, tutorialIds.clicker, driverObj)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
imgSrc: '/images/onboarding-journeys/Starter-Projects-Icon.png',
|
||||||
|
text: intl.formatMessage(messages.starterProjectTitle),
|
||||||
|
handleOnClick: () => redirectToProject(projects.clicker)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
imgSrc: '/images/onboarding-journeys/On-Own-Icon.png',
|
||||||
|
text: intl.formatMessage(messages.onMyOwnTitle),
|
||||||
|
handleOnClick: () => ownOptingPicked(setIsOnOwnOptionPicked, driverObj)
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
popover: {
|
||||||
|
title: intl.formatMessage(messages.createTitle),
|
||||||
|
showButtons: ['close'],
|
||||||
|
sectionComponents: {
|
||||||
|
description: <EditorJourneyDescription
|
||||||
|
title={intl.formatMessage(messages.startTitle)}
|
||||||
|
descriptionData={[
|
||||||
|
{
|
||||||
|
imgSrc: '/images/onboarding-journeys/Tutorials-Icon.png',
|
||||||
|
text: intl.formatMessage(messages.tutorialTitle),
|
||||||
|
handleOnClick: () => openTutorial(onActivateDeck, tutorialIds.pong, driverObj)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
imgSrc: '/images/onboarding-journeys/Starter-Projects-Icon.png',
|
||||||
|
text: intl.formatMessage(messages.starterProjectTitle),
|
||||||
|
handleOnClick: () => redirectToProject(projects.pong)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
imgSrc: '/images/onboarding-journeys/On-Own-Icon.png',
|
||||||
|
text: intl.formatMessage(messages.onMyOwnTitle),
|
||||||
|
handleOnClick: () => ownOptingPicked(setIsOnOwnOptionPicked, driverObj)
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
popover: {
|
||||||
|
title: intl.formatMessage(messages.createTitle),
|
||||||
|
showButtons: ['close'],
|
||||||
|
sectionComponents: {
|
||||||
|
description: <EditorJourneyDescription
|
||||||
|
title={intl.formatMessage(messages.startTitle)}
|
||||||
|
descriptionData={[
|
||||||
|
{
|
||||||
|
imgSrc: '/images/onboarding-journeys/Tutorials-Icon.png',
|
||||||
|
text: intl.formatMessage(messages.tutorialTitle),
|
||||||
|
handleOnClick: () =>
|
||||||
|
openTutorial(onActivateDeck, tutorialIds.animateCharacter, driverObj)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
imgSrc: '/images/onboarding-journeys/Starter-Projects-Icon.png',
|
||||||
|
text: intl.formatMessage(messages.starterProjectTitle),
|
||||||
|
handleOnClick: () => redirectToProject(projects.animateCharacter)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
imgSrc: '/images/onboarding-journeys/On-Own-Icon.png',
|
||||||
|
text: intl.formatMessage(messages.onMyOwnTitle),
|
||||||
|
handleOnClick: () => ownOptingPicked(setIsOnOwnOptionPicked, driverObj)
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
popover: {
|
||||||
|
title: intl.formatMessage(messages.createTitle),
|
||||||
|
showButtons: ['close'],
|
||||||
|
sectionComponents: {
|
||||||
|
description: <EditorJourneyDescription
|
||||||
|
title={intl.formatMessage(messages.startTitle)}
|
||||||
|
descriptionData={[
|
||||||
|
{
|
||||||
|
imgSrc: '/images/onboarding-journeys/Tutorials-Icon.png',
|
||||||
|
text: intl.formatMessage(messages.tutorialTitle),
|
||||||
|
handleOnClick: () => openTutorial(onActivateDeck, tutorialIds.makeItFly, driverObj)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
imgSrc: '/images/onboarding-journeys/Starter-Projects-Icon.png',
|
||||||
|
text: intl.formatMessage(messages.starterProjectTitle),
|
||||||
|
handleOnClick: () => redirectToProject(projects.makeItFly)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
imgSrc: '/images/onboarding-journeys/On-Own-Icon.png',
|
||||||
|
text: intl.formatMessage(messages.onMyOwnTitle),
|
||||||
|
handleOnClick: () => ownOptingPicked(setIsOnOwnOptionPicked, driverObj)
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
popover: {
|
||||||
|
title: intl.formatMessage(messages.createTitle),
|
||||||
|
showButtons: ['close'],
|
||||||
|
sectionComponents: {
|
||||||
|
description: <EditorJourneyDescription
|
||||||
|
title={intl.formatMessage(messages.startTitle)}
|
||||||
|
descriptionData={[
|
||||||
|
{
|
||||||
|
imgSrc: '/images/onboarding-journeys/Tutorials-Icon.png',
|
||||||
|
text: intl.formatMessage(messages.tutorialTitle),
|
||||||
|
handleOnClick: () => openTutorial(onActivateDeck, tutorialIds.recordSound, driverObj)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
imgSrc: '/images/onboarding-journeys/Starter-Projects-Icon.png',
|
||||||
|
text: intl.formatMessage(messages.starterProjectTitle),
|
||||||
|
handleOnClick: () => redirectToProject(projects.recordSound)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
imgSrc: '/images/onboarding-journeys/On-Own-Icon.png',
|
||||||
|
text: intl.formatMessage(messages.onMyOwnTitle),
|
||||||
|
handleOnClick: () => ownOptingPicked(setIsOnOwnOptionPicked, driverObj)
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
popover: {
|
||||||
|
title: intl.formatMessage(messages.createTitle),
|
||||||
|
showButtons: ['close'],
|
||||||
|
sectionComponents: {
|
||||||
|
description: <EditorJourneyDescription
|
||||||
|
title={intl.formatMessage(messages.startTitle)}
|
||||||
|
descriptionData={[
|
||||||
|
{
|
||||||
|
imgSrc: '/images/onboarding-journeys/Tutorials-Icon.png',
|
||||||
|
text: intl.formatMessage(messages.tutorialTitle),
|
||||||
|
handleOnClick: () => openTutorial(onActivateDeck, tutorialIds.makeMusic, driverObj)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
imgSrc: '/images/onboarding-journeys/Starter-Projects-Icon.png',
|
||||||
|
text: intl.formatMessage(messages.starterProjectTitle),
|
||||||
|
handleOnClick: () => redirectToProject(projects.makeMusic)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
imgSrc: '/images/onboarding-journeys/On-Own-Icon.png',
|
||||||
|
text: intl.formatMessage(messages.onMyOwnTitle),
|
||||||
|
handleOnClick: () => ownOptingPicked(setIsOnOwnOptionPicked, driverObj)
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}], [onActivateDeck, setIsOnOwnOptionPicked]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DriverJourney
|
||||||
|
configProps={{
|
||||||
|
popoverClass: 'gui-journey',
|
||||||
|
overlayOpacity: 0,
|
||||||
|
steps: steps
|
||||||
|
}}
|
||||||
|
driverObj={driverObj}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
EditorJourneyDescription.propTypes = {
|
||||||
|
title: PropTypes.string,
|
||||||
|
descriptionData: PropTypes.arrayOf(PropTypes.shape({
|
||||||
|
imgSrc: PropTypes.string,
|
||||||
|
text: PropTypes.string,
|
||||||
|
handleOnClick: PropTypes.func
|
||||||
|
}))
|
||||||
|
};
|
||||||
|
|
||||||
|
EditorJourney.propTypes = {
|
||||||
|
onActivateDeck: PropTypes.func,
|
||||||
|
setIsOnOwnOptionPicked: PropTypes.func
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = EditorJourney;
|
65
src/components/journeys/editor-journey/editor-journey.scss
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
@import "../../../colors";
|
||||||
|
@import "../../../frameless";
|
||||||
|
|
||||||
|
.driver-popover.gui-journey {
|
||||||
|
font-family: "Helvetica Neue", "Helvetica", Arial, sans-serif;
|
||||||
|
max-width: unset;
|
||||||
|
padding: 0;
|
||||||
|
border-radius: 15px;
|
||||||
|
top: 50% !important;
|
||||||
|
left: 50% !important;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
|
||||||
|
.driver-popover-close-btn {
|
||||||
|
height: 2.5rem;
|
||||||
|
width: 2.5rem;
|
||||||
|
border-radius: 50%;
|
||||||
|
margin: 0.5rem;
|
||||||
|
font-size: 2rem;
|
||||||
|
font-weight: bold;
|
||||||
|
color: $type-white;
|
||||||
|
background-color: $ui-aqua-dark;
|
||||||
|
}
|
||||||
|
|
||||||
|
.driver-popover-title {
|
||||||
|
padding: 1rem 0;
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: 700;
|
||||||
|
text-align: center;
|
||||||
|
color: $type-white;
|
||||||
|
margin: 0;
|
||||||
|
background-color: $ui-aqua;
|
||||||
|
border-radius: 15px 15px 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.driver-popover-title[style*=block]+.driver-popover-description {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
padding: 1rem 0;
|
||||||
|
font-size: 1.125rem;
|
||||||
|
font-family: "Helvetica Neue", "Helvetica", Arial, sans-serif;
|
||||||
|
font-weight: 400;
|
||||||
|
text-align: center;
|
||||||
|
color: $type-gray;
|
||||||
|
background-color: $ui-light-primary;
|
||||||
|
}
|
||||||
|
|
||||||
|
.description-wrapper {
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-evenly;
|
||||||
|
gap: 2rem;
|
||||||
|
margin: 3rem 4rem;
|
||||||
|
|
||||||
|
.journey-option {
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 1rem;
|
||||||
|
|
||||||
|
img {
|
||||||
|
max-height: $cols2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
58
src/components/journeys/project-journey/project-journey.jsx
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
const React = require('react');
|
||||||
|
const {driver} = require('driver.js');
|
||||||
|
const DriverJourney = require('../driver-journey/driver-journey.jsx');
|
||||||
|
const {defineMessages, useIntl} = require('react-intl');
|
||||||
|
require('./project-journey.scss');
|
||||||
|
|
||||||
|
const messages = defineMessages({
|
||||||
|
playProject: {
|
||||||
|
id: 'project.journey.play',
|
||||||
|
defaultMessage: 'Click green flag to play',
|
||||||
|
description: 'Play project'
|
||||||
|
},
|
||||||
|
remixProject: {
|
||||||
|
id: 'project.journey.remix',
|
||||||
|
defaultMessage: 'Make your own version!',
|
||||||
|
description: 'Remix project'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const ProjectJourney = () => {
|
||||||
|
const [driverObj] = React.useState(() => (
|
||||||
|
driver()
|
||||||
|
));
|
||||||
|
|
||||||
|
const intl = useIntl();
|
||||||
|
|
||||||
|
const steps = [{
|
||||||
|
element: 'div[class^="stage_green-flag-overlay-wrapper"] > div',
|
||||||
|
popover: {
|
||||||
|
description: intl.formatMessage(messages.playProject)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
element: '.remix-button',
|
||||||
|
popover: {
|
||||||
|
description: intl.formatMessage(messages.remixProject)
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DriverJourney
|
||||||
|
configProps={{
|
||||||
|
popoverClass: 'project-journey',
|
||||||
|
showButtons: [
|
||||||
|
'next',
|
||||||
|
'previous'
|
||||||
|
],
|
||||||
|
nextBtnText: 'Next',
|
||||||
|
prevBtnText: 'Previous',
|
||||||
|
showProgress: false,
|
||||||
|
steps: steps
|
||||||
|
}}
|
||||||
|
driverObj={driverObj}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = ProjectJourney;
|
50
src/components/journeys/project-journey/project-journey.scss
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
@import "../../../colors";
|
||||||
|
|
||||||
|
.driver-popover.project-journey {
|
||||||
|
background-color: $ui-purple-dark;
|
||||||
|
|
||||||
|
.driver-popover-arrow-side-left.driver-popover-arrow {
|
||||||
|
border-left-color: $ui-purple-dark;;
|
||||||
|
}
|
||||||
|
|
||||||
|
.driver-popover-arrow-side-right.driver-popover-arrow {
|
||||||
|
border-right-color: $ui-purple-dark;;
|
||||||
|
}
|
||||||
|
|
||||||
|
.driver-popover-arrow-side-top.driver-popover-arrow {
|
||||||
|
border-top-color: $ui-purple-dark;;
|
||||||
|
}
|
||||||
|
|
||||||
|
.driver-popover-arrow-side-bottom.driver-popover-arrow {
|
||||||
|
border-bottom-color: $ui-purple-dark;;
|
||||||
|
}
|
||||||
|
|
||||||
|
.driver-popover-description {
|
||||||
|
color: $ui-white;
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: 700;
|
||||||
|
font-family: "Helvetica Neue", "Helvetica", Arial, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.driver-popover-navigation-btns {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-evenly;
|
||||||
|
|
||||||
|
.driver-popover-btn-disabled {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
display: inline-block;
|
||||||
|
border: 0;
|
||||||
|
border-radius: 2rem;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 0.75rem 1rem;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
font-weight: bold;
|
||||||
|
|
||||||
|
background-color: $ui-white;
|
||||||
|
color: $ui-purple-dark;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
const React = require('react');
|
||||||
|
const {driver} = require('driver.js');
|
||||||
|
const DriverJourney = require('../driver-journey/driver-journey.jsx');
|
||||||
|
const {defineMessages, useIntl} = require('react-intl');
|
||||||
|
require('./tutorials-highlight.scss');
|
||||||
|
|
||||||
|
const messages = defineMessages({
|
||||||
|
tutorialsHighlight: {
|
||||||
|
id: 'gui.highlight.tutorials',
|
||||||
|
defaultMessage: 'Click here for tutorials',
|
||||||
|
description: 'Tutorials highlight'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const TutorialsHighlight = () => {
|
||||||
|
const [driverObj] = React.useState(() => (
|
||||||
|
driver()
|
||||||
|
));
|
||||||
|
|
||||||
|
const intl = useIntl();
|
||||||
|
|
||||||
|
const steps = [{
|
||||||
|
element: '.tutorials-button',
|
||||||
|
popover: {
|
||||||
|
showButtons: ['close'],
|
||||||
|
side: 'bottom',
|
||||||
|
description: intl.formatMessage(messages.tutorialsHighlight)
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DriverJourney
|
||||||
|
configProps={{
|
||||||
|
popoverClass: 'tutorials-highlight',
|
||||||
|
showProgress: false,
|
||||||
|
overlayOpacity: 0,
|
||||||
|
steps: steps
|
||||||
|
}}
|
||||||
|
driverObj={driverObj}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = TutorialsHighlight;
|
|
@ -0,0 +1,41 @@
|
||||||
|
@import "../../../colors";
|
||||||
|
|
||||||
|
.driver-popover.tutorials-highlight {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
background-color: $ui-purple-dark;
|
||||||
|
|
||||||
|
.driver-popover-close-btn {
|
||||||
|
height: 2.5rem;
|
||||||
|
width: 2.5rem;
|
||||||
|
border-radius: 50%;
|
||||||
|
margin: 0.5rem;
|
||||||
|
font-size: 2rem;
|
||||||
|
font-weight: bold;
|
||||||
|
color: $type-white;
|
||||||
|
background-color: $ui-purple-dark;
|
||||||
|
}
|
||||||
|
|
||||||
|
.driver-popover-arrow-side-left.driver-popover-arrow {
|
||||||
|
border-left-color: $ui-purple-dark;;
|
||||||
|
}
|
||||||
|
|
||||||
|
.driver-popover-arrow-side-right.driver-popover-arrow {
|
||||||
|
border-right-color: $ui-purple-dark;;
|
||||||
|
}
|
||||||
|
|
||||||
|
.driver-popover-arrow-side-top.driver-popover-arrow {
|
||||||
|
border-top-color: $ui-purple-dark;;
|
||||||
|
}
|
||||||
|
|
||||||
|
.driver-popover-arrow-side-bottom.driver-popover-arrow {
|
||||||
|
border-bottom-color: $ui-purple-dark;;
|
||||||
|
}
|
||||||
|
|
||||||
|
.driver-popover-description {
|
||||||
|
color: $ui-white;
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: 700;
|
||||||
|
font-family: "Helvetica Neue", "Helvetica", Arial, sans-serif;
|
||||||
|
}
|
||||||
|
}
|
22
src/lib/use-previous.js
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
import {useState} from 'react';
|
||||||
|
|
||||||
|
export const usePrevious = (
|
||||||
|
value,
|
||||||
|
comparator = (prev, current) => prev === current
|
||||||
|
) => {
|
||||||
|
const [state, setState] = useState({
|
||||||
|
value: value,
|
||||||
|
prev: null
|
||||||
|
});
|
||||||
|
|
||||||
|
const current = state.value;
|
||||||
|
|
||||||
|
if (!comparator(current, value)) {
|
||||||
|
setState({
|
||||||
|
value: value,
|
||||||
|
prev: current
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return state.prev;
|
||||||
|
};
|
|
@ -35,12 +35,14 @@ const FormsyProjectUpdater = require('./formsy-project-updater.jsx');
|
||||||
const EmailConfirmationModal = require('../../components/modal/email-confirmation/modal.jsx');
|
const EmailConfirmationModal = require('../../components/modal/email-confirmation/modal.jsx');
|
||||||
const EmailConfirmationBanner = require('../../components/dropdown-banner/email-confirmation/banner.jsx');
|
const EmailConfirmationBanner = require('../../components/dropdown-banner/email-confirmation/banner.jsx');
|
||||||
const {onCommented} = require('../../lib/user-guiding.js');
|
const {onCommented} = require('../../lib/user-guiding.js');
|
||||||
|
const queryString = require('query-string').default;
|
||||||
|
|
||||||
const projectShape = require('./projectshape.jsx').projectShape;
|
const projectShape = require('./projectshape.jsx').projectShape;
|
||||||
require('./preview.scss');
|
require('./preview.scss');
|
||||||
|
|
||||||
const frameless = require('../../lib/frameless');
|
const frameless = require('../../lib/frameless');
|
||||||
const {useState, useCallback} = require('react');
|
const {useState, useCallback} = require('react');
|
||||||
|
const ProjectJourney = require('../../components/journeys/project-journey/project-journey.jsx');
|
||||||
|
|
||||||
// disable enter key submission on formsy input fields; otherwise formsy thinks
|
// disable enter key submission on formsy input fields; otherwise formsy thinks
|
||||||
// we meant to trigger the "See inside" button. Instead, treat these keypresses
|
// we meant to trigger the "See inside" button. Instead, treat these keypresses
|
||||||
|
@ -255,6 +257,11 @@ const PreviewPresentation = ({
|
||||||
)}
|
)}
|
||||||
{ projectInfo && projectInfo.author && projectInfo.author.id && (
|
{ projectInfo && projectInfo.author && projectInfo.author.id && (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
|
{
|
||||||
|
isProjectLoaded &&
|
||||||
|
queryString.parse(location.search, {parseBooleans: true}).showJourney &&
|
||||||
|
<ProjectJourney />
|
||||||
|
}
|
||||||
{showEmailConfirmationBanner && <EmailConfirmationBanner
|
{showEmailConfirmationBanner && <EmailConfirmationBanner
|
||||||
/* eslint-disable react/jsx-no-bind */
|
/* eslint-disable react/jsx-no-bind */
|
||||||
onRequestDismiss={() => onBannerDismiss('confirmed_email')}
|
onRequestDismiss={() => onBannerDismiss('confirmed_email')}
|
||||||
|
|
|
@ -8,7 +8,7 @@ const PropTypes = require('prop-types');
|
||||||
const connect = require('react-redux').connect;
|
const connect = require('react-redux').connect;
|
||||||
const injectIntl = require('react-intl').injectIntl;
|
const injectIntl = require('react-intl').injectIntl;
|
||||||
const parser = require('scratch-parser');
|
const parser = require('scratch-parser');
|
||||||
const queryString = require('query-string');
|
const queryString = require('query-string').default;
|
||||||
|
|
||||||
const api = require('../../lib/api');
|
const api = require('../../lib/api');
|
||||||
const Page = require('../../components/page/www/page.jsx');
|
const Page = require('../../components/page/www/page.jsx');
|
||||||
|
@ -26,8 +26,7 @@ const CanceledDeletionModal = require('../../components/login/canceled-deletion-
|
||||||
const NotAvailable = require('../../components/not-available/not-available.jsx');
|
const NotAvailable = require('../../components/not-available/not-available.jsx');
|
||||||
const Meta = require('./meta.jsx');
|
const Meta = require('./meta.jsx');
|
||||||
const {
|
const {
|
||||||
onProjectShared,
|
onProjectShared
|
||||||
onProjectLoaded
|
|
||||||
} = require('../../lib/user-guiding.js');
|
} = require('../../lib/user-guiding.js');
|
||||||
|
|
||||||
const sessionActions = require('../../redux/session.js');
|
const sessionActions = require('../../redux/session.js');
|
||||||
|
@ -44,16 +43,41 @@ const IntlGUI = injectIntl(GUI.default);
|
||||||
const localStorageAvailable = 'localStorage' in window && window.localStorage !== null;
|
const localStorageAvailable = 'localStorage' in window && window.localStorage !== null;
|
||||||
|
|
||||||
const xhr = require('xhr');
|
const xhr = require('xhr');
|
||||||
const {useEffect} = require('react');
|
const {useEffect, useState} = require('react');
|
||||||
|
const EditorJourney = require('../../components/journeys/editor-journey/editor-journey.jsx');
|
||||||
|
const {usePrevious} = require('react-use');
|
||||||
|
const TutorialsHighlight = require('../../components/journeys/tutorials-highlight/tutorials-highlight.jsx');
|
||||||
|
|
||||||
|
const IntlGUIWithProjectHandler = ({...props}) => {
|
||||||
|
const [showJourney, setShowJourney] = useState(false);
|
||||||
|
const [isOnOwnOptionPicked, setIsOnOwnOptionPicked] = useState(false);
|
||||||
|
const prevProjectId = usePrevious(props.projectId);
|
||||||
|
|
||||||
const IntlGUIWithProjectHandler = ({user, permissions, ...props}) => {
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (props.projectId && props.projectId !== '0') {
|
const isTutorialOpen = !!queryString.parse(location.search).tutorial;
|
||||||
onProjectLoaded(user.id, permissions);
|
|
||||||
}
|
|
||||||
}, [props.projectId, user.id, permissions]);
|
|
||||||
|
|
||||||
return <IntlGUI {...props} />;
|
if (
|
||||||
|
props.projectId &&
|
||||||
|
prevProjectId === '0' &&
|
||||||
|
props.projectId !== '0' &&
|
||||||
|
!isTutorialOpen
|
||||||
|
) {
|
||||||
|
setShowJourney(true);
|
||||||
|
}
|
||||||
|
}, [props.projectId, prevProjectId, location]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<IntlGUI {...props} />
|
||||||
|
{showJourney && (
|
||||||
|
<EditorJourney
|
||||||
|
onActivateDeck={props.onActivateDeck}
|
||||||
|
setIsOnOwnOptionPicked={setIsOnOwnOptionPicked}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{isOnOwnOptionPicked && <TutorialsHighlight />}
|
||||||
|
</>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
IntlGUIWithProjectHandler.propTypes = {
|
IntlGUIWithProjectHandler.propTypes = {
|
||||||
|
@ -691,6 +715,7 @@ class Preview extends React.Component {
|
||||||
const parts = window.location.pathname.toLowerCase()
|
const parts = window.location.pathname.toLowerCase()
|
||||||
.split('/')
|
.split('/')
|
||||||
.filter(Boolean);
|
.filter(Boolean);
|
||||||
|
const queryParams = location.search;
|
||||||
let newUrl;
|
let newUrl;
|
||||||
if (projectId === '0') {
|
if (projectId === '0') {
|
||||||
newUrl = `/${parts[0]}/editor`;
|
newUrl = `/${parts[0]}/editor`;
|
||||||
|
@ -702,7 +727,7 @@ class Preview extends React.Component {
|
||||||
history.pushState(
|
history.pushState(
|
||||||
{projectId: projectId},
|
{projectId: projectId},
|
||||||
{projectId: projectId},
|
{projectId: projectId},
|
||||||
newUrl
|
`${newUrl}${queryParams}`
|
||||||
);
|
);
|
||||||
if (callback) callback();
|
if (callback) callback();
|
||||||
});
|
});
|
||||||
|
@ -907,6 +932,7 @@ class Preview extends React.Component {
|
||||||
onUpdateProjectTitle={this.handleUpdateProjectTitle}
|
onUpdateProjectTitle={this.handleUpdateProjectTitle}
|
||||||
user={this.props.user}
|
user={this.props.user}
|
||||||
permissions={this.props.permissions}
|
permissions={this.props.permissions}
|
||||||
|
onActivateDeck={this.props.onActivateDeck}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{this.props.registrationOpen && (
|
{this.props.registrationOpen && (
|
||||||
|
@ -984,6 +1010,7 @@ Preview.propTypes = {
|
||||||
lovedLoaded: PropTypes.bool,
|
lovedLoaded: PropTypes.bool,
|
||||||
moreCommentsToLoad: PropTypes.bool,
|
moreCommentsToLoad: PropTypes.bool,
|
||||||
original: projectShape,
|
original: projectShape,
|
||||||
|
onActivateDeck: PropTypes.func,
|
||||||
parent: projectShape,
|
parent: projectShape,
|
||||||
permissions: PropTypes.object,
|
permissions: PropTypes.object,
|
||||||
playerMode: PropTypes.bool,
|
playerMode: PropTypes.bool,
|
||||||
|
@ -1237,6 +1264,9 @@ const mapDispatchToProps = dispatch => ({
|
||||||
},
|
},
|
||||||
setFullScreen: fullscreen => {
|
setFullScreen: fullscreen => {
|
||||||
dispatch(GUI.setFullScreen(fullscreen));
|
dispatch(GUI.setFullScreen(fullscreen));
|
||||||
|
},
|
||||||
|
onActivateDeck: id => {
|
||||||
|
dispatch(GUI.activateDeck(id));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
BIN
static/images/onboarding-journeys/Animation-Icon.png
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
static/images/onboarding-journeys/Character-Animation.jpg
Normal file
After Width: | Height: | Size: 109 KiB |
BIN
static/images/onboarding-journeys/Clicker-Game.jpg
Normal file
After Width: | Height: | Size: 91 KiB |
BIN
static/images/onboarding-journeys/Fly-Animation.jpg
Normal file
After Width: | Height: | Size: 39 KiB |
BIN
static/images/onboarding-journeys/Games-Icon.png
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
static/images/onboarding-journeys/Make-Music.jpg
Normal file
After Width: | Height: | Size: 124 KiB |
BIN
static/images/onboarding-journeys/Music-Icon.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
static/images/onboarding-journeys/Name-Art.jpg
Normal file
After Width: | Height: | Size: 134 KiB |
BIN
static/images/onboarding-journeys/On-Own-Icon.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
static/images/onboarding-journeys/Pong-Game.jpg
Normal file
After Width: | Height: | Size: 69 KiB |
BIN
static/images/onboarding-journeys/Record-Music.jpg
Normal file
After Width: | Height: | Size: 132 KiB |
BIN
static/images/onboarding-journeys/Starter-Projects-Icon.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
static/images/onboarding-journeys/Tutorials-Icon.png
Normal file
After Width: | Height: | Size: 4.9 KiB |
|
@ -171,6 +171,7 @@ module.exports = {
|
||||||
loader: 'css-loader',
|
loader: 'css-loader',
|
||||||
options: {
|
options: {
|
||||||
modules: {
|
modules: {
|
||||||
|
auto: true,
|
||||||
localIdentName: '[name]_[local]_[hash:base64:5]',
|
localIdentName: '[name]_[local]_[hash:base64:5]',
|
||||||
exportLocalsConvention: 'camelCase'
|
exportLocalsConvention: 'camelCase'
|
||||||
},
|
},
|
||||||
|
@ -271,11 +272,11 @@ module.exports = {
|
||||||
'process.env.ASSET_HOST': `"${process.env.ASSET_HOST || 'https://assets.scratch.mit.edu'}"`,
|
'process.env.ASSET_HOST': `"${process.env.ASSET_HOST || 'https://assets.scratch.mit.edu'}"`,
|
||||||
'process.env.BACKPACK_HOST': `"${process.env.BACKPACK_HOST || 'https://backpack.scratch.mit.edu'}"`,
|
'process.env.BACKPACK_HOST': `"${process.env.BACKPACK_HOST || 'https://backpack.scratch.mit.edu'}"`,
|
||||||
'process.env.CLOUDDATA_HOST': `"${process.env.CLOUDDATA_HOST || 'clouddata.scratch.mit.edu'}"`,
|
'process.env.CLOUDDATA_HOST': `"${process.env.CLOUDDATA_HOST || 'clouddata.scratch.mit.edu'}"`,
|
||||||
'process.env.PROJECT_HOST': `"${process.env.PROJECT_HOST || 'https://projects.scratch.mit.edu'}"`,
|
'process.env.PROJECT_HOST': `"${process.env.PROJECT_HOST || 'http://localhost:8444'}"`,
|
||||||
'process.env.STATIC_HOST': `"${process.env.STATIC_HOST || 'https://uploads.scratch.mit.edu'}"`,
|
'process.env.STATIC_HOST': `"${process.env.STATIC_HOST || 'https://uploads.scratch.mit.edu'}"`,
|
||||||
'process.env.SCRATCH_ENV': `"${process.env.SCRATCH_ENV || 'development'}"`,
|
'process.env.SCRATCH_ENV': `"${process.env.SCRATCH_ENV || 'development'}"`,
|
||||||
'process.env.THUMBNAIL_URI': `"${process.env.THUMBNAIL_URI || '/internalapi/project/thumbnail/{}/set/'}"`,
|
'process.env.THUMBNAIL_URI': `"${process.env.THUMBNAIL_URI || '/projects/{}/thumbnail'}"`,
|
||||||
'process.env.THUMBNAIL_HOST': `"${process.env.THUMBNAIL_HOST || ''}"`,
|
'process.env.THUMBNAIL_HOST': `"${process.env.THUMBNAIL_HOST || 'http://localhost:4001'}"`,
|
||||||
'process.env.DEBUG': Boolean(process.env.DEBUG),
|
'process.env.DEBUG': Boolean(process.env.DEBUG),
|
||||||
'process.env.GA_ID': `"${process.env.GA_ID || 'UA-000000-01'}"`,
|
'process.env.GA_ID': `"${process.env.GA_ID || 'UA-000000-01'}"`,
|
||||||
'process.env.GTM_ENV_AUTH': `"${process.env.GTM_ENV_AUTH || ''}"`,
|
'process.env.GTM_ENV_AUTH': `"${process.env.GTM_ENV_AUTH || ''}"`,
|
||||||
|
|