feat: [UEPR-44] Implemented custom journeys for onboarding
83
package-lock.json
generated
|
@ -11,6 +11,7 @@
|
|||
"dependencies": {
|
||||
"bunyan": "1.8.15",
|
||||
"clipboard-copy": "2.0.1",
|
||||
"driver.js": "^1.3.1",
|
||||
"express": "4.19.2",
|
||||
"express-http-proxy": "1.6.3",
|
||||
"lodash.defaults": "4.2.0",
|
||||
|
@ -91,7 +92,7 @@
|
|||
"postcss-loader": "4.3.0",
|
||||
"postcss-simple-vars": "5.0.2",
|
||||
"prop-types": "15.8.1",
|
||||
"query-string": "5.1.1",
|
||||
"query-string": "9.1.0",
|
||||
"react": "16.14.0",
|
||||
"react-dom": "16.14.0",
|
||||
"react-intl": "5.25.1",
|
||||
|
@ -8393,13 +8394,12 @@
|
|||
"license": "MIT"
|
||||
},
|
||||
"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==",
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.4.1.tgz",
|
||||
"integrity": "sha512-+8VxcR21HhTy8nOt6jf20w0c9CADrw1O8d+VZ/YzzCt4bJ3uBjw+D1q2osAB8RnpwwaeYBxy0HyKQxD5JBMuuQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10"
|
||||
"node": ">=14.16"
|
||||
}
|
||||
},
|
||||
"node_modules/decompress-response": {
|
||||
|
@ -8797,6 +8797,11 @@
|
|||
"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": {
|
||||
"version": "0.8.8",
|
||||
"resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.8.8.tgz",
|
||||
|
@ -10566,6 +10571,18 @@
|
|||
"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": {
|
||||
"version": "1.2.5",
|
||||
"resolved": "https://registry.npmjs.org/filtered-vector/-/filtered-vector-1.2.5.tgz",
|
||||
|
@ -20282,18 +20299,20 @@
|
|||
}
|
||||
},
|
||||
"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==",
|
||||
"version": "9.1.0",
|
||||
"resolved": "https://registry.npmjs.org/query-string/-/query-string-9.1.0.tgz",
|
||||
"integrity": "sha512-t6dqMECpCkqfyv2FfwVS1xcB6lgXW/0XZSaKdsCNGYkqMO76AFiJEg4vINzoDKcZa6MS7JX+OHIjwh06K5vczw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"decode-uri-component": "^0.2.0",
|
||||
"object-assign": "^4.1.0",
|
||||
"strict-uri-encode": "^1.0.0"
|
||||
"decode-uri-component": "^0.4.1",
|
||||
"filter-obj": "^5.1.0",
|
||||
"split-on-first": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/querystringify": {
|
||||
|
@ -22647,6 +22666,15 @@
|
|||
"hasInstallScript": true,
|
||||
"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": {
|
||||
"version": "3.8.2",
|
||||
"resolved": "https://registry.npmjs.org/immutable/-/immutable-3.8.2.tgz",
|
||||
|
@ -22734,6 +22762,20 @@
|
|||
"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": {
|
||||
"version": "2.9.0",
|
||||
"resolved": "https://registry.npmjs.org/react-intl/-/react-intl-2.9.0.tgz",
|
||||
|
@ -23878,6 +23920,18 @@
|
|||
"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": {
|
||||
"version": "1.0.0",
|
||||
"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",
|
||||
"integrity": "sha512-R3f198pcvnB+5IpnBlRkphuE9n46WyVl8I39W/ZUTZLz4nqSP/oLYUrcnJrw462Ds8he4YKMov2efsTIw1BDGQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
|
|
|
@ -46,6 +46,7 @@
|
|||
"dependencies": {
|
||||
"bunyan": "1.8.15",
|
||||
"clipboard-copy": "2.0.1",
|
||||
"driver.js": "^1.3.1",
|
||||
"express": "4.19.2",
|
||||
"express-http-proxy": "1.6.3",
|
||||
"lodash.defaults": "4.2.0",
|
||||
|
@ -115,18 +116,18 @@
|
|||
"lodash.merge": "4.6.2",
|
||||
"lodash.mergewith": "4.6.2",
|
||||
"lodash.omit": "3.1.0",
|
||||
"lodash.uniqby": "4.7.0",
|
||||
"lodash.sample": "4.2.1",
|
||||
"lodash.uniqby": "4.7.0",
|
||||
"mini-css-extract-plugin": "1.6.2",
|
||||
"minilog": "2.1.0",
|
||||
"pako": "0.2.8",
|
||||
"plotly.js": "1.47.4",
|
||||
"postcss-import": "12.0.1",
|
||||
"postcss": "8.4.40",
|
||||
"postcss-import": "12.0.1",
|
||||
"postcss-loader": "4.3.0",
|
||||
"postcss-simple-vars": "5.0.2",
|
||||
"prop-types": "15.8.1",
|
||||
"query-string": "5.1.1",
|
||||
"query-string": "9.1.0",
|
||||
"react": "16.14.0",
|
||||
"react-dom": "16.14.0",
|
||||
"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 EmailConfirmationBanner = require('../../components/dropdown-banner/email-confirmation/banner.jsx');
|
||||
const {onCommented} = require('../../lib/user-guiding.js');
|
||||
const queryString = require('query-string').default;
|
||||
|
||||
const projectShape = require('./projectshape.jsx').projectShape;
|
||||
require('./preview.scss');
|
||||
|
||||
const frameless = require('../../lib/frameless');
|
||||
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
|
||||
// we meant to trigger the "See inside" button. Instead, treat these keypresses
|
||||
|
@ -255,6 +257,11 @@ const PreviewPresentation = ({
|
|||
)}
|
||||
{ projectInfo && projectInfo.author && projectInfo.author.id && (
|
||||
<React.Fragment>
|
||||
{
|
||||
isProjectLoaded &&
|
||||
queryString.parse(location.search, {parseBooleans: true}).showJourney &&
|
||||
<ProjectJourney />
|
||||
}
|
||||
{showEmailConfirmationBanner && <EmailConfirmationBanner
|
||||
/* eslint-disable react/jsx-no-bind */
|
||||
onRequestDismiss={() => onBannerDismiss('confirmed_email')}
|
||||
|
|
|
@ -8,7 +8,7 @@ const PropTypes = require('prop-types');
|
|||
const connect = require('react-redux').connect;
|
||||
const injectIntl = require('react-intl').injectIntl;
|
||||
const parser = require('scratch-parser');
|
||||
const queryString = require('query-string');
|
||||
const queryString = require('query-string').default;
|
||||
|
||||
const api = require('../../lib/api');
|
||||
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 Meta = require('./meta.jsx');
|
||||
const {
|
||||
onProjectShared,
|
||||
onProjectLoaded
|
||||
onProjectShared
|
||||
} = require('../../lib/user-guiding.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 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(() => {
|
||||
if (props.projectId && props.projectId !== '0') {
|
||||
onProjectLoaded(user.id, permissions);
|
||||
}
|
||||
}, [props.projectId, user.id, permissions]);
|
||||
const isTutorialOpen = !!queryString.parse(location.search).tutorial;
|
||||
|
||||
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 = {
|
||||
|
@ -691,6 +715,7 @@ class Preview extends React.Component {
|
|||
const parts = window.location.pathname.toLowerCase()
|
||||
.split('/')
|
||||
.filter(Boolean);
|
||||
const queryParams = location.search;
|
||||
let newUrl;
|
||||
if (projectId === '0') {
|
||||
newUrl = `/${parts[0]}/editor`;
|
||||
|
@ -702,7 +727,7 @@ class Preview extends React.Component {
|
|||
history.pushState(
|
||||
{projectId: projectId},
|
||||
{projectId: projectId},
|
||||
newUrl
|
||||
`${newUrl}${queryParams}`
|
||||
);
|
||||
if (callback) callback();
|
||||
});
|
||||
|
@ -907,6 +932,7 @@ class Preview extends React.Component {
|
|||
onUpdateProjectTitle={this.handleUpdateProjectTitle}
|
||||
user={this.props.user}
|
||||
permissions={this.props.permissions}
|
||||
onActivateDeck={this.props.onActivateDeck}
|
||||
/>
|
||||
)}
|
||||
{this.props.registrationOpen && (
|
||||
|
@ -984,6 +1010,7 @@ Preview.propTypes = {
|
|||
lovedLoaded: PropTypes.bool,
|
||||
moreCommentsToLoad: PropTypes.bool,
|
||||
original: projectShape,
|
||||
onActivateDeck: PropTypes.func,
|
||||
parent: projectShape,
|
||||
permissions: PropTypes.object,
|
||||
playerMode: PropTypes.bool,
|
||||
|
@ -1237,6 +1264,9 @@ const mapDispatchToProps = dispatch => ({
|
|||
},
|
||||
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',
|
||||
options: {
|
||||
modules: {
|
||||
auto: true,
|
||||
localIdentName: '[name]_[local]_[hash:base64:5]',
|
||||
exportLocalsConvention: 'camelCase'
|
||||
},
|
||||
|
@ -271,11 +272,11 @@ module.exports = {
|
|||
'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.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.SCRATCH_ENV': `"${process.env.SCRATCH_ENV || 'development'}"`,
|
||||
'process.env.THUMBNAIL_URI': `"${process.env.THUMBNAIL_URI || '/internalapi/project/thumbnail/{}/set/'}"`,
|
||||
'process.env.THUMBNAIL_HOST': `"${process.env.THUMBNAIL_HOST || ''}"`,
|
||||
'process.env.THUMBNAIL_URI': `"${process.env.THUMBNAIL_URI || '/projects/{}/thumbnail'}"`,
|
||||
'process.env.THUMBNAIL_HOST': `"${process.env.THUMBNAIL_HOST || 'http://localhost:4001'}"`,
|
||||
'process.env.DEBUG': Boolean(process.env.DEBUG),
|
||||
'process.env.GA_ID': `"${process.env.GA_ID || 'UA-000000-01'}"`,
|
||||
'process.env.GTM_ENV_AUTH': `"${process.env.GTM_ENV_AUTH || ''}"`,
|
||||
|
|