Compare commits

..
Author SHA1 Message Date
Christopher Willis-Ford a83d9369dc Revert "Merge pull request #4735 from paulkaplan/revert-storage-update"
Go back to `scratch-storage` version 1.3.1 in the `scratch-desktop`
branch so that we have the performance improvements from
LLK/scratch-storage#83

If I understand correctly we reverted from 1.3.1 to 1.2.2 because of
problems affecting HTTP saves, which are not available in Scratch
Desktop.

This reverts commit b435ac3101, reversing
changes made to 0d7a05d37a.
2019-05-16 19:59:58 -07:00
Christopher Willis-Ford 012bb612ad Revert "Merge pull request #4736 from LLK/revert-4644-load-library-images-through-storage"
This brings "Load library images through storage" back into the
`scratch-desktop` branch since it's necessary here. We need to figure
out some performance details before trying to merge it into `develop`
though.
2019-05-16 19:50:38 -07:00
Christopher Willis-Ford b9e7bde676 Merge tag '0.1.0-prerelease.20190516172446' into scratch-desktop 2019-05-16 12:38:02 -07:00
chrisgarrity 4a26810956 Merge pull request #4825 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20190516171147
Update scratch-paint to the latest version 🚀
2019-05-16 13:14:51 -04:00
greenkeeper[bot] 92fd318aa8 chore(package): update scratch-paint to version 0.2.0-prerelease.20190516171147 2019-05-16 17:13:17 +00:00
chrisgarrity bc2555ddc4 Merge pull request #4824 from LLK/greenkeeper/scratch-l10n-3.3.20190516144548
Update scratch-l10n to the latest version 🚀
2019-05-16 10:56:28 -04:00
greenkeeper[bot] 8ee9794304 chore(package): update scratch-l10n to version 3.3.20190516144548 2019-05-16 14:48:58 +00:00
picklesrus 30388ffa4f Merge pull request #4819 from LLK/greenkeeper/scratch-l10n-3.3.20190515143901
Update scratch-l10n to the latest version 🚀
2019-05-15 15:33:14 -04:00
picklesrus 41e2909782 Merge pull request #4821 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190515153227
chore(package): update scratch-vm to version 0.2.0-prerelease.2019051…
2019-05-15 13:16:09 -04:00
greenkeeper[bot] 4048ed8c84 chore(package): update scratch-vm to version 0.2.0-prerelease.20190515153227
Closes #4804
2019-05-15 15:36:48 +00:00
greenkeeper[bot] 7a21b17d0c chore(package): update scratch-l10n to version 3.3.20190515143901 2019-05-15 14:41:54 +00:00
chrisgarrity 72bc6f3005 Merge pull request #4816 from chrisgarrity/add-rap-uz
Update dependencies for Rapa Nui and Uzbek
2019-05-15 08:05:33 -04:00
Chris Garrity a8a7e0c92f Update dependencies for Rapa Nui 2019-05-14 18:18:43 -04:00
Paul Kaplan 333b91ccfd Merge pull request #4802 from LLK/greenkeeper/scratch-l10n-3.2.20190508143856
Update scratch-l10n to the latest version 🚀
2019-05-08 14:27:43 -04:00
greenkeeper[bot] a3266fe641 chore(package): update scratch-l10n to version 3.2.20190508143856 2019-05-08 14:53:25 +00:00
Evelyn Eastmond 10449a21b5 Merge pull request #4801 from evhan55/bugfix/4799
Enable pointer events for workspace while tutorial is showing
2019-05-08 08:38:20 -04:00
Evelyn Eastmond 29f5d2a872 Fixing pointer events to not prevent clicks in workspace. 2019-05-07 15:43:35 -04:00
Evelyn Eastmond f8b3e4961c Merge pull request #4731 from evhan55/feature/4670
Add shrink/expand and new fencing feature to tutorial cards
2019-05-07 12:53:02 -04:00
Evelyn Eastmond cca701e954 Add more layout for RTL. 2019-05-07 11:52:27 -04:00
Evelyn Eastmond 89c0807cba Reset position and expanded state of cards on activation. 2019-05-07 11:42:40 -04:00
Evelyn Eastmond 544a4c745d Change drag offset var names. 2019-05-07 11:42:40 -04:00
Evelyn Eastmond f6e9a7dd23 Remove console log from reducer, fix variable and comments in component. 2019-05-07 11:42:40 -04:00
Evelyn Eastmond 69132b6f43 Add variables for calculation of custom dragging bounds. 2019-05-07 11:42:40 -04:00
Evelyn Eastmond aa88e2c6a6 Removing tester top/left positions. 2019-05-07 11:42:40 -04:00
Evelyn Eastmond b89734b0e9 Add preliminary fencing div/overlay, with Paul's help. 2019-05-07 11:42:40 -04:00
Evelyn Eastmond d5eb058d00 Fixing linting and an extra line. 2019-05-07 11:42:40 -04:00
Evelyn Eastmond fe308b6b58 Commenting out div overlay hack for now. 2019-05-07 11:42:40 -04:00
Evelyn Eastmond ab14942c80 Starting to play with Draggable for cards. 2019-05-07 11:42:40 -04:00
Evelyn Eastmond 908cb2e5c6 New close button and some margin for text. 2019-05-07 11:42:40 -04:00
Evelyn Eastmond 6935aa0820 Add formattedmessage to shrink/expand labels. 2019-05-07 11:42:40 -04:00
Evelyn Eastmond 4ec8ac16a8 Reword 'toggle' to 'shrinkExpand' throughout code. 2019-05-07 11:42:40 -04:00
Evelyn Eastmond bfe2877d0b Rename icon, add shrink expand icons to card header. 2019-05-07 11:42:40 -04:00
Evelyn Eastmond 9d50e9a165 Adding initial shrink/expand icons. 2019-05-07 11:42:40 -04:00
Evelyn Eastmond eb496cfe75 Give cards a min-width so shrink/expand doesn't change size. 2019-05-07 11:42:40 -04:00
Evelyn Eastmond dfd01da913 Using a hidden css style, cleaning up left/right button and card hiding. 2019-05-07 11:42:40 -04:00
Evelyn Eastmond c8bdb6ce09 Change remove and toggle buttons flow to column for stacking layout. 2019-05-07 11:42:40 -04:00
Evelyn Eastmond 3169c03e66 Reverse order of close button icon and label. 2019-05-07 11:42:40 -04:00
Evelyn Eastmond 26323cccf2 Hide bottom border on header in shrink mode. 2019-05-07 11:42:40 -04:00
Evelyn Eastmond 2bb9a61539 Fixing right-card and card-preview to use toggle correctly. 2019-05-07 11:42:40 -04:00
Evelyn Eastmond 4efd3fcbad Rename shrinkButton to toggleButton. 2019-05-07 11:42:40 -04:00
Evelyn Eastmond 170705440a Adding toggle state to Cards. Using toggle state to set the CSS state. 2019-05-07 11:42:40 -04:00
Evelyn Eastmond a52fdaa032 Completing propagation of toddleCards. 2019-05-07 11:42:40 -04:00
Evelyn Eastmond 6d972da227 Adding a stub toggleCards reducer. 2019-05-07 11:42:40 -04:00
Evelyn Eastmond 96bd677ef9 Starting to add an onToggleCards prop. 2019-05-07 11:42:40 -04:00
Evelyn Eastmond b77bc7a8b4 Adding a new div to hold the shrink button. 2019-05-07 11:42:40 -04:00
Paul Kaplan 7f06f55f83 Merge pull request #4798 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190506184649
Update scratch-vm to the latest version 🚀
2019-05-07 08:41:22 -04:00
greenkeeper[bot] 04381439c2 chore(package): update scratch-vm to version 0.2.0-prerelease.20190506184649 2019-05-06 18:49:45 +00:00
Paul Kaplan 7b658c60c7 Merge pull request #4786 from paulkaplan/update-eslint-react
Update eslint-plugin-react and fix newly discovered lint errors.
2019-05-06 10:33:59 -04:00
picklesrus 0c68103eb6 Merge pull request #4664 from mzgoddard/is-3d-supported
replace Blocks.utils.is3dSupported with constant value function
2019-05-02 11:53:52 -04:00
Michael "Z" Goddard 65c787d0da replace Blocks.utils.is3dSupported with constant value function 2019-05-01 17:31:39 -04:00
chrisgarrity 0b40daf936 Merge pull request #4793 from LLK/greenkeeper/scratch-l10n-3.2.20190501144004
chore(package): update scratch-l10n to version 3.2.20190501144004
2019-05-01 17:09:25 -04:00
chrisgarrity 75607ebbe3 Merge pull request #4792 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190430163103
chore(package): update scratch-vm to version 0.2.0-prerelease.2019043…
2019-05-01 17:07:50 -04:00
Paul Kaplan 58b178f679 Merge pull request #4775 from LLK/greenkeeper/chromedriver-74.0.0
chore(package): update chromedriver to version 74.0.0
2019-05-01 15:00:07 -04:00
Paul Kaplan ffeebb03bd Update eslint-plugin-react and fix new lint errors.
This includes a small bug where the target pane wouldn't automatically update if changes to the sprite occured while in fullscreen/player mode.

Closes #4221
2019-05-01 11:01:44 -04:00
greenkeeper[bot] 7c1f0d2739 chore(package): update scratch-l10n to version 3.2.20190501144004
Closes #4774
2019-05-01 14:42:15 +00:00
Eric Rosenbaum ac0c6a4305 Merge pull request #4785 from ericrosenbaum/feature/enable-boost
Enable the boost extension
2019-05-01 10:22:09 -04:00
Benjamin Wheeler ff776dc90c Merge pull request #4784 from benjiwheeler/fix-save-on-see-project-page
Fix auto-save when clicking See Project Page
2019-05-01 09:44:24 -04:00
Ben Wheeler 516baedad9 added tests, rewrote menu-bar-hoc to accomodate tests 2019-05-01 07:02:07 -04:00
Eric Rosenbaum 1c8ba1d1eb enable boost extension 2019-04-30 16:13:50 -04:00
Ben Wheeler 16024769c8 renamed confirm-replace-hoc to menu-bar-hoc; moved project changed checking there 2019-04-30 15:02:22 -04:00
Paul Kaplan ed9316266e Merge pull request #4782 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20190430175441
Update scratch-paint to the latest version 🚀
2019-04-30 14:35:22 -04:00
greenkeeper[bot] 103f55bd6e chore(package): update scratch-paint to version 0.2.0-prerelease.20190430175441 2019-04-30 17:56:07 +00:00
greenkeeper[bot] 009d9db74f chore(package): update scratch-vm to version 0.2.0-prerelease.20190430163103
Closes #4781
2019-04-30 16:33:45 +00:00
Paul Kaplan 416bc85455 Merge pull request #4717 from gunchleoc/add-troubleshooting-documentation
Add troubleshooting documentation
2019-04-29 13:15:59 -04:00
Benjamin Wheeler 88a5ab8052 Cleaned up wording and explanation around npm linking 2019-04-28 12:44:39 -04:00
Paul Kaplan 31454f7ca0 Seriously, update VM to a version that actually exists 2019-04-24 17:15:09 -04:00
Eric Rosenbaum bfd0712c83 update scratch-vm 2019-04-24 16:43:00 -04:00
Eric Rosenbaum 3c9d482a02 Merge pull request #4773 from LLK/ericrosenbaum-update-vm
Update scratch-vm
2019-04-24 13:25:02 -04:00
Eric Rosenbaum 5c3694ec19 Update scratch-vm 2019-04-24 13:24:48 -04:00
Eric Rosenbaum 997abfeb27 Merge pull request #4772 from LLK/greenkeeper/scratch-l10n-3.2.20190424151256
chore(package): update scratch-l10n to version 3.2.20190424151256
2019-04-24 12:56:25 -04:00
Eric Rosenbaum df77245f0d Merge branch 'develop' into greenkeeper/scratch-l10n-3.2.20190424151256 2019-04-24 12:55:55 -04:00
Eric Rosenbaum 5e2ea2ee64 Merge pull request #4771 from LLK/ericrosenbaum-update-blocks
update scratch-blocks version
2019-04-24 12:51:59 -04:00
Eric Rosenbaum 655af4ed31 update scratch-blocks version 2019-04-24 12:51:33 -04:00
chrisgarrity 9d385faa84 Merge pull request #4769 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1556058260
Update scratch-blocks to the latest version 🚀
2019-04-24 11:18:42 -04:00
greenkeeper[bot] 455808fe26 chore(package): update scratch-l10n to version 3.2.20190424151256
Closes #4770
2019-04-24 15:14:51 +00:00
greenkeeper[bot] c2dbdc6279 chore(package): update chromedriver to version 74.0.0
Closes #4077
2019-04-24 13:33:48 +00:00
greenkeeper[bot] 8faf8af5da chore(package): update scratch-blocks to version 0.1.0-prerelease.1556058260 2019-04-24 11:08:41 +00:00
Eric Rosenbaum cfaf1ee049 Merge pull request #4756 from ericrosenbaum/bugfix/go-space-direct
Fix Vernier branding (“Go Direct”)
2019-04-23 17:54:09 -04:00
Eric Rosenbaum 29d9bb0d41 Merge branch 'develop' into bugfix/go-space-direct 2019-04-23 17:25:06 -04:00
Paul Kaplan 9d185bd6c8 Merge pull request #4753 from paulkaplan/project-save-trigger
Allow GUI consumer to receive a function for triggering saves.
2019-04-23 08:32:36 -04:00
Paul Kaplan 7c26245c53 Merge pull request #4757 from paulkaplan/remove-feedback-button
Remove unneeded feedback button in menubar
2019-04-23 08:32:09 -04:00
Paul Kaplan 4dc3f73e74 Merge pull request #4763 from LLK/greenkeeper/scratch-render-0.1.0-prerelease.20190419202157
Update scratch-render to the latest version 🚀
2019-04-23 08:31:52 -04:00
greenkeeper[bot] 1a66060aa2 chore(package): update scratch-render to version 0.1.0-prerelease.20190419202157 2019-04-20 22:39:55 +00:00
Benjamin Wheeler cf9e165ccf Merge pull request #4676 from mzgoddard/menu-bar
Move projectChanged into HOC wrapping MenuBar, to avoid unnecessary renders
2019-04-19 17:29:10 -04:00
Eric Rosenbaum e877486daa Merge pull request #4761 from ericrosenbaum/feature/boost-assets
Add Boost extension assets
2019-04-19 17:24:55 -04:00
Paul Kaplan 1eb095e235 Merge pull request #4760 from LLK/greenkeeper/scratch-svg-renderer-0.2.0-prerelease.20190419183947
Update scratch-svg-renderer to the latest version 🚀
2019-04-19 15:17:43 -04:00
Eric Rosenbaum 905e938c57 Move the boost tile into the correct order 2019-04-19 14:55:11 -04:00
Eric Rosenbaum fe8b914f8d Add Boost extension assets 2019-04-19 14:45:54 -04:00
greenkeeper[bot] a48cc065c0 chore(package): update scratch-svg-renderer to version 0.2.0-prerelease.20190419183947 2019-04-19 18:45:35 +00:00
Paul Kaplan 6e50909e0f Remove feedback icon 2019-04-19 13:17:48 -04:00
Paul Kaplan 6480b262de Remove unused import 2019-04-19 13:17:26 -04:00
Paul Kaplan a0745e4f40 Allow GUI consumer to receive a function for triggering saves.
Use the same implementation as onSetProjectThumbnailer which does the same thing for getting a thumbnail externally.
2019-04-19 09:19:00 -04:00
Paul Kaplan 8eb9e37d67 Remove unneeded feedback button in menubar
It is no longer needed, as the playground is no longer the public face of scratch gui
2019-04-18 13:49:28 -04:00
Eric Rosenbaum 69506d9c1d Fix Vernier branding (“Go Direct”) 2019-04-18 11:59:33 -04:00
Benjamin Wheeler d37f1fcae1 Merge pull request #4755 from benjiwheeler/fix-upload-confirm
removed confirm prompt if you are editing someone else's project and then do file -> upload
2019-04-18 11:48:53 -04:00
Ben Wheeler dfb7b55145 removed confirm prompt if you are editing someone else's project and then do file -> upload 2019-04-18 11:34:11 -04:00
Karishma Chadha 6e335085d1 Merge pull request #4750 from LLK/greenkeeper/scratch-l10n-3.2.20190417195644
chore(package): update scratch-l10n to version 3.2.20190417195644
2019-04-17 17:51:40 -04:00
Karishma Chadha cde9660b12 Merge pull request #4751 from LLK/greenkeeper/react-intl-2.8.0
chore(package): update react-intl to version 2.8.0
2019-04-17 17:30:19 -04:00
Karishma Chadha abe782cdba Merge pull request #4752 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190417203423
chore(package): update scratch-vm to version 0.2.0-prerelease.2019041…
2019-04-17 17:08:23 -04:00
greenkeeper[bot] 96f3377733 chore(package): update scratch-vm to version 0.2.0-prerelease.20190417203423
Closes #4740
2019-04-17 20:37:00 +00:00
Karishma Chadha e43011233f Merge branch 'develop' into greenkeeper/scratch-l10n-3.2.20190417195644 2019-04-17 16:24:13 -04:00
Karishma Chadha 7c4d92ac08 Merge pull request #4743 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20190416185326
Update scratch-paint to the latest version 🚀
2019-04-17 16:21:46 -04:00
Karishma Chadha 3033717d67 Merge pull request #4748 from LLK/hotfix/assetIdNull
Hotfix/asset id null
2019-04-17 16:17:40 -04:00
greenkeeper[bot] 5a2bec60d9 chore(package): update scratch-l10n to version 3.2.20190417195644
Closes #4746
2019-04-17 19:58:37 +00:00
chrisgarrity 7e2f9b8361 Merge pull request #4747 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1555512815
Update scratch-blocks to the latest version 🚀
2019-04-17 15:47:56 -04:00
Benjamin Wheeler 5c1d3e38bd Merge pull request #4710 from benjiwheeler/only-autosave-if-changed
Share, See-project-page buttons will only save if project has changed
2019-04-17 14:55:22 -04:00
Benjamin Wheeler f8243eae9e Merge pull request #4700 from benjiwheeler/upload-in-place
upload in current project id, rather than creating new project id
2019-04-17 14:54:51 -04:00
greenkeeper[bot] 14ead16a8a chore(package): update scratch-blocks to version 0.1.0-prerelease.1555512815 2019-04-17 14:58:11 +00:00
Ben Wheeler c064eaed4e upload in current project id, rather than creating new project id 2019-04-16 23:34:07 -04:00
Karishma Chadha 5758626777 Fix travis.yml before_deploy script to correctly pattern match 2019-04-16 16:01:40 -04:00
greenkeeper[bot] 934396d87a chore(package): update scratch-paint to version 0.2.0-prerelease.20190416185326 2019-04-16 18:55:08 +00:00
Karishma Chadha 130c5de4b0 Update travis yml so that hotfix/ branches are released to hotfix channel 2019-04-16 14:33:12 -04:00
Karishma Chadha 09bcfba800 Add hotfix prefix as branch for releasing 2019-04-16 11:06:47 -04:00
Karishma Chadha cb928b8490 Merge pull request #4733 from paulkaplan/presave-backpack-asset
Make sure to save assets before sending them to the backpack
2019-04-16 11:05:03 -04:00
Evelyn Eastmond 4aba90c8fb Merge pull request #4724 from evhan55/bugfix/4668
Changing tutorial video width to be smaller
2019-04-12 14:35:03 -04:00
Karishma Chadha 308568a332 Fix bugs found while testing related to retrieving and storing asset. 2019-04-12 12:10:49 -04:00
Paul Kaplan c87560aabc Merge pull request #4734 from LLK/revert-4732-greenkeeper/scratch-render-0.1.0-prerelease.20190410184112
Revert "Update scratch-render to the latest version 🚀"
2019-04-11 11:15:29 -04:00
Paul Kaplan 13f72153a3 Merge branch 'develop' into revert-4732-greenkeeper/scratch-render-0.1.0-prerelease.20190410184112 2019-04-11 11:15:17 -04:00
Paul Kaplan b435ac3101 Merge pull request #4735 from paulkaplan/revert-storage-update
Revert storage update due to asset saving errors
2019-04-11 11:14:36 -04:00
Paul Kaplan 0d7a05d37a Merge pull request #4736 from LLK/revert-4644-load-library-images-through-storage
Revert "Load library images through storage"
2019-04-11 11:14:20 -04:00
Paul Kaplan 0fbbf55b51 Revert "Load library images through storage" 2019-04-11 11:13:34 -04:00
Paul Kaplan a9c624b746 Revert storage update due to asset saving errors 2019-04-11 11:12:38 -04:00
Paul Kaplan 0caee112e0 Revert "Update scratch-render to the latest version 🚀" 2019-04-11 11:09:22 -04:00
Paul Kaplan 17e28b2a24 Make sure to save assets before sending them to the backpack
This prevents assets from existing in the backpack server without also existing in the asset server.
2019-04-11 08:29:46 -04:00
Paul Kaplan 7fcbae60bc Merge pull request #4732 from LLK/greenkeeper/scratch-render-0.1.0-prerelease.20190410184112
Update scratch-render to the latest version 🚀
2019-04-10 16:39:15 -04:00
greenkeeper[bot] 9aabea4065 chore(package): update scratch-render to version 0.1.0-prerelease.20190410184112 2019-04-10 18:52:08 +00:00
Chris Willis-Ford 8a1b104f28 Merge pull request #4644 from cwillisf/load-library-images-through-storage
Load library images through storage
2019-04-10 11:35:46 -07:00
Paul Kaplan 8f35eacf9f Merge pull request #4726 from LLK/greenkeeper/scratch-storage-1.3.1
chore(package): update scratch-storage to version 1.3.1
2019-04-10 13:29:57 -04:00
GunChleoc 327983fc8c Shift new troubleshooting documentation to README.md 2019-04-10 17:47:52 +01:00
Paul Kaplan 57ede34c29 Merge pull request #4730 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190410143927
chore(package): update scratch-vm to version 0.2.0-prerelease.2019041…
2019-04-10 12:12:27 -04:00
Paul Kaplan d76eacd7e5 Merge pull request #4728 from LLK/greenkeeper/scratch-l10n-3.1.20190410143750
Update scratch-l10n to the latest version 🚀
2019-04-10 12:11:58 -04:00
greenkeeper[bot] 32f9ae614c chore(package): update scratch-vm to version 0.2.0-prerelease.20190410143927
Closes #4727
2019-04-10 14:42:12 +00:00
greenkeeper[bot] 490c56e46e chore(package): update scratch-l10n to version 3.1.20190410143750 2019-04-10 14:40:08 +00:00
Paul Kaplan c5290a2640 Use an actual url for backpack host so storage doesnt explode 2019-04-10 09:19:13 -04:00
Paul Kaplan 00f07c3801 Merge pull request #4690 from ericrosenbaum/bugfix/load-ext-without-library-assets
Prevent crash on load hardware ext without assets
2019-04-10 09:06:53 -04:00
Paul Kaplan a441cbbe60 Merge pull request #4725 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190409205534
chore(package): update scratch-vm to version 0.2.0-prerelease.2019040…
2019-04-10 09:01:42 -04:00
greenkeeper[bot] 8656148a91 chore(package): update scratch-vm to version 0.2.0-prerelease.20190409205534
Closes #4714
2019-04-09 20:58:10 +00:00
Eric Rosenbaum b32e0ca6d7 Merge pull request #4720 from ericrosenbaum/feature/add-vernier-extension2
Add Vernier Force and Accel extension
2019-04-09 15:32:11 -04:00
Evelyn Eastmond 5b4daec4ab Fixing video height in iframe so the video controls show better. 2019-04-09 13:57:25 -04:00
Evelyn Eastmond aed66d49cf Changing tutorial video width to be smaller and match other tutorial steps. Cutting off video by about 5 pixels on the bottom to prevent any jittering between steps. 2019-04-09 13:53:45 -04:00
Eric Rosenbaum 5d5331b0ad Optimized image assets 2019-04-08 15:48:55 -04:00
Eric Rosenbaum 83b48a683a Add Vernier extension tile 2019-04-08 14:16:10 -04:00
GunChleoc 93477a19d6 Add page title to troubleshooting documentation 2019-04-07 08:27:58 +01:00
GunChleoc a87b08d791 Add troubleshooting page 2019-04-07 08:26:02 +01:00
chrisgarrity 494269bcf5 Merge pull request #4716 from LLK/greenkeeper/scratch-l10n-3.1.20190404094457
Update scratch-l10n to the latest version 🚀
2019-04-04 12:02:42 +02:00
greenkeeper[bot] c684774f06 chore(package): update scratch-l10n to version 3.1.20190404094457 2019-04-04 09:47:04 +00:00
greenkeeper[bot] 9296438911 chore(package): update scratch-storage to version 1.3.1
Closes #4697
2019-04-03 19:29:15 +00:00
Paul Kaplan e9a9767f67 Merge pull request #4689 from LLK/greenkeeper/scratch-render-0.1.0-prerelease.20190326161919
Update scratch-render to the latest version 🚀
2019-04-03 13:55:35 -04:00
Paul Kaplan 174bd295e1 Merge pull request #4713 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1554297463
chore(package): update scratch-blocks to version 0.1.0-prerelease.155…
2019-04-03 13:55:13 -04:00
Paul Kaplan 57a9e0b8ca Merge pull request #4698 from LLK/greenkeeper/scratch-svg-renderer-0.2.0-prerelease.20190329052730
Update scratch-svg-renderer to the latest version 🚀
2019-04-03 13:54:04 -04:00
Paul Kaplan e89a8eab11 Merge branch 'develop' into greenkeeper/scratch-svg-renderer-0.2.0-prerelease.20190329052730 2019-04-03 13:53:51 -04:00
Paul Kaplan 98274c8b8e Merge pull request #4699 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190329172358
Update scratch-vm to the latest version 🚀
2019-04-03 13:53:23 -04:00
Karishma Chadha c8617289bc Merge pull request #4712 from kchadha/extension-button-support
Register callback keys for buttons used in future extensions.
2019-04-03 11:39:08 -04:00
Karishma Chadha e82e370e83 Register callback keys for buttons used in future extensions. 2019-04-03 11:10:10 -04:00
greenkeeper[bot] e2adf9bf14 chore(package): update scratch-blocks to version 0.1.0-prerelease.1554297463
Closes #4692
2019-04-03 13:20:07 +00:00
Ben Wheeler 881e17dc67 See project page button will only save if user can save AND project has changed 2019-04-01 14:40:48 -04:00
Benjamin Wheeler 021063470d Merge pull request #4709 from benjiwheeler/projects-editor-pushstate
Keep URI correct when uploading project while signed out
2019-04-01 14:23:33 -04:00
Ben Wheeler 2b5196106a remove code in sb-file-uploader that sets uri history on loading project from file 2019-04-01 14:08:11 -04:00
greenkeeper[bot] 628bc2cd26 chore(package): update scratch-vm to version 0.2.0-prerelease.20190329172358 2019-03-30 01:07:25 +00:00
greenkeeper[bot] 6d5a6d407b chore(package): update scratch-svg-renderer to version 0.2.0-prerelease.20190329052730 2019-03-30 00:10:29 +00:00
chrisgarrity ce6df3633a Merge pull request #4695 from chrisgarrity/update-l10n
update scratch-l10n to latest
2019-03-29 10:19:05 +01:00
chrisgarrity fecc6f4f6f update scratch-l10n to latest 2019-03-29 09:37:48 +01:00
Paul Kaplan 839deda612 Merge pull request #4674 from mzgoddard/project-changed
dispatch projectChange redux action when project has not changed
2019-03-28 14:01:18 -04:00
Paul Kaplan dac1ece8fd Merge pull request #4665 from mzgoddard/locale-string
use Collator.compare in place of localeCompare
2019-03-28 13:51:42 -04:00
Michael "Z" Goddard fc8c242173 separate projectChanged, MenuBar doesn't need it to render
Rendering the MenuBar does not depend on projectChanged. Some of its
event handling does. Abstracting that dependency we can handle some
behaviour in MenuBar without extra renders.
2019-03-27 11:12:30 -04:00
Eric Rosenbaum 4fab686aea Prevent crash on load hardware ext without assets 2019-03-26 15:59:03 -04:00
greenkeeper[bot] bfae437886 chore(package): update scratch-render to version 0.1.0-prerelease.20190326161919 2019-03-26 17:25:12 +00:00
Ray Schamp c9a989bfab Merge pull request #4688 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190325203133
chore(package): update scratch-vm to latest version
2019-03-26 12:07:05 -04:00
Ray Schamp 9bd2ca246b Update package.json 2019-03-26 12:06:24 -04:00
Ray Schamp f2b9732748 Merge pull request #4687 from rschamp/no-publish-greenkeeper
Exclude greenkeeper builds from gh-pages
2019-03-26 10:41:23 -04:00
Ray fda637edba Exclude greenkeeper builds from gh-pages
They take up so much space and time, but aren't really looked at very much
2019-03-26 10:38:07 -04:00
Ray Schamp a8ea46a357 Merge pull request #4682 from ericrosenbaum/bugfix/webpack-for-vernier-only
Add vernier module to webpack config
2019-03-26 09:44:56 -04:00
Eric Rosenbaum 37826cb901 add vernier module to webpack config 2019-03-25 17:18:37 -04:00
greenkeeper[bot] 6fec13c941 chore(package): update scratch-vm to version 0.2.0-prerelease.20190325203133
Closes #4678
2019-03-25 20:34:08 +00:00
picklesrus f86fd4995f Merge pull request #4675 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190321151527
Update scratch-vm to the latest version 🚀
2019-03-21 11:21:53 -04:00
greenkeeper[bot] 596bdad97a chore(package): update scratch-vm to version 0.2.0-prerelease.20190321151527 2019-03-21 15:18:06 +00:00
Paul Kaplan 2522ad378a Merge pull request #4661 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20190318170811
Update scratch-paint to the latest version 🚀
2019-03-20 15:27:00 -04:00
Paul Kaplan 09d9b51c20 Merge pull request #4673 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190320150859
Update scratch-vm to the latest version 🚀
2019-03-20 15:06:35 -04:00
Michael "Z" Goddard 3f73c6df7b dispatch projectChange redux action when project has not changed
Dispatching a message through redux makes it reconsider all currently
listening components. Dispatching for each block, while loading, can
mean dispatching and reconsidering thousands of times if not more.
While dispatching is pretty quick doing this for every block ends up
taking a lot of time.
2019-03-20 14:37:02 -04:00
greenkeeper[bot] fb85d6ab51 chore(package): update scratch-vm to version 0.2.0-prerelease.20190320150859 2019-03-20 17:12:24 +00:00
Paul Kaplan 97b056ea87 Merge pull request #4671 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190319213349
Update scratch-vm to the latest version 🚀
2019-03-20 11:51:53 -04:00
greenkeeper[bot] 05027a5aee chore(package): update scratch-vm to version 0.2.0-prerelease.20190319213349 2019-03-19 23:42:48 +00:00
Michael "Z" Goddard f5d1dd7e03 use Collator.compare in place of localeCompare 2019-03-19 17:14:05 -04:00
Karishma Chadha e0f8497b48 Merge pull request #4666 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190319154833
chore(package): update scratch-vm to version 0.2.0-prerelease.2019031…
2019-03-19 14:08:09 -04:00
greenkeeper[bot] 0abb94bc81 chore(package): update scratch-vm to version 0.2.0-prerelease.20190319154833
Closes #4662
2019-03-19 15:59:11 +00:00
Evelyn Eastmond 66b3f43581 Merge pull request #4657 from evhan55/extensions/lib-index-file-refactor
Refactor extensions library index file
2019-03-19 11:10:58 -04:00
Paul Kaplan e0db28d82b Merge pull request #4659 from paulkaplan/external-project-save
Move project saving to server to helper, allow external save action
2019-03-18 16:49:29 -04:00
greenkeeper[bot] cf86e4c432 chore(package): update scratch-paint to version 0.2.0-prerelease.20190318170811 2019-03-18 19:58:40 +00:00
Christopher Willis-Ford 28cd140d9f Cancel ScratchImage load if unmounted 2019-03-18 12:58:25 -07:00
Paul Kaplan 00a4db1213 Remove complexity around promise/non-promise.
Since this is in a promise chain, it doesn't matter if onUpdateProjectData returns a promise or a primitive, it'll work either way
2019-03-18 15:09:31 -04:00
Christopher Willis-Ford 5f6b89ec2a Simplify unnecessarily complicated loop 2019-03-18 12:07:41 -07:00
Evelyn Eastmond a4cb3b1eb5 Changing React property name for connection tip icon throughout. 2019-03-18 15:04:12 -04:00
Evelyn Eastmond 8e3bd8c631 Changing prop names throughout React chain to reflect new extension library property names. 2019-03-18 14:58:32 -04:00
Evelyn Eastmond a74bb9afc7 Renaming more variable names related to connection modal properties. 2019-03-18 14:50:49 -04:00
Evelyn Eastmond a082d8329e Adding speech2text folder for extension library. 2019-03-18 14:44:43 -04:00
Evelyn Eastmond 9f9a780093 Adding TODO comments for some file renaming suggestions. 2019-03-18 14:44:43 -04:00
Evelyn Eastmond 8c36ad3cc2 Adding some comments for possible variable name changes. 2019-03-18 14:44:43 -04:00
Evelyn Eastmond 1b2555c7de Renaming some more variables in the extensions library index file. 2019-03-18 14:44:43 -04:00
Evelyn Eastmond 996ed08908 Adding asset folders, moving files, and renaming some extension library variables. 2019-03-18 14:44:43 -04:00
Paul Kaplan 1a194a13e6 Merge pull request #4660 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1552662801
chore(package): update scratch-blocks to version 0.1.0-prerelease.155…
2019-03-18 13:18:24 -04:00
Paul Kaplan 634c780ce1 Merge pull request #4658 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190318152140
Update scratch-vm to the latest version 🚀
2019-03-18 13:15:26 -04:00
Paul Kaplan 7709580710 Move project saving to server to helper, allow external save action 2019-03-18 12:00:01 -04:00
greenkeeper[bot] db487efd62 chore(package): update scratch-vm to version 0.2.0-prerelease.20190318152140 2019-03-18 15:25:51 +00:00
chrisgarrity b26ac6d62b Merge pull request #4646 from chrisgarrity/webconfig-static-path
Set static asset path (publicPath) with environment var
2019-03-18 09:52:05 +01:00
Paul Kaplan e5ba46f499 Merge pull request #4655 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190314205043
Update scratch-vm to the latest version 🚀
2019-03-15 11:20:05 -04:00
greenkeeper[bot] 2d1638e909 chore(package): update scratch-blocks to version 0.1.0-prerelease.1552662801
Closes #4654
2019-03-15 15:15:46 +00:00
greenkeeper[bot] 437106fa33 chore(package): update scratch-vm to version 0.2.0-prerelease.20190314205043 2019-03-14 20:53:31 +00:00
Christopher Willis-Ford 992f012ff3 TO REVERT: Scratch Desktop GUI hacks
At some point these things will be changed / disabled / removed in
master or develop; at that point this change should be replaced with the
more official approach.
2019-03-14 11:51:38 -07:00
Christopher Willis-Ford c53b88dbad use relative publicPath for file-loader 2019-03-14 11:51:38 -07:00
Christopher Willis-Ford 1fd9a1f689 Merge branch 'develop' into load-library-images-through-storage 2019-03-14 11:49:47 -07:00
Chris Willis-Ford 33f3d1d79b Merge pull request #4645 from cwillisf/add-telemetry-hooks
Add telemetry hooks
2019-03-14 10:47:59 -07:00
Christopher Willis-Ford 5051ba2105 Code review: remove dead code, clarify arg name 2019-03-14 10:35:39 -07:00
Paul Kaplan 551b7b98ce Merge pull request #4652 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190314020908
chore(package): update scratch-vm to version 0.2.0-prerelease.2019031…
2019-03-14 08:03:48 -04:00
Paul Kaplan a3e8dd1aab Merge pull request #4651 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1552509492
Update scratch-blocks to the latest version 🚀
2019-03-14 08:03:06 -04:00
greenkeeper[bot] bb0e1e54cd chore(package): update scratch-vm to version 0.2.0-prerelease.20190314020908
Closes #4650
2019-03-14 02:46:54 +00:00
Evelyn Eastmond 98ec67d728 Merge pull request #4641 from evhan55/feature/extensions-wifi-icon
Allow for both bluetooth and internet connection icons in extensions
2019-03-13 19:39:57 -04:00
Chris Willis-Ford f773ee2dab Merge branch 'develop' into load-library-images-through-storage 2019-03-13 16:33:38 -07:00
Christopher Willis-Ford a7487fb26d Fix ScratchImage current jobs count going NaN 2019-03-13 16:00:24 -07:00
greenkeeper[bot] 3adacd3f2a chore(package): update scratch-blocks to version 0.1.0-prerelease.1552509492 2019-03-13 22:48:55 +00:00
Paul Kaplan 1dd3e1d26a Merge pull request #4638 from paulkaplan/remove-analytics
Remove most of the GA events
2019-03-13 17:03:21 -04:00
Christopher Willis-Ford ce5aa9fb7a Fix ScratchImage visibility check on Windows Firefox ESR 2019-03-13 11:09:36 -07:00
Paul Kaplan 846a174f13 Merge pull request #4647 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190312212117
Update scratch-vm to the latest version 🚀
2019-03-13 14:08:56 -04:00
greenkeeper[bot] c779c5fe77 chore(package): update scratch-vm to version 0.2.0-prerelease.20190312212117 2019-03-13 16:08:45 +00:00
chrisgarrity e2a2dcf0fd Set static asset path (publicPath) with environment var
Allows consumers of gui (desktop, android, etc) to rebuild gui with a relative path for static assets. Default is an absolute path as that is what is required for www.
2019-03-13 10:55:11 +01:00
chrisgarrity 3e33c10b04 Fix mock store for project-saver-hoc test
Account for the addition of locale prop expected in mapStateToProps.
Note: if/when the telemetry code gets moved into its own hoc, the prop should get removed, and the test should get updated as well.
2019-03-12 16:13:44 -07:00
Christopher Willis-Ford f0ba4180c7 Fix spacing in comments 2019-03-12 16:13:44 -07:00
Christopher Willis-Ford f6b9f11d3b Merge branch 'develop' into scratch-desktop
Conflicts:
	src/components/library-item/library-item.jsx
	src/components/library/library.jsx
	src/components/menu-bar/menu-bar.jsx
	src/containers/library-item.jsx
	src/lib/project-saver-hoc.jsx
2019-03-12 16:13:40 -07:00
Christopher Willis-Ford d71a9114b7 Merge remote-tracking branch 'upstream/develop' into scratch-desktop 2019-03-12 16:13:28 -07:00
Christopher Willis-Ford 5c4f50c1b8 Merge branch 'develop' into scratch-desktop 2019-03-12 16:13:28 -07:00
Christopher Willis-Ford 4d671ddfab Merge remote-tracking branch 'upstream/develop' into scratch-desktop 2019-03-12 16:13:27 -07:00
Christopher Willis-Ford 94901b33fe Merge remote-tracking branch 'upstream/develop' into scratch-desktop 2019-03-12 16:13:27 -07:00
Christopher Willis-Ford f6dc2726b5 Implement projectDidSave telemetry event 2019-03-12 16:13:21 -07:00
Christopher Willis-Ford 7b4c0657c6 Merge remote-tracking branch 'upstream/develop' into scratch-desktop 2019-03-12 15:55:50 -07:00
Christopher Willis-Ford 39279fa0ef Merge remote-tracking branch 'upstream/develop' into scratch-desktop 2019-03-12 15:55:50 -07:00
Christopher Willis-Ford 5f2f95a6db HACK: report telemetry events if telemetry is active 2019-03-12 15:55:49 -07:00
Christopher Willis-Ford 790fc13157 Fix ScratchImage incompatibilities 2019-03-12 15:01:50 -07:00
Christopher Willis-Ford face0d20d4 Test library icon rotation by comparing src over time 2019-03-12 15:01:50 -07:00
Christopher Willis-Ford 5c16407818 Fix imageSource for rotating library icons 2019-03-12 15:01:50 -07:00
Christopher Willis-Ford d8201fd4ad Merge branch 'develop' into scratch-desktop
Conflicts:
	src/components/library-item/library-item.jsx
	src/components/library/library.jsx
	src/components/menu-bar/menu-bar.jsx
	src/containers/library-item.jsx
	src/lib/project-saver-hoc.jsx
2019-03-12 15:01:42 -07:00
Christopher Willis-Ford 2b1cd4951d Merge remote-tracking branch 'upstream/develop' into scratch-desktop 2019-03-12 15:01:26 -07:00
Christopher Willis-Ford 849fe8fc0c Merge branch 'develop' into scratch-desktop 2019-03-12 15:01:25 -07:00
Christopher Willis-Ford f600e6095e Merge remote-tracking branch 'upstream/develop' into scratch-desktop 2019-03-12 15:01:25 -07:00
Christopher Willis-Ford d952f774dd Merge remote-tracking branch 'upstream/develop' into scratch-desktop 2019-03-12 15:01:25 -07:00
Christopher Willis-Ford 38ee27d896 Merge remote-tracking branch 'upstream/develop' into scratch-desktop 2019-03-12 15:01:24 -07:00
Christopher Willis-Ford 73e1bc352b Merge remote-tracking branch 'upstream/develop' into scratch-desktop 2019-03-12 15:01:24 -07:00
Christopher Willis-Ford 46ce75bf2a Prevent crash when opening tutorials library 2019-03-12 15:01:23 -07:00
Christopher Willis-Ford 2c285fee8a Load 6 ScratchImages at a time, prioritize visible 2019-03-12 15:01:08 -07:00
Christopher Willis-Ford d1c3cf8bb0 Move image-from-storage code into new ScratchImage component 2019-03-12 14:38:25 -07:00
Christopher Willis-Ford a09c7ddb53 Clarify getAssetTypeForExtension 2019-03-12 14:38:25 -07:00
Christopher Willis-Ford fec81c93a3 Make sure icon load starts immediately 2019-03-12 14:38:25 -07:00
Christopher Willis-Ford 2b3c70dfc3 Move event processing out of LibraryItemComponent 2019-03-12 14:38:25 -07:00
Christopher Willis-Ford db9236bef2 Fix sprite lib animation: move load into LibraryItem
The `LibraryItem` component has been split into a container, responsible
for handling interaction with the storage module, and a component,
responsible for presentation. The component now always takes an
`iconURI` and the logic to load an icon from storage has been moved from
`Library` into `LibraryItem`.

TODO: move animation logic from `SpriteLibrary` into `LibraryItem`. This
should simplify the code quite a bit and might also improve performance.
2019-03-12 14:38:25 -07:00
Paul Kaplan fb383ee5f5 Merge pull request #4642 from LLK/greenkeeper/scratch-render-0.1.0-prerelease.20190312125229
Update scratch-render to the latest version 🚀
2019-03-12 14:35:38 -04:00
Paul Kaplan 3a33e7ea86 Update blocks.jsx 2019-03-12 14:34:45 -04:00
greenkeeper[bot] 8e51409e20 chore(package): update scratch-render to version 0.1.0-prerelease.20190312125229 2019-03-12 17:34:45 +00:00
Evelyn Eastmond f29b82b636 Adding right padding to icons. 2019-03-12 10:39:27 -04:00
Evelyn Eastmond 31625d6807 Adding ability to show both bluetooth and internet icons in extension library item tiles. 2019-03-12 10:10:59 -04:00
unknown dadef73bfa Testing two icons side by side to see how the padding is currently working. 2019-03-12 09:39:24 -04:00
Paul Kaplan 268ad88281 Merge pull request #4636 from LLK/greenkeeper/scratch-paint-0.2.0-prerelease.20190311150519
Update scratch-paint to the latest version 🚀
2019-03-11 15:55:26 -04:00
Paul Kaplan 51f9aafec1 Remove analytics library imports 2019-03-11 15:41:07 -04:00
Paul Kaplan 6ea53f63ae Merge pull request #4637 from paulkaplan/remove-auto-select
Remove focus selecting in slider prompt
2019-03-11 15:16:06 -04:00
Paul Kaplan e746b98d83 Remove some of the high-volume analytics events
Too much traffic, not enough value. Leaving stuff having to do with tips library/tutorial card navigation as well as hardware extension connectivity, as those help us find issues with Scratch.
2019-03-11 15:14:30 -04:00
Paul Kaplan f72d0b5985 Remove focus selecting in slider prompt
It is not reliably selecting the input and is making it hard to type into.
2019-03-11 15:02:46 -04:00
greenkeeper[bot] 832dabbb51 chore(package): update scratch-paint to version 0.2.0-prerelease.20190311150519 2019-03-11 17:09:36 +00:00
Paul Kaplan c5b5931e11 Merge pull request #4633 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190308165927
Update scratch-vm to the latest version 🚀
2019-03-11 11:37:36 -04:00
Paul Kaplan f0e518df8a Merge pull request #4635 from LLK/greenkeeper/scratch-render-0.1.0-prerelease.20190304181151
chore(package): update scratch-render to version 0.1.0-prerelease.201…
2019-03-11 11:37:23 -04:00
Paul Kaplan 158409d4d8 Merge pull request #4568 from apple502j/slider-decimal
Slider range change with decimal value support
2019-03-11 11:19:49 -04:00
Paul Kaplan abf49b3115 Fix more prop ordering for linter 2019-03-11 11:05:16 -04:00
Paul Kaplan 4af0d9fe27 Merge pull request #4628 from LLK/touch-picker
Make the stage color picker work with touch.
2019-03-11 11:00:31 -04:00
Paul Kaplan a627552c8e Fix prop ordering for linter 2019-03-11 10:58:09 -04:00
Paul Kaplan 127e1a9632 Add container test for slider prompt
A few modifications were made while making this test:
- Change the "defaultMin/Max" to just be called "min/max", and pass the state through to make the inputs a controlled component. It is best to make form elements controlled since it makes it more clear how data flows.
- Force the passed in data to always be of type `number`, and transform it in the constructor to always be of type `string`, and change the onOk to transform it back to a number. With inputs, it is best to be very explicit about data types, and make it clear where you are using strings and where you are using numbers.
- Do not reset the state to `0` when the field is blank, that makes typing into it very hard (only applies after making it a controlled field)
- Use consistent booleans. Since the VM uses `isDiscrete`, change it so we do not use any opposite flags (like `decimal`) for clarity.

Writing tests for this really helped me understand the behavior. If you read through the test descriptions and see anything you did not intend, let me know.
2019-03-11 10:51:50 -04:00
Paul Kaplan 438e0f9cf7 Add default values for non-required props 2019-03-11 09:52:48 -04:00
Paul Kaplan 1e532745b7 Add a selenium test for slider range 2019-03-11 09:44:56 -04:00
greenkeeper[bot] db811553c9 chore(package): update scratch-vm to version 0.2.0-prerelease.20190308165927 2019-03-08 17:06:09 +00:00
apple502j ef05591d20 Remove decimal option and use type=text 2019-03-08 19:32:01 +09:00
Benjamin Wheeler c83d85e368 Merge pull request #4589 from JinXJinX/fix_account_nav
fix(UI): fix cursor property when hovering account nav items
2019-03-07 15:48:50 -05:00
Paul Kaplan d8d39b1d61 Merge pull request #4585 from JinXJinX/develop
fix(css): fix css value has mixed support warning
2019-03-07 13:31:39 -05:00
Paul Kaplan f364994a46 Fix trying to get color info before pickX/Y was set 2019-03-07 11:17:48 -05:00
Paul Kaplan 9273612f99 Merge pull request #4616 from LLK/greenkeeper/scratch-svg-renderer-0.2.0-prerelease.20190304180800
Update scratch-svg-renderer to the latest version 🚀
2019-03-06 17:12:30 -05:00
Paul Kaplan 880bc530b7 Merge branch 'develop' into greenkeeper/scratch-svg-renderer-0.2.0-prerelease.20190304180800 2019-03-06 17:11:52 -05:00
Paul Kaplan c109c10db6 Merge pull request #4627 from kchadha/run-pify-through-babel-loader
Run pify package through babel loader.
2019-03-06 17:11:07 -05:00
Paul Kaplan 5258254562 Merge pull request #4630 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190306171051
chore(package): update scratch-vm to version 0.2.0-prerelease.2019030…
2019-03-06 17:10:51 -05:00
Paul Kaplan 43a0ed8635 Merge pull request #4629 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1551865183
chore(package): update scratch-blocks to version 0.1.0-prerelease.155…
2019-03-06 17:10:11 -05:00
greenkeeper[bot] f2122bab42 chore(package): update scratch-vm to version 0.2.0-prerelease.20190306171051
Closes #4626
2019-03-06 17:13:32 +00:00
Karishma Chadha c29e943c6b Run pify package through babel loader. 2019-03-06 11:25:43 -05:00
Karishma Chadha d3cc72db4d Merge pull request #4625 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190304203619
chore(package): update scratch-vm to version 0.2.0-prerelease.20190304203619
2019-03-06 11:19:33 -05:00
Paul Kaplan b2b4da2f68 Make the stage color picker work with touch. 2019-03-06 11:17:36 -05:00
greenkeeper[bot] 782fc9aebf chore(package): update scratch-blocks to version 0.1.0-prerelease.1551865183
Closes #4622
2019-03-06 09:42:06 +00:00
Eric Rosenbaum 7c557db8b4 Merge pull request #4621 from ericrosenbaum/feature/boost-coming-soon
Add Boost extension "coming soon" tile
2019-03-05 14:50:44 -05:00
Eric Rosenbaum b76593cabb Optimize svg and png 2019-03-05 14:36:43 -05:00
Eric Rosenbaum ced6e8df18 Fix localization description 2019-03-05 11:29:53 -05:00
Eric Rosenbaum 0ea314d98a Update boost tile and icon 2019-03-05 11:26:56 -05:00
Eric Rosenbaum fe00c71d28 Merge pull request #4618 from ericrosenbaum/bugfix/scratch-link-icon
Update scratch link icon
2019-03-04 16:41:03 -05:00
greenkeeper[bot] 22d96ca94b chore(package): update scratch-vm to version 0.2.0-prerelease.20190304203619
Closes #4609
2019-03-04 20:39:04 +00:00
Eric Rosenbaum 08ec713bff update scratch link icon 2019-03-04 14:10:20 -05:00
greenkeeper[bot] e9c9758618 chore(package): update scratch-render to version 0.1.0-prerelease.20190304181151
Closes #4558
2019-03-04 18:13:26 +00:00
greenkeeper[bot] e51ec837e8 chore(package): update scratch-svg-renderer to version 0.2.0-prerelease.20190304180800 2019-03-04 18:09:45 +00:00
chrisgarrity 6468395ba5 Merge pull request #4507 from chrisgarrity/issue/3632-localize-tutorials
Initial version of translated tutorial gifs
2019-03-04 14:15:15 +01:00
chrisgarrity 686dbb5245 Refactor step and step image naming conventions
Step Ids and step file names did not follow a consistent pattern. To make it easier to automate in the future, things were renamed in the following ways:

StepId: camel case, prefix with the short name that we usually use to refer to the tutorial, e.g. name, music, chaseGame. Followed by a name for the step, e.g. nameAddSprite, nameAddBackdrop. When there are two of a step, add ‘2’ to the end e.g. nameAddSprite2

file-naming: all step files should be in the steps directory. The name is the ‘kebab’ case of the stepId, followed by the language-code, then the file extension. For example the english version of the file for `nameAddSprite` would be `name-add-sprite.en.gif`, `chaseGameAddSprite2` would be `chase-game-add-sprite2.en.gif`
2019-02-28 15:22:44 +01:00
Paul Kaplan 8783d1dfa7 Merge pull request #4596 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190226023539
chore(package): update scratch-vm to version 0.2.0-prerelease.2019022…
2019-02-28 09:00:51 -05:00
Paul Kaplan c6ab3c8e42 Merge pull request #4575 from LLK/greenkeeper/scratch-l10n-3.1.20190220143732
Update scratch-l10n to the latest version 🚀
2019-02-28 09:00:05 -05:00
chrisgarrity 26de4b2e51 refactor image files
* thumbnails folder: thumbnail images for tutorials (multistep decks and videos). Images should not need translations (avoid text that would need translation in the image)
* steps folder: gif images associated with tutorial steps. Images can be translated. English (default) images have the extension `.en.gif`, other language images should replace the `.en` with the corresponding locale.
* renamed some of the steps files to be more consistent and a bit less redundant
* removed a couple of files that were no longer referenced
* revised webpack config to allow localized step files to be loaded from `/static` on www
2019-02-28 12:06:49 +01:00
picklesrus 0dd0fd8e7a Merge pull request #4537 from picklesrus/init-locale-vm
Fixes #4253 by telling the vm the langauge when it is initialized.  B…
2019-02-27 16:37:31 -05:00
apple502j 8fcfe7c7d7 Set default min/max state value to props default 2019-02-27 20:22:31 +09:00
greenkeeper[bot] 2de31ba0ed chore(package): update scratch-vm to version 0.2.0-prerelease.20190226023539
Closes #4588
2019-02-26 04:34:25 +00:00
Xu Jing 742e9e327a fix(UI): fix cursor property when hovering account nav items 2019-02-25 18:11:27 -05:00
Xu Jing 9efcadad5d fix(css): fix css justify-content property's value 'start, end' has mixed support warning 2019-02-25 01:51:53 -05:00
Eric Rosenbaum a4ea94bd39 Add LEGO BOOST “coming soon” tile 2019-02-20 17:32:07 -05:00
picklesrus 60f2f85734 Keep props from getting passed to children that don't need them. 2019-02-20 16:32:11 -05:00
Paul Kaplan 6d5183f99d Merge pull request #4567 from paulkaplan/project-thumbnailer
Add a way for GUI consumer to retrieve snapshots of the stage
2019-02-20 13:39:30 -05:00
greenkeeper[bot] 3b340d02af chore(package): update scratch-l10n to version 3.1.20190220143732 2019-02-20 14:53:53 +00:00
Paul Kaplan 2ab48e011e Merge pull request #4571 from LLK/greenkeeper/scratch-l10n-3.1.20190218084652
Update scratch-l10n to the latest version 🚀
2019-02-19 12:02:04 -05:00
greenkeeper[bot] ce82a52fce chore(package): update scratch-l10n to version 3.1.20190218084652 2019-02-18 09:04:04 +00:00
Paul Kaplan 9a2990f301 Merge pull request #4566 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190215190223
Update scratch-vm to the latest version 🚀
2019-02-17 08:55:04 -05:00
apple502j d431c3f8ba Slider range change with decimal value support 2019-02-17 08:58:20 +09:00
greenkeeper[bot] 2d3632b014 chore(package): update scratch-vm to version 0.2.0-prerelease.20190215190223 2019-02-15 19:25:06 +00:00
Paul Kaplan d301794463 Add a way for GUI consumer to retrieve snapshots of the stage 2019-02-15 14:21:37 -05:00
Paul Kaplan 1a2c3bd0b7 Merge pull request #4563 from paulkaplan/fix-tab-toolbox-refresh
Request a toolbox refresh after switching between tabs to fix #4561
2019-02-15 11:27:01 -05:00
Paul Kaplan 9977ec63d5 Merge pull request #4562 from mzgoddard/mouse-move-drag-perf
Mouse move drag perf
2019-02-15 09:25:17 -05:00
Paul Kaplan 0f803dfc41 Request a toolbox refresh after switching between tabs to fix #4561 2019-02-15 09:10:42 -05:00
Michael "Z" Goddard 6f101523b4 move when the dragging drawable is extracted from the renderer
Extracting the drawable art data is very expensive. We should delay
doing that work as long as possible. As such it should wait until a few
more branching return statements before we extract it.
2019-02-14 16:50:24 -05:00
Paul Kaplan e3dbf435da Merge pull request #4553 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190213210403
Update scratch-vm to the latest version 🚀
2019-02-14 15:38:38 -05:00
Paul Kaplan 215019262b Merge pull request #4557 from paulkaplan/hotfix/gif-frames
Fix loading single frame gifs by removing onDone in favor of indices
2019-02-14 13:55:51 -05:00
Paul Kaplan 40a5ff783a Fix loading single frame gifs by removing onDone in favor of indices
Because the `onFrame` callback could do async work, it was possible for onDone to try to submit the costumes before the onFrame had produced any. This was subject to browsers different timing treatments of setTimeout vs. promise resolution. This makes the gif-decoder use the same style of progress API as the file uploader
2019-02-14 13:55:15 -05:00
greenkeeper[bot] 1cfccde0c4 chore(package): update scratch-vm to version 0.2.0-prerelease.20190213210403 2019-02-13 22:59:11 +00:00
Paul Kaplan 0b557ab520 Merge pull request #4551 from LLK/greenkeeper/scratch-l10n-3.1.20190213193054
Update scratch-l10n to the latest version 🚀
2019-02-13 17:16:15 -05:00
greenkeeper[bot] 2c6519a129 chore(package): update scratch-l10n to version 3.1.20190213193054 2019-02-13 21:32:30 +00:00
Paul Kaplan 208060e441 Merge pull request #4534 from paulkaplan/importing-alert
Show an "Importing..." alert during file import
2019-02-13 16:30:28 -05:00
Paul Kaplan a10704c44b Merge pull request #4528 from paulkaplan/ask-above-monitors
Put the ask blocks text input above the monitors on the stage.
2019-02-13 16:29:55 -05:00
Paul Kaplan 4c53f700c5 Merge pull request #4549 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190213190040
chore(package): update scratch-vm to version 0.2.0-prerelease.2019021…
2019-02-13 15:56:34 -05:00
Paul Kaplan a0f54fda89 Merge pull request #4550 from LLK/greenkeeper/scratch-render-0.1.0-prerelease.20190213183713
chore(package): update scratch-render to version 0.1.0-prerelease.201…
2019-02-13 15:38:17 -05:00
Paul Kaplan ac0f004e5a Add error handling for the costumeUpload function 2019-02-13 15:07:17 -05:00
Paul Kaplan 324dc1717c Refactor to handle errors from file reader 2019-02-13 14:47:21 -05:00
greenkeeper[bot] 31e087c305 chore(package): update scratch-vm to version 0.2.0-prerelease.20190213190040
Closes #4548
2019-02-13 19:09:37 +00:00
greenkeeper[bot] 09493ec894 chore(package): update scratch-render to version 0.1.0-prerelease.20190213183713
Closes #4544
2019-02-13 19:01:34 +00:00
Paul Kaplan 7621fd0c08 Merge pull request #4545 from LLK/greenkeeper/scratch-l10n-3.1.20190213142844
chore(package): update scratch-l10n to version 3.1.20190213142844
2019-02-13 13:02:52 -05:00
Paul Kaplan 8c24c62e38 Merge pull request #4547 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190213162739
Update scratch-vm to the latest version 🚀
2019-02-13 12:59:57 -05:00
Paul Kaplan 59ca7fc771 Merge pull request #4529 from paulkaplan/support-is-discrete-sliders
Support isDiscrete sliders
2019-02-13 12:58:38 -05:00
greenkeeper[bot] 66c06d0a35 chore(package): update scratch-vm to version 0.2.0-prerelease.20190213162739 2019-02-13 16:43:03 +00:00
Paul Kaplan 541c43482d Make sure to only close import alert after import is done 2019-02-13 10:27:54 -05:00
Paul Kaplan 7847f9e775 Show an "Importing..." alert during file import 2019-02-13 10:21:48 -05:00
greenkeeper[bot] a182335f4a chore(package): update scratch-l10n to version 3.1.20190213142844
Closes #4536
2019-02-13 14:56:32 +00:00
Paul Kaplan 86239e56ab Merge pull request #4531 from paulkaplan/prompt-before-upload-replace
Prompt before file upload replaces project
2019-02-13 09:42:33 -05:00
Paul Kaplan cf418fd262 Merge pull request #4538 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190212160344
Update scratch-vm to the latest version 🚀
2019-02-13 09:30:57 -05:00
Paul Kaplan 4927e12e80 Share the replace project confirmation string between new and upload 2019-02-13 09:29:05 -05:00
Paul Kaplan 9896a24472 Merge pull request #4541 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1549990124
chore(package): update scratch-blocks to version 0.1.0-prerelease.154…
2019-02-12 13:59:26 -05:00
greenkeeper[bot] a4406eb542 chore(package): update scratch-blocks to version 0.1.0-prerelease.1549990124
Closes #4540
2019-02-12 16:52:10 +00:00
picklesrus 1aa241749b Fix up the tests and add a couple checks. 2019-02-12 11:41:32 -05:00
greenkeeper[bot] ce3046972a chore(package): update scratch-vm to version 0.2.0-prerelease.20190212160344 2019-02-12 16:15:47 +00:00
Paul Kaplan c9d9f0bd4f Merge pull request #4535 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190212150857
Update scratch-vm to the latest version 🚀
2019-02-12 11:14:04 -05:00
picklesrus ac0705c12c Fixes #4253 by telling the vm the langauge when it is initialized. Before it didn't get the language until the blocks component mounted. 2019-02-12 10:54:32 -05:00
greenkeeper[bot] 735140f276 chore(package): update scratch-vm to version 0.2.0-prerelease.20190212150857 2019-02-12 15:12:02 +00:00
Paul Kaplan ebd372a090 Update to develop and remove try-it steps from new tests 2019-02-12 09:22:24 -05:00
Paul Kaplan 24ca96971e Merge branch 'develop' into prompt-before-upload-replace 2019-02-12 09:21:53 -05:00
Paul Kaplan ade61132a7 Merge pull request #4410 from nikhiljha/nj/ui-changes
Delete Preview Modal
2019-02-12 09:20:57 -05:00
Paul Kaplan 4500c4f781 Remove more try-it steps 2019-02-12 09:05:05 -05:00
Paul Kaplan cae9d9a683 Merge branch 'develop' into nj/ui-changes 2019-02-12 09:04:31 -05:00
Paul Kaplan 9bef38e977 Remove try-it step from more integration tests 2019-02-12 08:53:28 -05:00
Paul Kaplan 773b8e28d4 Merge pull request #4526 from LLK/greenkeeper/scratch-l10n-3.1.20190211142555
Update scratch-l10n to the latest version 🚀
2019-02-11 15:23:31 -05:00
Paul Kaplan d51c984b90 Add integration test for uploading a file and seeing the prompt 2019-02-11 15:22:40 -05:00
Paul Kaplan 361a9d7393 Make sure to put history in try/catch so it can be used with selenium
Selenium loads the playground using the file:// protocol, where history.replaceState throws an error
2019-02-11 14:40:41 -05:00
Paul Kaplan 29e738cb92 Prompt user about file upload replacing the current project
This only shows when you are on a project that is SHOWING_WITHOUT_ID and has changes. This is only the case when logged out, or in the desktop editor.
2019-02-11 14:03:00 -05:00
Paul Kaplan 4ef98ec0fe Add unit test to make sure isDiscrete becomes correct step size 2019-02-11 11:48:18 -05:00
Paul Kaplan 0ae9d70685 Support the isDiscrete flag on slider monitors 2019-02-11 11:19:41 -05:00
Paul Kaplan 3993d5c8f1 Put the ask blocks text input above the monitors on the stage. 2019-02-11 10:45:03 -05:00
Paul Kaplan 7563eddb2b Merge pull request #4509 from LLK/gif-upload
Gif upload v1: sprites, costumes
2019-02-11 10:12:40 -05:00
greenkeeper[bot] b879fd412b chore(package): update scratch-l10n to version 3.1.20190211142555 2019-02-11 14:54:30 +00:00
Paul Kaplan 1d617214b4 Simplify disposal method handling 2019-02-11 09:28:16 -05:00
Ray Schamp e2113cf1e0 Merge pull request #4525 from rschamp/feature/push-tags
Tag NPM releases
2019-02-11 09:22:30 -05:00
Paul Kaplan 3856fbc6c5 Tag NPM releases
Check if an NPM release just happened. And if so, tag the commit and push to GitHub.

Thanks to @paulkaplan for pairing on this
2019-02-11 09:18:43 -05:00
Nikhil Jha fb9c3eed74 Merge branch 'develop' into nj/ui-changes 2019-02-08 20:21:32 -08:00
Paul Kaplan b618f2493c Fix backdrop uploading from gif 2019-02-08 13:56:00 -05:00
Paul Kaplan 8dad7d8ec0 Remove extra parens 2019-02-08 13:45:30 -05:00
Paul Kaplan a18ea5a92c Name uploaded backdrops based on the name 2019-02-08 13:44:10 -05:00
Paul Kaplan d17824d7e3 Merge pull request #4517 from LLK/greenkeeper/scratch-blocks-0.1.0-prerelease.1549643185
Update scratch-blocks to the latest version 🚀
2019-02-08 13:38:41 -05:00
Paul Kaplan 474a00ed72 Merge branch 'develop' into greenkeeper/scratch-blocks-0.1.0-prerelease.1549643185 2019-02-08 13:38:27 -05:00
Ray Schamp 44cb5e8706 Merge pull request #4520 from LLK/prune-removal
Remove prune step from deployment for now
2019-02-08 13:38:24 -05:00
Paul Kaplan 0002f5181c Merge pull request #4515 from LLK/greenkeeper/scratch-l10n-3.1.20190207224638
Update scratch-l10n to the latest version 🚀
2019-02-08 13:38:02 -05:00
Paul Kaplan 7c1d720e17 Merge pull request #4485 from towerofnix/sort-of-variables
Sort variable options in "of" block
2019-02-08 13:37:27 -05:00
Ray Schamp a150bc99db Remove prune step from deployment for now
It doesn't affect greenkeeper subdirectories, which are the ones that cause the most issues. And now the prune step itself is breaking builds. So until it works better, remove this step.
2019-02-08 13:28:22 -05:00
Paul Kaplan b29862fc96 Merge pull request #4498 from mzgoddard/lazy-text-encoder
require text-encoding only if it is needed
2019-02-08 13:27:19 -05:00
Paul Kaplan 38fb199533 Merge pull request #4500 from mzgoddard/loading
Set an initial message number to skip rendering a second time
2019-02-08 13:25:54 -05:00
Paul Kaplan 232d6610ae Merge pull request #4519 from LLK/greenkeeper/scratch-vm-0.2.0-prerelease.20190207224121
chore(package): update scratch-vm to version 0.2.0-prerelease.2019020…
2019-02-08 13:24:50 -05:00
Paul Kaplan b0e2ea7d20 Merge pull request #4518 from LLK/greenkeeper/scratch-render-0.1.0-prerelease.20190208165820
chore(package): update scratch-render to version 0.1.0-prerelease.201…
2019-02-08 13:23:48 -05:00
Paul Kaplan 28635d8028 Remove namer from costume and sound upload helpers to reduce spaghetti 2019-02-08 13:18:57 -05:00
Paul Kaplan ba277a5238 Fix namer conventions to be less bizarre 2019-02-08 13:18:57 -05:00
Paul Kaplan 489467e63d Pass function for naming costumes, add integration tests 2019-02-08 13:18:57 -05:00
Paul Kaplan 88651e6e48 Gif upload v1: sprites, costumes 2019-02-08 13:18:57 -05:00
greenkeeper[bot] 5e3c251405 chore(package): update scratch-render to version 0.1.0-prerelease.20190208165820
Closes #4513
2019-02-08 16:59:54 +00:00
greenkeeper[bot] 41ba125641 chore(package): update scratch-blocks to version 0.1.0-prerelease.1549643185 2019-02-08 16:28:53 +00:00
Paul Kaplan da65b41366 Merge pull request #4499 from LLK/multi-upload
Multiple file upload for costumes, sounds, backdrops and sprites
2019-02-08 10:47:15 -05:00
greenkeeper[bot] 34be9ac23d chore(package): update scratch-l10n to version 3.1.20190207224638 2019-02-07 22:48:35 +00:00
greenkeeper[bot] e20566a242 chore(package): update scratch-vm to version 0.2.0-prerelease.20190207224121
Closes #4510
2019-02-07 22:43:57 +00:00
Paul Kaplan 65f9f7f16d Merge pull request #4512 from LLK/revert-4501-greenkeeper/scratch-render-0.1.0-prerelease.20190206213754
Revert "Update scratch-render to the latest version 🚀"
2019-02-07 16:24:15 -05:00
Paul Kaplan 71a251448c Revert "Update scratch-render to the latest version 🚀" 2019-02-07 16:22:41 -05:00
chrisgarrity 96668b5b04 Initial version of translated tutorial gifs
Proof of concept with spanish as the only example.
2019-02-07 19:14:33 +01:00
Nikhil Jha 781c682e60 Merge branch 'develop' into nj/ui-changes 2019-02-06 14:56:46 -08:00
Michael "Z" Goddard c9cfda8ca8 set an initial message number to skip rendering a second time 2019-02-06 16:53:14 -05:00
Paul Kaplan 65756fcc50 Load files one at a time to enforce load order 2019-02-06 14:32:17 -05:00
Paul Kaplan 7eabc4d9cd Add integration tests for multiple file upload
Including sprites, backdrops, costumes and sounds inputs
2019-02-06 12:09:43 -05:00
Paul Kaplan 8a46b8f71b Make file uploader call callback for each uploaded file 2019-02-06 12:09:32 -05:00
Florrie 9166cb12d5 Don't refer to scratchBlocksUtils from scratch-gui 2019-02-06 12:24:16 -04:00
Michael "Z" Goddard 66a9607bc7 require text-encoding only if it is needed 2019-02-06 10:32:12 -05:00
Florrie c6c5d61039 Sort variable options in "of" block 2019-02-04 16:03:44 -04:00
Nikhil Jha cc80ee345f remove "try it" 2019-02-01 15:29:00 -08:00
Nikhil Jha 9232b1c656 Merge branch 'develop' into nj/ui-changes 2019-02-01 15:19:38 -08:00
Nikhil Jha fcfff18426 try building again 2019-02-01 15:18:30 -08:00
Nikhil Jha 3d588321ca Different tests fail each time I run them. 2019-01-28 17:39:36 -08:00
Nikhil Jha aaa8fd20e4 Fix Lint 2019-01-28 17:07:43 -08:00
Nikhil Jha 9da0937ebb Remove References 2019-01-28 16:52:46 -08:00
Nikhil Jha 4e1a4aedf3 Sometimes local machine says no errors, but travis finds them... 2019-01-25 23:29:41 -08:00
Nikhil Jha f9c9148bab rg "Try It" -l | xargs gsed -i "/Try It/d" 2019-01-25 23:16:23 -08:00
Nikhil Jha 71f8d8f5e8 Remove Irrelevant Tests 2019-01-24 14:59:45 -08:00
Nikhil Jha 8459c67cee Actually fully remove preview modal. 2019-01-23 14:19:50 -08:00
Nikhil Jha 5b75c2e971 Revert "Fix #4262: Saving with Ctrl/Cmd + S"
This reverts commit e2ee0e06f7.
2019-01-23 08:44:32 -08:00
Nikhil Jha 28fbb5352c Fully delete preview modal. 2019-01-22 14:27:16 -08:00
Nikhil Jha e2ee0e06f7 Fix #4262: Saving with Ctrl/Cmd + S 2019-01-22 14:03:28 -08:00
Nikhil Jha 9c6bb6984b Fix #4263: Remove Preview Modal 2019-01-22 13:39:22 -08:00
greenkeeper[bot] 65642edc15 chore(package): update react-intl to version 2.8.0
Closes #3120
2019-01-16 12:21:26 +00:00
Christopher Willis-Ford 86b6e12453 WIP: Load media libs with scratch-storage
The basics work but hover animations are broken
2018-12-20 18:45:55 -08:00
300 changed files with 3045 additions and 1776 deletions

No files matched your search

+14 -7
View File
@@ -10,6 +10,7 @@ env:
- NODE_ENV=production
- SMOKE_URL=https://llk.github.io/scratch-gui/$TRAVIS_PULL_REQUEST_BRANCH
- NPM_TAG=latest
- RELEASE_VERSION="0.1.0-prerelease.$(date +'%Y%m%d%H%M%S')"
cache:
directories:
- node_modules
@@ -22,8 +23,9 @@ script:
before_deploy:
- >
if [ -z "$BEFORE_DEPLOY_RAN" ]; then
npm --no-git-tag-version version 0.1.0-prerelease.$(date +%Y%m%d%H%M%S)
npm --no-git-tag-version version $RELEASE_VERSION
if [ "$TRAVIS_BRANCH" == "master" ]; then export NPM_TAG=stable; fi
if [[ "$TRAVIS_BRANCH" == hotfix/* ]]; then export NPM_TAG=hotfix; fi # double brackets are important for matching the wildcard
git config --global user.email $(git log --pretty=format:"%ae" -n1)
git config --global user.name $(git log --pretty=format:"%an" -n1)
export BEFORE_DEPLOY_RAN=true
@@ -35,11 +37,21 @@ deploy:
- master
- develop
- smoke
- hotfix/*
condition: $TRAVIS_EVENT_TYPE != cron
skip_cleanup: true
email: $NPM_EMAIL
api_key: $NPM_TOKEN
tag: $NPM_TAG
- provider: script
on:
branch:
- master
- develop
- smoke
condition: $TRAVIS_EVENT_TYPE != cron
skip_cleanup: true
script: if npm info scratch-gui | grep -q $RELEASE_VERSION; then git tag $RELEASE_VERSION && git push https://${GH_TOKEN}@github.com/${TRAVIS_REPO_SLUG}.git $RELEASE_VERSION; fi
- provider: s3
on:
branch:
@@ -55,14 +67,9 @@ deploy:
- provider: script
on:
all_branches: true
condition: $TRAVIS_EVENT_TYPE != cron
condition: $TRAVIS_EVENT_TYPE != cron && ! $TRAVIS_BRANCH =~ ^greenkeeper/
skip_cleanup: true
script: npm run deploy -- -x -e $TRAVIS_BRANCH -r https://${GH_TOKEN}@github.com/${TRAVIS_REPO_SLUG}.git
- provider: script
on:
all_branches: true
condition: $TRAVIS_EVENT_TYPE != cron
script: npm run prune -- https://${GH_TOKEN}@github.com/${TRAVIS_REPO_SLUG}.git
- provider: script
on:
branch: develop
+85 -16
View File
@@ -30,29 +30,38 @@ Then go to [http://localhost:8601/](http://localhost:8601/) - the playground out
## Developing alongside other Scratch repositories
### Linking this code to another project's `node_modules/scratch-gui`
### Getting another repo to point to this code
If you wish to develop `scratch-gui` alongside other scratch repositories that depend on it, you may wish
to have the other repositories use your local `scratch-gui` build instead of fetching the current production
version of the scratch-gui that is found by default using `npm install`.
Here's how to link your local `scratch-gui` code to another project's `node_modules/scratch-gui`.
#### Configuration
If you wish to develop scratch-gui alongside other scratch repositories that depend on it, you may wish
to have the other repositories use your local scratch-gui build instead of fetching the current production
version of the scratch-gui that is found by default using `npm install`.
1. In your local `scratch-gui` repository's top level:
1. Make sure you have run `npm install`
2. Build the `dist` directory by running `BUILD_MODE=dist npm run build`
3. Establish a link to this repository by running `npm link`
To do this:
1. Make sure you have run `npm install` from this (scratch-gui) repository's top level
2. Make sure you have run `npm install` from the top level of each repository (such as scratch-www) that depends on scratch-gui
3. From this (scratch-gui) repository's top level, build the `dist` directory by running `BUILD_MODE=dist npm run build`
4. From this (scratch-gui) repository's top level, establish a link to this repository by running `npm link`
5. From the top level of each repository that depends on scratch-gui, run `npm link scratch-gui`
6. Build or run the repositories that depend on scratch-gui
2. From the top level of each repository (such as `scratch-www`) that depends on `scratch-gui`:
1. Make sure you have run `npm install`
2. Run `npm link scratch-gui`
3. Build or run the repositoriy
Instead of `BUILD_MODE=dist npm run build` you can also use `BUILD_MODE=dist npm run watch`, however this may be unreliable.
#### Using `npm run watch`
Instead of `BUILD_MODE=dist npm run build`, you can use `BUILD_MODE=dist npm run watch` instead. This will watch for changes to your `scratch-gui` code, and automatically rebuild when there are changes. Sometimes this has been unreliable; if you are having problems, try going back to `BUILD_MODE=dist npm run build` until you resolve them.
#### Oh no! It didn't work!
* Follow the recipe above step by step and don't change the order. It is especially important to run npm first because installing after the linking will reset the linking.
* Make sure the repositories are siblings on your machine's file tree.
* If you have multiple Terminal tabs or windows open for the different Scratch repositories, make sure to use the same node version in all of them.
* In the worst case unlink the repositories by running `npm unlink` in both, and start over.
If you can't get linking to work right, try:
* Follow the recipe above step by step and don't change the order. It is especially important to run `npm install` _before_ `npm link`, because installing after the linking will reset the linking.
* Make sure the repositories are siblings on your machine's file tree, like `.../.../MY_SCRATCH_DEV_DIRECTORY/scratch-gui/` and `.../.../MY_SCRATCH_DEV_DIRECTORY/scratch-www/`.
* Consistent node.js version: If you have multiple Terminal tabs or windows open for the different Scratch repositories, make sure to use the same node version in all of them.
* If nothing else works, unlink the repositories by running `npm unlink` in both, and start over.
## Testing
### Documentation
@@ -121,6 +130,66 @@ If you want to watch the browser as it runs the test, rather than running headle
USE_HEADLESS=no $(npm bin)/jest --runInBand test/integration/backpack.test.js
```
## Troubleshooting
### Ignoring optional dependencies
When running `npm install`, you can get warnings about optionsl dependencies:
```
npm WARN optional Skipping failed optional dependency /chokidar/fsevents:
npm WARN notsup Not compatible with your operating system or architecture: fsevents@1.2.7
```
You can suppress them by adding the `no-optional` switch:
```
npm install --no-optional
```
Further reading: [Stack Overflow](https://stackoverflow.com/questions/36725181/not-compatible-with-your-operating-system-or-architecture-fsevents1-0-11)
### Resolving dependencies
When installing for the first time, you can get warnings which need to be resolved:
```
npm WARN eslint-config-scratch@5.0.0 requires a peer of babel-eslint@^8.0.1 but none was installed.
npm WARN eslint-config-scratch@5.0.0 requires a peer of eslint@^4.0 but none was installed.
npm WARN scratch-paint@0.2.0-prerelease.20190318170811 requires a peer of react-intl-redux@^0.7 but none was installed.
npm WARN scratch-paint@0.2.0-prerelease.20190318170811 requires a peer of react-responsive@^4 but none was installed.
```
You can check which versions are available:
```
npm view react-intl-redux@0.* version
```
You will neet do install the required version:
```
npm install --no-optional --save-dev react-intl-redux@^0.7
```
The dependency itself might have more missing dependencies, which will show up like this:
```
user@machine:~/sources/scratch/scratch-gui (491-translatable-library-objects)$ npm install --no-optional --save-dev react-intl-redux@^0.7
scratch-gui@0.1.0 /media/cuideigin/Linux/sources/scratch/scratch-gui
├── react-intl-redux@0.7.0
└── UNMET PEER DEPENDENCY react-responsive@5.0.0
```
You will need to install those as well:
```
npm install --no-optional --save-dev react-responsive@^5.0.0
```
Further reading: [Stack Overflow](https://stackoverflow.com/questions/46602286/npm-requires-a-peer-of-but-all-peers-are-in-package-json-and-node-modules)
## Publishing to GitHub Pages
You can publish the GUI to github.io so that others on the Internet can view it.
[Read the wiki for a step-by-step guide.](https://github.com/LLK/scratch-gui/wiki/Publishing-to-GitHub-Pages)
+12 -10
View File
@@ -44,7 +44,7 @@
"babel-loader": "^8.0.4",
"base64-loader": "1.0.0",
"bowser": "1.9.4",
"chromedriver": "2.44.1",
"chromedriver": "74.0.0",
"classnames": "2.2.6",
"computed-style-to-inline-style": "3.0.0",
"copy-webpack-plugin": "^4.5.1",
@@ -56,7 +56,7 @@
"eslint": "^5.0.1",
"eslint-config-scratch": "^5.0.0",
"eslint-plugin-import": "^2.8.0",
"eslint-plugin-react": "7.11.1",
"eslint-plugin-react": "^7.12.4",
"file-loader": "2.0.0",
"get-float-time-domain-data": "0.1.0",
"get-user-media-promise": "1.1.4",
@@ -76,6 +76,7 @@
"lodash.throttle": "4.0.1",
"minilog": "3.1.0",
"mkdirp": "^0.5.1",
"omggif": "1.0.9",
"papaparse": "4.6.2",
"postcss-import": "^12.0.0",
"postcss-loader": "^3.0.0",
@@ -89,7 +90,7 @@
"react-dom": "16.2.0",
"react-draggable": "3.0.5",
"react-ga": "2.5.3",
"react-intl": "2.4.0",
"react-intl": "2.8.0",
"react-modal": "3.6.1",
"react-popover": "0.5.10",
"react-redux": "5.0.7",
@@ -99,18 +100,19 @@
"react-test-renderer": "16.2.0",
"react-tooltip": "3.8.0",
"react-virtualized": "9.20.1",
"react-visibility-sensor": "5.0.2",
"redux": "3.7.2",
"redux-mock-store": "^1.2.3",
"redux-throttle": "0.1.1",
"rimraf": "^2.6.1",
"scratch-audio": "0.1.0-prerelease.20190114210212",
"scratch-blocks": "0.1.0-prerelease.1549534784",
"scratch-l10n": "3.1.20190206143031",
"scratch-paint": "0.2.0-prerelease.20190114205252",
"scratch-render": "0.1.0-prerelease.20190206213754",
"scratch-storage": "1.2.2",
"scratch-svg-renderer": "0.2.0-prerelease.20190125192231",
"scratch-vm": "0.2.0-prerelease.20190207191220",
"scratch-blocks": "0.1.0-prerelease.1557852594",
"scratch-l10n": "3.3.20190516144548",
"scratch-paint": "0.2.0-prerelease.20190516171147",
"scratch-render": "0.1.0-prerelease.20190419202157",
"scratch-storage": "1.3.1",
"scratch-svg-renderer": "0.2.0-prerelease.20190419183947",
"scratch-vm": "0.2.0-prerelease.20190515153227",
"selenium-webdriver": "3.6.0",
"startaudiocontext": "1.2.1",
"style-loader": "^0.23.0",
+4 -2
View File
@@ -142,7 +142,7 @@ class ActionMenu extends React.Component {
<div className={styles.moreButtonsOuter}>
<div className={styles.moreButtons}>
{(moreButtons || []).map(({img, title, onClick: handleClick,
fileAccept, fileChange, fileInput}, keyId) => {
fileAccept, fileChange, fileInput, fileMultiple}, keyId) => {
const isComingSoon = !handleClick;
const hasFileInput = fileInput;
const tooltipId = `${this.mainTooltipId}-${title}`;
@@ -166,6 +166,7 @@ class ActionMenu extends React.Component {
<input
accept={fileAccept}
className={styles.fileInput}
multiple={fileMultiple}
ref={fileInput}
type="file"
onChange={fileChange}
@@ -198,7 +199,8 @@ ActionMenu.propTypes = {
onClick: PropTypes.func, // Optional, "coming soon" if no callback provided
fileAccept: PropTypes.string, // Optional, only for file upload
fileChange: PropTypes.func, // Optional, only for file upload
fileInput: PropTypes.func // Optional, only for file upload
fileInput: PropTypes.func, // Optional, only for file upload
fileMultiple: PropTypes.bool // Optional, only for file upload
})),
onClick: PropTypes.func.isRequired,
title: PropTypes.node.isRequired,
+1 -1
View File
@@ -5,7 +5,7 @@
color: $ui-white;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
display: flex;
justify-content: end;
justify-content: flex-end;
align-items: center;
font-size: .8125rem;
}
+42 -3
View File
@@ -2,10 +2,18 @@
@import "../../css/colors.css";
@import "../../css/z-index.css";
.card-container-overlay {
position: fixed;
pointer-events: none;
z-index: $z-index-card;
}
.card-container {
position:absolute;
pointer-events: auto;
z-index: $z-index-card;
margin: 0.5rem 2rem;
min-width: 468px;
}
.left-card, .right-card {
@@ -104,11 +112,38 @@
font-weight: bold;
}
.header-buttons-hidden {
border-bottom: 0px;
}
.header-buttons-right {
display: flex;
flex-direction: row;
}
.header-buttons img {
margin-bottom: 2px;
}
.shrink-expand-button {
cursor: pointer;
color: white;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding: 0.75rem;
}
.shrink-expand-button:hover, .all-button:hover {
background-color: $ui-black-transparent;
}
.remove-button, .all-button {
cursor: pointer;
color: white;
display: flex;
flex-direction: row;
flex-direction: column;
justify-content: center;
align-items: center;
padding: 0.75rem;
@@ -137,7 +172,7 @@
}
.step-video {
height: 337px;
height: 256px;
}
.step-image {
@@ -188,7 +223,7 @@
}
.close-icon {
height: 1rem;
height: 1.25rem;
margin: .125rem 0; /* To offset the .25rem difference in icon size */
}
@@ -267,3 +302,7 @@
background: $ui-white;
box-shadow: 0px 0px 0px 2px $ui-black-transparent;
}
.hidden {
display: none;
}
+125 -67
View File
@@ -1,10 +1,14 @@
import PropTypes from 'prop-types';
import React, {Fragment} from 'react';
import classNames from 'classnames';
import {FormattedMessage} from 'react-intl';
import Draggable from 'react-draggable';
import styles from './card.css';
import shrinkIcon from './icon--shrink.svg';
import expandIcon from './icon--expand.svg';
import rightArrow from './icon--next.svg';
import leftArrow from './icon--prev.svg';
@@ -12,9 +16,10 @@ import helpIcon from '../../lib/assets/icon--tutorials.svg';
import closeIcon from './icon--close.svg';
import {translateVideo} from '../../lib/libraries/decks/translate-video.js';
import {translateImage} from '../../lib/libraries/decks/translate-image.js';
const CardHeader = ({onCloseCards, onShowAll, totalSteps, step}) => (
<div className={styles.headerButtons}>
const CardHeader = ({onCloseCards, onShrinkExpandCards, onShowAll, totalSteps, step, expanded}) => (
<div className={expanded ? styles.headerButtons : classNames(styles.headerButtons, styles.headerButtonsHidden)}>
<div
className={styles.allButton}
onClick={onShowAll}
@@ -40,19 +45,42 @@ const CardHeader = ({onCloseCards, onShowAll, totalSteps, step}) => (
))}
</div>
) : null}
<div
className={styles.removeButton}
onClick={onCloseCards}
>
<FormattedMessage
defaultMessage="Close"
description="Title for button to close how-to card"
id="gui.cards.close"
/>
<img
className={styles.closeIcon}
src={closeIcon}
/>
<div className={styles.headerButtonsRight}>
<div
className={styles.shrinkExpandButton}
onClick={onShrinkExpandCards}
>
<img
draggable={false}
src={expanded ? shrinkIcon : expandIcon}
/>
{expanded ?
<FormattedMessage
defaultMessage="Shrink"
description="Title for button to shrink how-to card"
id="gui.cards.shrink"
/> :
<FormattedMessage
defaultMessage="Expand"
description="Title for button to expand how-to card"
id="gui.cards.expand"
/>
}
</div>
<div
className={styles.removeButton}
onClick={onCloseCards}
>
<img
className={styles.closeIcon}
src={closeIcon}
/>
<FormattedMessage
defaultMessage="Close"
description="Title for button to close how-to card"
id="gui.cards.close"
/>
</div>
</div>
</div>
);
@@ -68,11 +96,11 @@ const VideoStep = ({video, dragging}) => (
allowFullScreen
allowTransparency="true"
frameBorder="0"
height="338"
height="257"
scrolling="no"
src={`https://fast.wistia.net/embed/iframe/${video}?seo=false&videoFoam=true`}
title="📹"
width="600"
width="466"
/>
<script
async
@@ -106,13 +134,13 @@ ImageStep.propTypes = {
title: PropTypes.node.isRequired
};
const NextPrevButtons = ({isRtl, onNextStep, onPrevStep}) => (
const NextPrevButtons = ({isRtl, onNextStep, onPrevStep, expanded}) => (
<Fragment>
{onNextStep ? (
<div>
<div className={isRtl ? styles.leftCard : styles.rightCard} />
<div className={expanded ? (isRtl ? styles.leftCard : styles.rightCard) : styles.hidden} />
<div
className={isRtl ? styles.leftButton : styles.rightButton}
className={expanded ? (isRtl ? styles.leftButton : styles.rightButton) : styles.hidden}
onClick={onNextStep}
>
<img
@@ -124,9 +152,9 @@ const NextPrevButtons = ({isRtl, onNextStep, onPrevStep}) => (
) : null}
{onPrevStep ? (
<div>
<div className={isRtl ? styles.rightCard : styles.leftCard} />
<div className={expanded ? (isRtl ? styles.rightCard : styles.leftCard) : styles.hidden} />
<div
className={isRtl ? styles.rightButton : styles.leftButton}
className={expanded ? (isRtl ? styles.rightButton : styles.leftButton) : styles.hidden}
onClick={onPrevStep}
>
<img
@@ -140,13 +168,16 @@ const NextPrevButtons = ({isRtl, onNextStep, onPrevStep}) => (
);
NextPrevButtons.propTypes = {
expanded: PropTypes.bool.isRequired,
isRtl: PropTypes.bool,
onNextStep: PropTypes.func,
onPrevStep: PropTypes.func
};
CardHeader.propTypes = {
expanded: PropTypes.bool.isRequired,
onCloseCards: PropTypes.func.isRequired,
onShowAll: PropTypes.func.isRequired,
onShrinkExpandCards: PropTypes.func.isRequired,
step: PropTypes.number,
totalSteps: PropTypes.number
};
@@ -218,6 +249,7 @@ const Cards = props => {
locale,
onActivateDeckFactory,
onCloseCards,
onShrinkExpandCards,
onDrag,
onStartDrag,
onEndDrag,
@@ -225,71 +257,95 @@ const Cards = props => {
onNextStep,
onPrevStep,
step,
expanded,
...posProps
} = props;
let {x, y} = posProps;
if (activeDeckId === null) return;
// Tutorial cards need to calculate their own dragging bounds
// to allow for dragging the cards off the left, right and bottom
// edges of the workspace.
const cardHorizontalDragOffset = 400; // ~80% of card width
const cardVerticalDragOffset = expanded ? 257 : 0; // ~80% of card height, if expanded
const menuBarHeight = 48; // TODO: get pre-calculated from elsewhere?
const wideCardWidth = 500;
if (x === 0 && y === 0) {
// initialize positions
x = isRtl ? -292 : 292;
// The tallest cards are about 385px high, and the default position is pinned
x = isRtl ? (-190 - wideCardWidth - cardHorizontalDragOffset) : 292;
x += cardHorizontalDragOffset;
// The tallest cards are about 320px high, and the default position is pinned
// to near the bottom of the blocks palette to allow room to work above.
const tallCardHeight = 385;
const tallCardHeight = 320;
const bottomMargin = 60; // To avoid overlapping the backpack region
y = window.innerHeight - tallCardHeight - bottomMargin;
y = window.innerHeight - tallCardHeight - bottomMargin - menuBarHeight;
}
const steps = content[activeDeckId].steps;
return (
<Draggable
bounds="parent"
position={{x: x, y: y}}
onDrag={onDrag}
onStart={onStartDrag}
onStop={onEndDrag}
// Custom overlay to act as the bounding parent for the draggable, using values from above
<div
className={styles.cardContainerOverlay}
style={{
width: `${window.innerWidth + (2 * cardHorizontalDragOffset)}px`,
height: `${window.innerHeight - menuBarHeight + cardVerticalDragOffset}px`,
top: `${menuBarHeight}px`,
left: `${-cardHorizontalDragOffset}px`
}}
>
<div className={styles.cardContainer}>
<div className={styles.card}>
<CardHeader
step={step}
totalSteps={steps.length}
onCloseCards={onCloseCards}
onShowAll={onShowAll}
/>
<div className={styles.stepBody}>
{steps[step].deckIds ? (
<PreviewsStep
content={content}
deckIds={steps[step].deckIds}
onActivateDeckFactory={onActivateDeckFactory}
onShowAll={onShowAll}
/>
) : (
steps[step].video ? (
<VideoStep
dragging={dragging}
video={translateVideo(steps[step].video, locale)}
<Draggable
bounds="parent"
position={{x: x, y: y}}
onDrag={onDrag}
onStart={onStartDrag}
onStop={onEndDrag}
>
<div className={styles.cardContainer}>
<div className={styles.card}>
<CardHeader
expanded={expanded}
step={step}
totalSteps={steps.length}
onCloseCards={onCloseCards}
onShowAll={onShowAll}
onShrinkExpandCards={onShrinkExpandCards}
/>
<div className={expanded ? styles.stepBody : styles.hidden}>
{steps[step].deckIds ? (
<PreviewsStep
content={content}
deckIds={steps[step].deckIds}
onActivateDeckFactory={onActivateDeckFactory}
onShowAll={onShowAll}
/>
) : (
<ImageStep
image={steps[step].image}
title={steps[step].title}
/>
)
)}
{steps[step].trackingPixel && steps[step].trackingPixel}
steps[step].video ? (
<VideoStep
dragging={dragging}
video={translateVideo(steps[step].video, locale)}
/>
) : (
<ImageStep
image={translateImage(steps[step].image, locale)}
title={steps[step].title}
/>
)
)}
{steps[step].trackingPixel && steps[step].trackingPixel}
</div>
<NextPrevButtons
expanded={expanded}
isRtl={isRtl}
onNextStep={step < steps.length - 1 ? onNextStep : null}
onPrevStep={step > 0 ? onPrevStep : null}
/>
</div>
<NextPrevButtons
isRtl={isRtl}
onNextStep={step < steps.length - 1 ? onNextStep : null}
onPrevStep={step > 0 ? onPrevStep : null}
/>
</div>
</div>
</Draggable>
</Draggable>
</div>
);
};
@@ -308,6 +364,7 @@ Cards.propTypes = {
})
}),
dragging: PropTypes.bool.isRequired,
expanded: PropTypes.bool.isRequired,
isRtl: PropTypes.bool.isRequired,
locale: PropTypes.string.isRequired,
onActivateDeckFactory: PropTypes.func.isRequired,
@@ -317,6 +374,7 @@ Cards.propTypes = {
onNextStep: PropTypes.func.isRequired,
onPrevStep: PropTypes.func.isRequired,
onShowAll: PropTypes.func,
onShrinkExpandCards: PropTypes.func.isRequired,
onStartDrag: PropTypes.func,
step: PropTypes.number.isRequired,
x: PropTypes.number,
+6 -6
View File
@@ -1,17 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="20px" height="20px" viewBox="0 0 20 20" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 52.2 (67145) - http://www.bohemiancoding.com/sketch -->
<title>General/Check Copy</title>
<!-- Generator: Sketch 53.2 (72643) - https://sketchapp.com -->
<title>Extensions/Connection/Close</title>
<desc>Created with Sketch.</desc>
<defs>
<path d="M10,7.73577763 L12.2675846,5.46819305 C12.8909754,4.84480221 13.8988379,4.84194841 14.5284447,5.47155526 C15.1536925,6.096803 15.1476685,7.11655392 14.531807,7.73241542 L12.2642224,10 L14.531807,12.2675846 C15.1551978,12.8909754 15.1580516,13.8988379 14.5284447,14.5284447 C13.903197,15.1536925 12.8834461,15.1476685 12.2675846,14.531807 L10,12.2642224 L7.73241542,14.531807 C7.10902458,15.1551978 6.10116212,15.1580516 5.47155526,14.5284447 C4.84630752,13.903197 4.85233155,12.8834461 5.46819305,12.2675846 L7.73577763,10 L5.46819305,7.73241542 C4.84480221,7.10902458 4.84194841,6.10116212 5.47155526,5.47155526 C6.096803,4.84630752 7.11655392,4.85233155 7.73241542,5.46819305 L10,7.73577763 Z" id="path-1"></path>
<path d="M15.464935,15.467297 C14.7644059,16.1777705 13.6185877,16.1777705 12.9081142,15.467297 L9.99881899,12.5580018 L7.08841885,15.467297 C6.38236506,16.1733508 5.23765187,16.1733508 4.53159807,15.467297 C4.17912364,15.1148226 4.00012409,14.6485398 4.00012409,14.1888866 C4.00012409,13.7281285 4.17912364,13.2629506 4.53159807,12.9104762 L7.44089328,10.001181 L4.52717833,7.08636112 C4.17359897,6.73278176 3.99459941,6.26760391 4.00012409,5.80242606 C4.00012409,5.34166795 4.17359897,4.88201477 4.52717833,4.52954034 C5.23212719,3.82348655 6.37684038,3.82348655 7.08399911,4.52954034 L9.99881899,7.44325529 L12.9125339,4.52954034 C13.6185877,3.82348655 14.7644059,3.82348655 15.4704597,4.52954034 C16.1765134,5.2344892 16.1765134,6.38030733 15.4704597,7.08636112 L12.5545348,10.001181 L15.4704597,12.914896 C16.1765134,13.6209497 16.1765134,14.7557185 15.464935,15.467297" id="path-1"></path>
</defs>
<g id="General/Check-Copy" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Extensions/Connection/Close" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<mask id="mask-2" fill="white">
<use xlink:href="#path-1"></use>
</mask>
<use id="close-icon" fill="#FFFFFF" xlink:href="#path-1"></use>
<g id="Color/White" mask="url(#mask-2)" fill="#FFFFFF">
<use id="close" fill="#FFFFFF" xlink:href="#path-1"></use>
<g id="White" mask="url(#mask-2)" fill="#FFFFFF">
<rect id="Color" x="0" y="0" width="20" height="20"></rect>
</g>
</g>

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

+18
View File
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="20px" height="20px" viewBox="0 0 20 20" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 53.2 (72643) - https://sketchapp.com -->
<title>Tutorials/Expand</title>
<desc>Created with Sketch.</desc>
<defs>
<path d="M4.5,2 L15.5,2 C16.8807119,2 18,3.11928813 18,4.5 L18,5.5 C18,5.77614237 17.7761424,6 17.5,6 L2.5,6 C2.22385763,6 2,5.77614237 2,5.5 L2,4.5 C2,3.11928813 3.11928813,2 4.5,2 Z M2.5,7 L17.5,7 C17.7761424,7 18,7.22385763 18,7.5 L18,15.5 C18,16.8807119 16.8807119,18 15.5,18 L4.5,18 C3.11928813,18 2,16.8807119 2,15.5 L2,7.5 C2,7.22385763 2.22385763,7 2.5,7 Z M9.65509466,15.1636146 C9.745416,15.2533569 9.86700241,15.3031495 9.99727357,15.3031495 C10.1275447,15.3031495 10.2491311,15.2533569 10.3394525,15.1636146 L12.1574588,13.3456082 C12.3004676,13.2025994 12.3415754,12.9993764 12.2657286,12.8158388 C12.1898819,12.6334592 12.0109762,12.5153467 11.8100691,12.5153467 L11.1506075,12.5153467 L10.7899012,9.98808625 C10.7464775,9.64474938 10.4703026,9.36567961 10.1124912,9.30951827 C10.0783312,9.30546539 10.0435922,9.30314946 10.0094322,9.30314946 C9.61282891,9.30314946 9.27586085,9.59785176 9.22548933,9.98519134 L8.8740467,12.5153467 L8.18447804,12.5153467 C7.9858869,12.5153467 7.81450796,12.6265114 7.73692425,12.8065751 C7.65876155,12.9866388 7.69639544,13.1962306 7.83708829,13.3456082 L9.65509466,15.1636146 Z" id="path-1"></path>
</defs>
<g id="Tutorials/Expand" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<mask id="mask-2" fill="white">
<use xlink:href="#path-1"></use>
</mask>
<g id="Combined-Shape" fill-rule="nonzero"></g>
<g id="White" mask="url(#mask-2)" fill="#FFFFFF">
<rect id="Color" x="0" y="0" width="20" height="20"></rect>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

+18
View File
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="20px" height="20px" viewBox="0 0 20 20" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 53.2 (72643) - https://sketchapp.com -->
<title>Tutorials/Shink</title>
<desc>Created with Sketch.</desc>
<defs>
<path d="M2.5,8 C2.22385763,8 2,7.77614237 2,7.5 C2,7.22385763 2.22385763,7 2.5,7 L3.5,7 C3.77614237,7 4,7.22385763 4,7.5 C4,7.77614237 3.77614237,8 3.5,8 L2.5,8 Z M5.5,8 C5.22385763,8 5,7.77614237 5,7.5 C5,7.22385763 5.22385763,7 5.5,7 L6.5,7 C6.77614237,7 7,7.22385763 7,7.5 C7,7.77614237 6.77614237,8 6.5,8 L5.5,8 Z M8.5,8 C8.22385763,8 8,7.77614237 8,7.5 C8,7.22385763 8.22385763,7 8.5,7 L9.5,7 C9.77614237,7 10,7.22385763 10,7.5 C10,7.77614237 9.77614237,8 9.5,8 L8.5,8 Z M11.5,8 C11.2238576,8 11,7.77614237 11,7.5 C11,7.22385763 11.2238576,7 11.5,7 L12.5,7 C12.7761424,7 13,7.22385763 13,7.5 C13,7.77614237 12.7761424,8 12.5,8 L11.5,8 Z M14.5,8 C14.2238576,8 14,7.77614237 14,7.5 C14,7.22385763 14.2238576,7 14.5,7 L15.5,7 C15.7761424,7 16,7.22385763 16,7.5 C16,7.77614237 15.7761424,8 15.5,8 L14.5,8 Z M17,7.5 C17,7.22385763 17.2238576,7 17.5,7 C17.7761424,7 18,7.22385763 18,7.5 L18,8.5 C18,8.77614237 17.7761424,9 17.5,9 C17.2238576,9 17,8.77614237 17,8.5 L17,7.5 Z M17,10.5 C17,10.2238576 17.2238576,10 17.5,10 C17.7761424,10 18,10.2238576 18,10.5 L18,11.5 C18,11.7761424 17.7761424,12 17.5,12 C17.2238576,12 17,11.7761424 17,11.5 L17,10.5 Z M17,13.5 C17,13.2238576 17.2238576,13 17.5,13 C17.7761424,13 18,13.2238576 18,13.5 L18,14.5 C18,14.7761424 17.7761424,15 17.5,15 C17.2238576,15 17,14.7761424 17,14.5 L17,13.5 Z M16.8130884,16.2258789 C16.9469778,15.9843665 17.2513011,15.8971208 17.4928135,16.0310102 C17.7343259,16.1648996 17.8215716,16.4692229 17.6876822,16.7107353 C17.4827416,17.0804113 17.1867239,17.3929429 16.8289716,17.6178178 C16.59518,17.7647739 16.286523,17.6943798 16.1395669,17.4605882 C15.9926108,17.2267966 16.0630049,16.9181396 16.2967965,16.7711835 C16.5118233,16.6360225 16.6900031,16.4479027 16.8130884,16.2258789 Z M14.5614675,17 C14.8376098,17 15.0614675,17.2238576 15.0614675,17.5 C15.0614675,17.7761424 14.8376098,18 14.5614675,18 L13.5614675,18 C13.2853251,18 13.0614675,17.7761424 13.0614675,17.5 C13.0614675,17.2238576 13.2853251,17 13.5614675,17 L14.5614675,17 Z M11.5614675,17 C11.8376098,17 12.0614675,17.2238576 12.0614675,17.5 C12.0614675,17.7761424 11.8376098,18 11.5614675,18 L10.5614675,18 C10.2853251,18 10.0614675,17.7761424 10.0614675,17.5 C10.0614675,17.2238576 10.2853251,17 10.5614675,17 L11.5614675,17 Z M8.56146746,17 C8.83760983,17 9.06146746,17.2238576 9.06146746,17.5 C9.06146746,17.7761424 8.83760983,18 8.56146746,18 L7.56146746,18 C7.28532508,18 7.06146746,17.7761424 7.06146746,17.5 C7.06146746,17.2238576 7.28532508,17 7.56146746,17 L8.56146746,17 Z M5.56146746,17 C5.83760983,17 6.06146746,17.2238576 6.06146746,17.5 C6.06146746,17.7761424 5.83760983,18 5.56146746,18 L4.56146746,18 C4.28532508,18 4.06146746,17.7761424 4.06146746,17.5 C4.06146746,17.2238576 4.28532508,17 4.56146746,17 L5.56146746,17 Z M3.22779886,16.2951753 C3.37445671,16.5291541 3.30366909,16.8377211 3.06969027,16.9843789 C2.83571145,17.1310368 2.52714447,17.0602491 2.38048662,16.8262703 C2.15682122,16.4694332 2.02609629,16.0609535 2.00352115,15.6335963 C1.98895424,15.3578384 2.20069137,15.1224836 2.47644927,15.1079167 C2.75220716,15.0933498 2.98756192,15.3050869 3.00212882,15.5808448 C3.01567642,15.8373067 3.0937811,16.0813625 3.22779886,16.2951753 Z M3,13.6229349 C3,13.8990773 2.77614237,14.1229349 2.5,14.1229349 C2.22385763,14.1229349 2,13.8990773 2,13.6229349 L2,12.6229349 C2,12.3467925 2.22385763,12.1229349 2.5,12.1229349 C2.77614237,12.1229349 3,12.3467925 3,12.6229349 L3,13.6229349 Z M3,10.6229349 C3,10.8990773 2.77614237,11.1229349 2.5,11.1229349 C2.22385763,11.1229349 2,10.8990773 2,10.6229349 L2,9.62293492 C2,9.34679254 2.22385763,9.12293492 2.5,9.12293492 C2.77614237,9.12293492 3,9.34679254 3,9.62293492 L3,10.6229349 Z M3,7.62293492 C3,7.89907729 2.77614237,8.12293492 2.5,8.12293492 C2.22385763,8.12293492 2,7.89907729 2,7.62293492 L2,7.5 C2,7.22385763 2.22385763,7 2.5,7 C2.77614237,7 3,7.22385763 3,7.5 L3,7.62293492 Z M4.5,2 L15.5,2 C16.8807119,2 18,3.11928813 18,4.5 L18,5.5 C18,5.77614237 17.7761424,6 17.5,6 L2.5,6 C2.22385763,6 2,5.77614237 2,5.5 L2,4.5 C2,3.11928813 3.11928813,2 4.5,2 Z M10.3449053,9.44268434 L12.1629117,11.2606907 C12.3036046,11.4100683 12.3412384,11.6196601 12.2630758,11.7997238 C12.185492,11.9797875 12.0141131,12.0909522 11.815522,12.0909522 L11.1259533,12.0909522 L10.7745107,14.6211076 C10.7241392,15.0084472 10.3871711,15.3031495 9.99056779,15.3031495 C9.9564078,15.3031495 9.92166882,15.3008335 9.88750883,15.2967806 C9.52969739,15.2406193 9.25352253,14.9615495 9.21009882,14.6182127 L8.84939246,12.0909522 L8.18993091,12.0909522 C7.98902384,12.0909522 7.81011812,11.9728397 7.73427135,11.7904601 C7.65842459,11.6069225 7.69953238,11.4036995 7.84254116,11.2606907 L9.66054753,9.44268434 C9.75086886,9.35294199 9.87245528,9.30314946 10.0027264,9.30314946 C10.1329976,9.30314946 10.254584,9.35294199 10.3449053,9.44268434 Z" id="path-1"></path>
</defs>
<g id="Tutorials/Shink" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<mask id="mask-2" fill="white">
<use xlink:href="#path-1"></use>
</mask>
<g id="Rectangle" fill-rule="nonzero"></g>
<g id="White" mask="url(#mask-2)" fill="#FFFFFF">
<rect id="Color" x="0" y="0" width="20" height="20"></rect>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.5 KiB

@@ -45,8 +45,8 @@ const AutoScanningStep = props => (
src={radarIcon}
/>
<img
className={styles.peripheralButtonImage}
src={props.peripheralButtonImage}
className={styles.connectionTipIcon}
src={props.connectionTipIconURL}
/>
</React.Fragment>
)}
@@ -142,9 +142,9 @@ const AutoScanningStep = props => (
);
AutoScanningStep.propTypes = {
connectionTipIconURL: PropTypes.string,
onRefresh: PropTypes.func,
onStartScan: PropTypes.func,
peripheralButtonImage: PropTypes.string,
phase: PropTypes.oneOf(Object.keys(PHASES))
};
@@ -15,7 +15,7 @@ const ConnectedStep = props => (
<div className={styles.peripheralActivity}>
<img
className={styles.peripheralActivityIcon}
src={props.peripheralImage}
src={props.connectionIconURL}
/>
<img
className={styles.bluetoothConnectedIcon}
@@ -64,9 +64,9 @@ const ConnectedStep = props => (
);
ConnectedStep.propTypes = {
connectionIconURL: PropTypes.string.isRequired,
onCancel: PropTypes.func,
onDisconnect: PropTypes.func,
peripheralImage: PropTypes.string.isRequired
onDisconnect: PropTypes.func
};
export default ConnectedStep;
@@ -18,7 +18,7 @@ const ConnectingStep = props => (
<div className={styles.peripheralActivity}>
<img
className={styles.peripheralActivityIcon}
src={props.peripheralImage}
src={props.connectionIconURL}
/>
<img
className={styles.bluetoothConnectingIcon}
@@ -63,8 +63,8 @@ const ConnectingStep = props => (
ConnectingStep.propTypes = {
connectingMessage: PropTypes.node.isRequired,
onDisconnect: PropTypes.func,
peripheralImage: PropTypes.string.isRequired
connectionIconURL: PropTypes.string.isRequired,
onDisconnect: PropTypes.func
};
export default ConnectingStep;
@@ -162,7 +162,7 @@
height: 80px; */
}
.peripheral-button-image {
.connection-tip-icon {
position: absolute;
}
@@ -27,7 +27,7 @@ const ConnectionModalComponent = props => (
className={styles.modalContent}
contentLabel={props.name}
headerClassName={styles.header}
headerImage={props.smallPeripheralImage}
headerImage={props.connectionSmallIconURL}
id="connectionModal"
onHelp={props.onHelp}
onRequestClose={props.onCancel}
@@ -44,17 +44,21 @@ const ConnectionModalComponent = props => (
);
ConnectionModalComponent.propTypes = {
connectingMessage: PropTypes.node,
connectingMessage: PropTypes.node.isRequired,
connectionSmallIconURL: PropTypes.string,
connectionTipIconURL: PropTypes.string,
name: PropTypes.node,
onCancel: PropTypes.func.isRequired,
onHelp: PropTypes.func.isRequired,
peripheralButtonImage: PropTypes.string,
phase: PropTypes.oneOf(Object.keys(PHASES)).isRequired,
smallPeripheralImage: PropTypes.string,
title: PropTypes.string.isRequired,
useAutoScan: PropTypes.bool.isRequired
};
ConnectionModalComponent.defaultProps = {
connectingMessage: 'Connecting'
};
export {
ConnectionModalComponent as default,
PHASES
@@ -17,7 +17,7 @@ const ErrorStep = props => (
<div className={styles.peripheralActivity}>
<img
className={styles.peripheralActivityIcon}
src={props.peripheralImage}
src={props.connectionIconURL}
/>
</div>
</Box>
@@ -70,9 +70,9 @@ const ErrorStep = props => (
);
ErrorStep.propTypes = {
connectionIconURL: PropTypes.string.isRequired,
onHelp: PropTypes.func,
onScanning: PropTypes.func,
peripheralImage: PropTypes.string.isRequired
onScanning: PropTypes.func
};
export default ErrorStep;
@@ -1,34 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="52px" height="52px" viewBox="0 0 52 52" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 51.1 (57501) - http://www.bohemiancoding.com/sketch -->
<title>scratchlink</title>
<desc>Created with Sketch.</desc>
<defs>
<path d="M6.599912,0 L37.399912,0 C41.045312,0 43.999912,2.95504 43.999912,6.6 L43.999912,37.4 C43.999912,41.04496 41.045312,44 37.399912,44 L6.599912,44 C2.954952,44 -8.8e-05,41.04496 -8.8e-05,37.4 L-8.8e-05,6.6 C-8.8e-05,2.95504 2.954952,0 6.599912,0" id="path-1"></path>
<filter x="-13.6%" y="-13.6%" width="127.3%" height="127.3%" filterUnits="objectBoundingBox" id="filter-2">
<feMorphology radius="2" operator="dilate" in="SourceAlpha" result="shadowSpreadOuter1"></feMorphology>
<feOffset dx="0" dy="0" in="shadowSpreadOuter1" result="shadowOffsetOuter1"></feOffset>
<feColorMatrix values="0 0 0 0 0.298039216 0 0 0 0 0.592156863 0 0 0 0 1 0 0 0 0.25 0" type="matrix" in="shadowOffsetOuter1"></feColorMatrix>
</filter>
</defs>
<g id="R1_Extension" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="No-Comms-with-Scratch-Link-or-Bluetooth" transform="translate(-506.000000, -262.000000)">
<g id="No-connection-v2" transform="translate(-2.000000, 0.000000)">
<g id="Modal" transform="translate(407.000000, 187.000000)">
<g id="Scratch-Link" transform="translate(53.000000, 75.000000)">
<g id="scratchlink" transform="translate(52.000000, 4.000000)">
<g id="Fill-1">
<use fill="black" fill-opacity="1" filter="url(#filter-2)" xlink:href="#path-1"></use>
<use fill="#25AFF4" fill-rule="evenodd" xlink:href="#path-1"></use>
</g>
<path d="M35.854764,19.82882 C36.027684,20.8681 36.062444,21.91574 35.988084,22.9427 L39.440764,24.78542 C39.984604,25.05162 40.240684,25.68522 40.034324,26.25458 L38.559444,30.3237 C38.353084,30.89438 37.748524,31.2169 37.159364,31.07082 L33.333564,30.26386 C32.718004,31.10382 32.018404,31.8835 31.217604,32.56902 L32.519124,36.64606 C32.716684,37.21982 32.448724,37.85034 31.898724,38.10686 L27.975684,39.93638 C27.425684,40.19246 26.770524,39.9927 26.458124,39.47262 L24.171444,35.85494 C23.131724,36.02742 22.084524,36.06218 21.057124,35.98826 L19.215724,39.43918 C18.949084,39.9839 18.314604,40.23998 17.744364,40.0323 L13.669084,38.54906 C13.099284,38.34138 12.777644,37.7377 12.923284,37.14898 L13.730684,33.32186 C12.896444,32.71818 12.116324,32.01858 11.431244,31.21778 L7.354204,32.5193 C6.780004,32.71686 6.149924,32.4489 5.893404,31.8989 L4.063884,27.97542 C3.807364,27.42542 4.007564,26.7707 4.527204,26.45786 L8.133884,24.1769 C7.972404,23.13146 7.920924,22.0781 8.006284,21.04586 L4.556684,19.20446 C4.011524,18.93782 3.755884,18.30202 3.964004,17.73178 L5.449444,13.66838 C5.658004,13.09902 6.262124,12.77826 6.850404,12.92478 L10.666964,13.73614 C11.270204,12.9019 11.981684,12.1165 12.782044,11.43098 L11.480964,7.35394 C11.283404,6.78018 11.551364,6.14966 12.101364,5.89314 L16.024404,4.06362 C16.574404,3.8071 17.229564,4.0073 17.541964,4.52782 L19.828644,8.14506 C20.868364,7.97258 21.927444,7.9321 22.960564,7.9893 L24.796244,4.55466 C25.062884,4.01038 25.697364,3.75474 26.266724,3.96198 L30.325724,5.4395 C30.895524,5.64674 31.217164,6.25086 31.071084,6.83914 L30.263684,10.6667 C31.109804,11.2651 31.883764,11.98186 32.580724,12.77694 L36.646324,11.4807 C37.220084,11.28314 37.850164,11.5511 38.106684,12.1011 L39.936204,16.02458 C40.192724,16.57458 39.992524,17.2293 39.472444,17.54214 L35.854764,19.82882 Z" id="Fill-4" fill-opacity="0.15" fill="#231F20"></path>
<path d="M30.417684,24.430956 C30.445844,26.694756 29.431644,28.874076 27.638204,30.404836 C26.209964,31.635956 24.408604,32.343916 22.535524,32.412996 C21.669604,33.092796 20.619324,33.496276 19.495124,33.567996 C19.375004,33.575476 19.254884,33.580316 19.134764,33.580316 C16.263324,33.580316 13.845964,31.337196 13.626404,28.473236 C13.623764,28.439796 13.621124,28.394036 13.618484,28.335076 C13.615844,28.314836 13.613644,28.294156 13.613644,28.271276 C13.557324,27.065236 13.585484,26.068636 13.621124,25.435036 C13.654564,24.850276 13.674804,23.797356 13.677444,23.552276 L13.677444,23.536876 C13.692844,22.680636 13.904924,21.845516 14.290804,21.096636 C14.180804,20.258876 14.183444,19.361716 14.300924,18.421876 L14.316324,18.276236 C14.318964,18.258196 14.318964,18.237516 14.321164,18.219916 C14.323804,18.204516 14.326444,18.186916 14.329084,18.168876 C14.425884,17.368956 14.594844,16.058196 15.310284,14.714436 C16.500924,12.460756 18.759444,11.116996 21.350164,11.116996 L21.460164,11.116996 C22.275044,10.662476 23.192444,10.419596 24.134924,10.419596 L24.234804,10.419596 C27.234284,10.473276 29.674524,12.951356 29.674524,15.945996 C29.674524,15.979436 29.572004,19.571596 29.457164,20.800516 C30.078004,21.886436 30.404924,23.117996 30.417684,24.430956" id="Fill-6" fill="#FFFFFF"></path>
<path d="M27.86278,24.459116 C27.88302,25.959076 27.19354,27.425596 25.96946,28.470596 C24.92974,29.367316 23.60094,29.863196 22.2343,29.863196 C21.9989,29.860556 21.7613,29.847796 21.52898,29.816996 C21.4753,29.888716 21.41898,29.957796 21.36046,30.023796 C20.83378,30.619116 20.10822,30.971996 19.31622,31.017756 C19.25506,31.023036 19.1961,31.025676 19.13494,31.025676 C17.58922,31.025676 16.28858,29.814356 16.17374,28.271276 C16.1711,28.240476 16.16846,28.209676 16.16846,28.176676 L16.16626,28.153796 C16.11478,27.055116 16.1403,26.150476 16.1711,25.585956 C16.20938,24.931676 16.23006,23.820236 16.23226,23.579996 C16.24502,22.828916 16.53366,22.146916 17.00138,21.628156 C16.76906,20.823396 16.69514,19.857596 16.8377,18.715356 L16.85574,18.549476 C16.85838,18.536716 16.85838,18.523956 16.86102,18.511196 C16.94286,17.841516 17.06518,16.852836 17.56854,15.907716 C18.32006,14.486956 19.69946,13.672076 21.3499,13.672076 C21.4423,13.672076 21.5391,13.674716 21.63854,13.679556 C21.81278,13.689676 21.98878,13.710356 22.15994,13.741156 C22.69674,13.258036 23.40954,12.959276 24.19142,12.974236 C25.80622,13.002396 27.11918,14.336476 27.11918,15.945996 C27.11918,15.981636 26.97618,20.281756 26.9071,20.654876 C26.86618,20.887196 26.79754,21.109396 26.70778,21.319276 C27.45138,22.177276 27.85266,23.248236 27.86278,24.459116" id="Fill-8" fill="#F9A83A"></path>
<path d="M21.41766,21.36794 C20.65162,21.2487 20.24286,20.7361 20.44614,19.15034 L20.46946,18.95894 C20.64018,17.58174 20.78362,17.27506 21.44626,17.3107 C21.6403,17.32302 21.86382,17.44578 22.0869,17.62882 C22.29414,17.86862 22.75394,18.17882 23.0131,18.79306 C23.19922,19.24362 23.26302,19.53754 23.28942,19.85434 L23.33078,20.24242 L23.33078,20.24066 C23.38534,20.50642 23.58906,20.7251 23.87066,20.77218 C24.23278,20.83862 24.58258,20.5975 24.64638,20.2345 C24.6543,20.19138 24.81886,16.01534 24.81886,15.94714 C24.81886,15.5793 24.52362,15.28054 24.15358,15.27438 C23.78354,15.27218 23.4839,15.5727 23.4839,15.93966 C23.4839,15.94802 23.48214,16.52442 23.47026,17.10522 C22.94358,16.53322 22.26994,16.01974 21.5149,15.9753 C19.49574,15.8675 19.2753,17.72386 19.14242,18.79394 L19.12218,18.98358 C18.85026,21.09778 19.55514,22.43186 21.21218,22.69146 C23.0131,22.97394 24.21518,23.38754 24.22618,24.49546 C24.23278,24.92622 24.00618,25.36842 23.61106,25.70766 C23.13454,26.1129 22.49654,26.2955 21.89594,26.19694 C21.70938,26.16834 21.5347,26.1173 21.3675,26.06142 C21.13166,25.9303 20.53678,25.56818 20.23538,25.15106 C19.98106,24.79906 19.89306,24.24554 19.86534,23.89442 C19.86886,23.7369 19.8693,23.6335 19.8693,23.62162 C19.8759,23.25202 19.57934,22.95062 19.2137,22.94182 C18.84234,22.9339 18.5383,23.23222 18.53126,23.60138 C18.53126,23.6137 18.51454,24.92226 18.46658,25.7103 C18.39794,26.97926 18.46658,28.05462 18.46658,28.10126 C18.49386,28.46998 18.8111,28.74806 19.18026,28.72342 C19.54766,28.70362 19.82838,28.38418 19.80286,28.01502 C19.80286,28.0093 19.76986,27.48878 19.77426,26.76806 C20.26398,27.0809 20.90726,27.38494 21.67418,27.51562 C22.67298,27.68326 23.6951,27.39418 24.47522,26.72318 C25.17482,26.12522 25.57522,25.30858 25.56422,24.48182 C25.54002,22.01254 22.75394,21.57914 21.41766,21.36794" id="Fill-10" fill="#FFFFFF"></path>
</g>
</g>
</g>
</g>
</g>
</g>
</svg>
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg width="52px" height="52px" viewBox="0 0 52 52" version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
style="enable-background:new 0 0 52 52;" xml:space="preserve">
<style type="text/css">
.st0{fill:#0FBD8C;}
.st1{fill:#FFFFFF;}
.st2{fill:#F9A83A;}
</style>
<title>Scratch Link</title>
<desc>Created with Sketch.</desc>
<g id="_x35_2x52-for-the-dialog">
<g id="Group">
<path id="bg" class="st0" d="M41.4,5c3.1,0,5.6,2.5,5.6,5.6v30.8c0,3.1-2.5,5.6-5.6,5.6H10.6C7.5,47,5,44.5,5,41.4V10.6
C5,7.5,7.5,5,10.6,5H41.4z"/>
<path id="scratch-outline-2" class="st1" d="M28.8,28.9c0,2.7-1.1,5.4-3.2,7.2c-1.7,1.5-3.8,2.3-5.9,2.4c-1,0.8-2.2,1.3-3.5,1.4
c-0.1,0-0.3,0-0.4,0c-3.3,0-6.1-2.7-6.4-6.2c0,0,0-0.1,0-0.2c0,0,0,0,0-0.1c-0.1-1.5,0-2.7,0-3.4c0-0.7,0.1-2,0.1-2.3v0
c0-1,0.3-2,0.7-2.9c-0.1-1-0.1-2.1,0-3.2l0-0.2c0,0,0,0,0-0.1c0,0,0,0,0-0.1c0.1-1,0.3-2.5,1.1-4.2c1.4-2.7,4-4.3,7-4.3h0.1
c0.9-0.5,2-0.8,3.1-0.8h0.1c3.5,0.1,6.3,3.1,6.3,6.7c0,0-0.1,4.4-0.3,5.9C28.4,25.9,28.7,27.4,28.8,28.9"/>
<path id="scratch-outline-1" class="st2" d="M25.8,29c0,1.8-0.8,3.6-2.2,4.8c-1.2,1.1-2.8,1.7-4.3,1.7c-0.3,0-0.5,0-0.8-0.1
c-0.1,0.1-0.1,0.2-0.2,0.2c-0.6,0.7-1.5,1.1-2.4,1.2c-0.1,0-0.1,0-0.2,0c-1.8,0-3.3-1.5-3.4-3.3c0,0,0-0.1,0-0.1l0,0
c-0.1-1.3,0-2.4,0-3.1c0-0.8,0.1-2.1,0.1-2.4c0-0.9,0.3-1.7,0.9-2.4c-0.3-1-0.4-2.1-0.2-3.5l0-0.2c0,0,0,0,0,0
c0.1-0.8,0.2-2,0.8-3.1c0.9-1.7,2.5-2.7,4.4-2.7c0.1,0,0.2,0,0.3,0c0.2,0,0.4,0,0.6,0.1c0.6-0.6,1.5-0.9,2.4-0.9
c1.9,0,3.4,1.6,3.4,3.6c0,0-0.2,5.2-0.2,5.7c0,0.3-0.1,0.5-0.2,0.8C25.3,26.2,25.8,27.5,25.8,29"/>
<path id="scratch-fill" class="st1" d="M18.3,25.3c-0.9-0.1-1.4-0.8-1.1-2.7l0-0.2c0.2-1.7,0.4-2,1.1-2c0.2,0,0.5,0.2,0.7,0.4
c0.2,0.3,0.8,0.7,1.1,1.4c0.2,0.5,0.3,0.9,0.3,1.3l0,0.5v0c0.1,0.3,0.3,0.6,0.6,0.6c0.4,0.1,0.8-0.2,0.9-0.6
c0-0.1,0.2-5.1,0.2-5.2c0-0.4-0.3-0.8-0.8-0.8c-0.4,0-0.8,0.4-0.8,0.8c0,0,0,0.7,0,1.4c-0.6-0.7-1.4-1.3-2.3-1.4
c-2.3-0.1-2.6,2.1-2.8,3.4l0,0.2c-0.3,2.5,0.5,4.2,2.4,4.5c2.1,0.3,3.5,0.8,3.5,2.2c0,0.5-0.3,1.1-0.7,1.5c-0.6,0.5-1.3,0.7-2,0.6
c-0.2,0-0.4-0.1-0.6-0.2c-0.3-0.2-1-0.6-1.3-1.1c-0.3-0.4-0.4-1.1-0.4-1.5c0-0.2,0-0.3,0-0.3c0-0.4-0.3-0.8-0.8-0.8
c-0.4,0-0.8,0.3-0.8,0.8c0,0,0,1.6-0.1,2.5c-0.1,1.5,0,2.8,0,2.9c0,0.4,0.4,0.8,0.8,0.8c0.4,0,0.8-0.4,0.7-0.9c0,0,0-0.6,0-1.5
c0.6,0.4,1.3,0.7,2.2,0.9c1.2,0.2,2.3-0.1,3.3-1c0.8-0.7,1.3-1.7,1.3-2.7C23.1,26,19.9,25.5,18.3,25.3"/>
<path id="signal" class="st1" d="M37.7,36.9c-0.2,0-0.4-0.1-0.5-0.2c-0.3-0.3-0.3-0.8,0-1.1c2.6-2.6,4-6,4-9.6
c0-3.6-1.4-7.1-4-9.7c-0.3-0.3-0.3-0.8,0-1.1c0.3-0.3,0.8-0.3,1,0c2.8,2.9,4.4,6.7,4.4,10.7c0,4-1.6,7.9-4.4,10.7
C38.1,36.9,37.9,36.9,37.7,36.9z M35,33.5c-0.2,0-0.4-0.1-0.5-0.2c-0.3-0.3-0.3-0.8,0-1.1c1.7-1.7,2.6-3.9,2.6-6.3
c0-2.4-0.9-4.6-2.6-6.3c-0.3-0.3-0.3-0.8,0-1.1c0.3-0.3,0.8-0.3,1,0c1.9,2,3,4.6,3,7.3c0,2.8-1.1,5.4-3,7.3
C35.4,33.5,35.2,33.5,35,33.5z M32.3,30.1c-0.2,0-0.4-0.1-0.5-0.2c-0.3-0.3-0.3-0.8,0-1.1c0.8-0.8,1.2-1.8,1.2-2.9
c0-1.1-0.4-2.1-1.2-2.9c-0.3-0.3-0.3-0.8,0-1.1c0.3-0.3,0.8-0.3,1,0c1,1,1.6,2.4,1.6,3.9c0,1.5-0.6,2.9-1.6,3.9
C32.7,30.1,32.5,30.1,32.3,30.1z"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 8.4 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

@@ -23,7 +23,7 @@ class PeripheralTile extends React.Component {
<Box className={styles.peripheralTileName}>
<img
className={styles.peripheralTileImage}
src={this.props.smallPeripheralImage}
src={this.props.connectionSmallIconURL}
/>
<Box className={styles.peripheralTileNameWrapper}>
<Box className={styles.peripheralTileNameLabel}>
@@ -77,11 +77,11 @@ class PeripheralTile extends React.Component {
}
PeripheralTile.propTypes = {
connectionSmallIconURL: PropTypes.string,
name: PropTypes.string,
onConnecting: PropTypes.func,
peripheralId: PropTypes.string,
rssi: PropTypes.number,
smallPeripheralImage: PropTypes.string
rssi: PropTypes.number
};
export default PeripheralTile;
@@ -34,11 +34,11 @@ const ScanningStep = props => (
<div className={styles.peripheralTilePane}>
{props.peripheralList.map(peripheral =>
(<PeripheralTile
connectionSmallIconURL={props.connectionSmallIconURL}
key={peripheral.peripheralId}
name={peripheral.name}
peripheralId={peripheral.peripheralId}
rssi={peripheral.rssi}
smallPeripheralImage={props.smallPeripheralImage}
onConnecting={props.onConnecting}
/>)
)}
@@ -86,6 +86,7 @@ const ScanningStep = props => (
);
ScanningStep.propTypes = {
connectionSmallIconURL: PropTypes.string,
onConnecting: PropTypes.func,
onRefresh: PropTypes.func,
peripheralList: PropTypes.arrayOf(PropTypes.shape({
@@ -93,8 +94,7 @@ ScanningStep.propTypes = {
rssi: PropTypes.number,
peripheralId: PropTypes.string
})),
scanning: PropTypes.bool.isRequired,
smallPeripheralImage: PropTypes.string
scanning: PropTypes.bool.isRequired
};
ScanningStep.defaultProps = {
+1 -1
View File
@@ -33,6 +33,6 @@
border-top: 1px solid $ui-black-transparent;
}
.menu-item-bordered:hover {
.menu-item-danger:hover {
background: $error-primary;
}
@@ -25,9 +25,17 @@ const BorderedMenuItem = props => (
/>
);
const DangerousMenuItem = props => (
<MenuItem
{...props}
attributes={{className: classNames(styles.menuItem, styles.menuItemBordered, styles.menuItemDanger)}}
/>
);
export {
BorderedMenuItem,
DangerousMenuItem,
StyledContextMenu as ContextMenu,
StyledMenuItem as MenuItem
};
+2 -12
View File
@@ -23,8 +23,6 @@ import BackdropLibrary from '../../containers/backdrop-library.jsx';
import Watermark from '../../containers/watermark.jsx';
import Backpack from '../../containers/backpack.jsx';
import PreviewModal from '../../containers/preview-modal.jsx';
import ImportModal from '../../containers/import-modal.jsx';
import WebGlModal from '../../containers/webgl-modal.jsx';
import TipsLibrary from '../../containers/tips-library.jsx';
import Cards from '../../containers/cards.jsx';
@@ -80,7 +78,6 @@ const GUIComponent = props => {
costumeLibraryVisible,
costumesTabVisible,
enableCommunity,
importInfoVisible,
intl,
isCreating,
isFullScreen,
@@ -100,6 +97,7 @@ const GUIComponent = props => {
onActivateTab,
onClickLogo,
onExtensionButtonClick,
onProjectTelemetryEvent,
onRequestCloseBackdropLibrary,
onRequestCloseCostumeLibrary,
onRequestCloseTelemetryModal,
@@ -108,7 +106,6 @@ const GUIComponent = props => {
onTelemetryModalCancel,
onTelemetryModalOptIn,
onTelemetryModalOptOut,
previewInfoVisible,
showComingSoon,
soundsTabVisible,
stageSizeMode,
@@ -157,9 +154,6 @@ const GUIComponent = props => {
dir={isRtl ? 'rtl' : 'ltr'}
{...componentProps}
>
{previewInfoVisible ? (
<PreviewModal />
) : null}
{telemetryModalVisible ? (
<TelemetryModal
onCancel={onTelemetryModalCancel}
@@ -174,9 +168,6 @@ const GUIComponent = props => {
{isCreating ? (
<Loader messageId="gui.loader.creating" />
) : null}
{importInfoVisible ? (
<ImportModal />
) : null}
{isRendererSupported ? null : (
<WebGlModal isRtl={isRtl} />
)}
@@ -227,6 +218,7 @@ const GUIComponent = props => {
onCloseAccountNav={onCloseAccountNav}
onLogOut={onLogOut}
onOpenRegistration={onOpenRegistration}
onProjectTelemetryEvent={onProjectTelemetryEvent}
onSeeCommunity={onSeeCommunity}
onShare={onShare}
onToggleLoginOpen={onToggleLoginOpen}
@@ -379,7 +371,6 @@ GUIComponent.propTypes = {
costumeLibraryVisible: PropTypes.bool,
costumesTabVisible: PropTypes.bool,
enableCommunity: PropTypes.bool,
importInfoVisible: PropTypes.bool,
intl: intlShape.isRequired,
isCreating: PropTypes.bool,
isFullScreen: PropTypes.bool,
@@ -407,7 +398,6 @@ GUIComponent.propTypes = {
onTelemetryModalOptOut: PropTypes.func,
onToggleLoginOpen: PropTypes.func,
onUpdateProjectTitle: PropTypes.func,
previewInfoVisible: PropTypes.bool,
renderLogin: PropTypes.func,
showComingSoon: PropTypes.bool,
soundsTabVisible: PropTypes.bool,
@@ -1,190 +0,0 @@
@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: 500px;
color: $text-primary;
overflow: hidden;
}
/*
TODO figure out how to remove filter altogether
since it is null...
Modal header has 3 items:
|x title filter|
Use the same width for both side item containers,
so that title remains centered
*/
$sides: 20rem;
.header {
display: flex;
flex-direction: row;
flex-wrap: nowrap;
justify-content: flex-start;
height: $library-header-height;
box-sizing: border-box;
width: 100%;
background-color: $extensions-primary;
}
.header-item {
display: flex;
align-items: center;
padding: 1rem;
text-decoration: none;
color: white;
user-select: none;
}
.header-item-filter {
display: flex;
flex-basis: $sides;
justify-content: flex-end;
}
.header-item-title {
flex-grow: 1;
flex-shrink: 0;
justify-content: center;
user-select: none;
letter-spacing: 0.4px;
cursor: default;
}
.header-item-title h2 {
font-size: 1.25rem;
}
.header-item-close {
display: flex;
flex-basis: $sides;
justify-content: flex-start;
}
[dir="rtl"] .header-item-close img {
transform: scaleX(-1);
}
.body {
background: $ui-white;
padding: 1.5rem 2.25rem;
text-align: center;
}
.input-row {
margin: 1.5rem 0;
font-weight: bolder;
text-align: right;
display: flex;
justify-content: center;
border: 1px solid;
border-radius: 0.25rem;
overflow: hidden;
}
.ok-input-container {
border-color: $motion-primary;
box-shadow: 0 0 0 0.2rem $motion-transparent;
}
.bad-input-container {
border-color: $data-primary;
box-shadow: 0 0 0 0.2rem hsla(30, 100%, 55%, 0.15);
}
.input-row input {
width: 100%;
padding: 0 1rem;
height: 3rem;
color: $text-primary;
font-size: .875rem;
outline: none;
border: none;
}
.input-row input::placeholder {
font-style: italic;
color: $text-primary-transparent;
}
.input-row button {
padding: 0.5rem 2rem;
font-weight: bold;
font-size: .875rem;
cursor: pointer;
border: 0px solid $extensions-primary;
outline: none;
}
.input-row button.ok-button {
background: $extensions-primary;
color: white;
}
.error-row {
margin: 1.5rem 0;
text-align: center;
display: flex;
justify-content: center;
background: hsla(30, 100%, 55%, 0.25);
color: $data-primary;
border: 1px solid $data-primary;
border-radius: 0.25rem;
}
.error-row p {
font-size: 0.875rem;
font-weight: bold;
}
/* 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;
color: $motion-primary;
}
.faq-link-text {
margin: 2rem 0 .5rem 0;
font-size: .875rem;
color: $text-primary;
}
.faq-link {
color: $motion-primary;
text-decoration: none;
}
@@ -1,168 +0,0 @@
import PropTypes from 'prop-types';
import React from 'react';
import ReactModal from 'react-modal';
import Box from '../box/box.jsx';
import {defineMessages, injectIntl, intlShape, FormattedMessage} from 'react-intl';
import classNames from 'classnames';
import CloseButton from '../close-button/close-button.jsx';
import styles from './import-modal.css';
const messages = defineMessages({
title: {
id: 'gui.importInfo.title',
defaultMessage: 'View a Scratch 2.0 Project',
description: 'Scratch 2.0 import modal label - for accessibility'
},
formDescription: {
defaultMessage:
'Enter a link to one of your shared Scratch projects. Changes made in this 3.0 Beta will not be saved.',
description: 'Import project message',
id: 'gui.importInfo.betamessage'
},
previewFormDescription: {
defaultMessage:
'Enter a link to one of your shared Scratch projects. Changes made in this 3.0 Preview will not be saved.',
description: 'Import project message',
id: 'gui.importInfo.message'
},
invalidFormatError: {
id: 'gui.importInfo.invalidFormatError',
defaultMessage: 'Uh oh, that project link or id doesn\'t look quite right.',
description: 'Invalid project link or id message'
}
});
const ImportModal = ({intl, ...props}) => (
<ReactModal
isOpen
className={styles.modalContent}
contentLabel={intl.formatMessage({...messages.title})}
overlayClassName={styles.modalOverlay}
onRequestClose={props.onCancel}
>
<div dir={props.isRtl ? 'rtl' : 'ltr'} >
<Box>
<div className={styles.header}>
<div
className={classNames(
styles.headerItem,
styles.headerItemClose
)}
>
<CloseButton
buttonType="back"
size={CloseButton.SIZE_LARGE}
onClick={props.onGoBack}
/>
</div>
<div
className={classNames(
styles.headerItem,
styles.headerItemTitle
)}
>
<h2>
{intl.formatMessage({...messages.title})}
</h2>
</div>
<div className={classNames(styles.headerItem, styles.headerItemFilter)}>
{null}
</div>
</div>
</Box>
<Box className={styles.body}>
<p>
{intl.formatMessage({...messages.formDescription})}
</p>
<Box
className={classNames(styles.inputRow,
(props.hasValidationError ? styles.badInputContainer : styles.okInputContainer))
}
>
<input
autoFocus
placeholder={props.placeholder}
value={props.inputValue}
onChange={props.onChange}
onKeyPress={props.onKeyPress}
/>
<button
className={styles.okButton}
title={intl.formatMessage({
defaultMessage: 'View Project',
description: 'Tooltip for View button',
id: 'gui.importModal.viewprojecttooltip'
})}
onClick={props.onViewProject}
>
<FormattedMessage
defaultMessage="View"
description="Label for button to load a scratch 2.0 project"
id="gui.importModal.viewproject"
/>
</button>
</Box>
{props.hasValidationError ?
<Box className={styles.errorRow}>
<p>
<FormattedMessage
{...messages[`${props.errorMessage}`]}
/>
</p>
</Box> : null
}
<Box className={styles.buttonRow}>
<button
onClick={props.onGoBack}
>
<FormattedMessage
defaultMessage="Go Back"
description="Label for button to back out of importing a project"
id="gui.importInfo.goback"
/>
</button>
</Box>
<Box className={styles.faqLinkText}>
<FormattedMessage
defaultMessage="To learn more, go to the {previewFaqLink}."
description="Invitation to try 3.0 preview"
id="gui.importInfo.previewfaq"
values={{
previewFaqLink: (
<a
className={styles.faqLink}
href="//scratch.mit.edu/3faq"
>
<FormattedMessage
defaultMessage="FAQ"
description="link to Scratch 3.0 FAQ page"
id="gui.importInfo.previewfaqlinktext"
/>
</a>
)
}}
/>
</Box>
</Box>
</div>
</ReactModal>
);
ImportModal.propTypes = {
errorMessage: PropTypes.string.isRequired,
hasValidationError: PropTypes.bool.isRequired,
inputValue: PropTypes.string.isRequired,
intl: intlShape.isRequired,
isRtl: PropTypes.bool,
onCancel: PropTypes.func.isRequired,
onChange: PropTypes.func.isRequired,
onGoBack: PropTypes.func.isRequired,
onKeyPress: PropTypes.func.isRequired,
onViewProject: PropTypes.func.isRequired,
placeholder: PropTypes.string
};
export default injectIntl(ImportModal);
@@ -178,6 +178,10 @@
font-weight: bold;
}
.featured-extension-metadata-detail img{
margin-right: 0.25rem;
}
.coming-soon-text {
position: absolute;
background-color: $data-primary;
+16 -13
View File
@@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
import React from 'react';
import Box from '../box/box.jsx';
import ScratchImage from '../scratch-image/scratch-image.jsx';
import styles from './library-item.css';
import classNames from 'classnames';
@@ -35,10 +36,12 @@ class LibraryItemComponent extends React.PureComponent {
/>
</div>
) : null}
<img
className={styles.featuredImage}
src={this.props.iconURL}
/>
{this.props.iconSource ? (
<ScratchImage
className={styles.featuredImage}
imageSource={this.props.iconSource}
/>
) : null}
</div>
{this.props.insetIconURL ? (
<div className={styles.libraryItemInsetImageContainer}>
@@ -72,12 +75,12 @@ class LibraryItemComponent extends React.PureComponent {
<div
className={styles.featuredExtensionMetadataDetail}
>
<img
src={this.props.bluetoothRequired ?
bluetoothIconURL :
internetConnectionIconURL
}
/>
{this.props.bluetoothRequired ? (
<img src={bluetoothIconURL} />
) : null}
{this.props.internetConnectionRequired ? (
<img src={internetConnectionIconURL} />
) : null}
</div>
</div>
) : null}
@@ -121,9 +124,9 @@ class LibraryItemComponent extends React.PureComponent {
{/* Layers of wrapping is to prevent layout thrashing on animation */}
<Box className={styles.libraryItemImageContainerWrapper}>
<Box className={styles.libraryItemImageContainer}>
<img
<ScratchImage
className={styles.libraryItemImage}
src={this.props.iconURL}
imageSource={this.props.iconSource}
/>
</Box>
</Box>
@@ -146,7 +149,7 @@ LibraryItemComponent.propTypes = {
extensionId: PropTypes.string,
featured: PropTypes.bool,
hidden: PropTypes.bool,
iconURL: PropTypes.string,
iconSource: ScratchImage.ImageSourcePropType,
insetIconURL: PropTypes.string,
internetConnectionRequired: PropTypes.bool,
name: PropTypes.oneOfType([
+50 -9
View File
@@ -9,7 +9,7 @@ import Modal from '../../containers/modal.jsx';
import Divider from '../divider/divider.jsx';
import Filter from '../filter/filter.jsx';
import TagButton from '../../containers/tag-button.jsx';
import analytics from '../../lib/analytics';
import storage from '../../lib/storage';
import styles from './library.css';
@@ -29,6 +29,47 @@ const messages = defineMessages({
const ALL_TAG = {tag: 'all', intlLabel: messages.allTag};
const tagListPrefix = [ALL_TAG];
/**
* Find the AssetType which corresponds to a particular file extension. For example, 'png' => AssetType.ImageBitmap.
* @param {string} fileExtension - the file extension to look up.
* @returns {AssetType} - the AssetType corresponding to the extension, if any.
*/
const getAssetTypeForFileExtension = function (fileExtension) {
const compareOptions = {
sensitivity: 'accent',
usage: 'search'
};
for (const assetTypeId in storage.AssetType) {
const assetType = storage.AssetType[assetTypeId];
if (fileExtension.localeCompare(assetType.runtimeFormat, compareOptions) === 0) {
return assetType;
}
}
};
/**
* Figure out an `imageSource` (URI or asset ID & type) for a library item's icon.
* @param {object} item - either a library item or one of a library item's costumes.
* @returns {object} - an `imageSource` ready to be passed to a `ScratchImage`.
*/
const getItemImageSource = function (item) {
if (item.rawURL) {
return {
uri: item.rawURL
};
}
// TODO: adjust libraries to be more storage-friendly; don't use split() here.
const md5 = item.md5 || item.baseLayerMD5;
if (md5) {
const [assetId, fileExtension] = md5.split('.');
return {
assetId: assetId,
assetType: getAssetTypeForFileExtension(fileExtension)
};
}
};
class LibraryComponent extends React.Component {
constructor (props) {
super(props);
@@ -60,7 +101,6 @@ class LibraryComponent extends React.Component {
}
handleClose () {
this.props.onRequestClose();
analytics.pageview(`/${this.props.id}/search?q=${this.state.filterQuery}`);
}
handleTagClick (tag) {
this.setState({
@@ -164,8 +204,10 @@ class LibraryComponent extends React.Component {
})}
ref={this.setFilteredDataRef}
>
{this.getFilteredData().map((dataItem, index) => (
<LibraryItem
{this.getFilteredData().map((dataItem, index) => {
const iconSource = getItemImageSource(dataItem);
const icons = dataItem.json && dataItem.json.costumes.map(getItemImageSource);
return (<LibraryItem
bluetoothRequired={dataItem.bluetoothRequired}
collaborator={dataItem.collaborator}
description={dataItem.description}
@@ -173,9 +215,8 @@ class LibraryComponent extends React.Component {
extensionId={dataItem.extensionId}
featured={dataItem.featured}
hidden={dataItem.hidden}
iconMd5={dataItem.md5}
iconRawURL={dataItem.rawURL}
icons={dataItem.json && dataItem.json.costumes}
iconSource={iconSource}
icons={icons}
id={index}
insetIconURL={dataItem.insetIconURL}
internetConnectionRequired={dataItem.internetConnectionRequired}
@@ -184,8 +225,8 @@ class LibraryComponent extends React.Component {
onMouseEnter={this.handleMouseEnter}
onMouseLeave={this.handleMouseLeave}
onSelect={this.handleSelect}
/>
))}
/>);
})}
</div>
</Modal>
);
+3 -5
View File
@@ -120,15 +120,13 @@ class LoaderComponent extends React.Component {
constructor (props) {
super(props);
this.state = {
messageNumber: 0
messageNumber: this.chooseRandomMessage()
};
}
componentDidMount () {
this.chooseRandomMessage();
// Start an interval to choose a new message every 5 seconds
this.intervalId = setInterval(() => {
this.chooseRandomMessage();
this.setState({messageNumber: this.chooseRandomMessage()});
}, 5000);
}
componentWillUnmount () {
@@ -145,7 +143,7 @@ class LoaderComponent extends React.Component {
break;
}
}
this.setState({messageNumber});
return messageNumber;
}
render () {
return (
+1 -1
View File
@@ -5,7 +5,7 @@
color: $ui-white;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
display: flex;
justify-content: start;
justify-content: flex-start;
align-items: center;
cursor: default;
}
@@ -1,12 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="20px" height="20px" viewBox="0 0 20 20" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 48.1 (47250) - http://www.bohemiancoding.com/sketch -->
<title>Feedback</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Feedback" fill="#4C97FF">
<path d="M12.3811686,14.2770244 C12.3811686,14.3987659 12.3515563,14.506558 12.306433,14.6257633 L11.4998525,16.6002595 C11.2770558,16.478518 10.93299,16.3846755 10.4704753,16.3846755 C10.0234717,16.3846755 9.6949171,16.478518 9.47071028,16.58631 L8.66412979,14.6257633 C8.60490535,14.506558 8.59080429,14.3848164 8.59080429,14.2643429 C8.60490535,14.2503934 8.60490535,14.2237624 8.60490535,14.2098129 L8.60490535,14.1705005 C8.60490535,13.9828155 8.67964095,13.7938625 8.813601,13.6188589 C9.1576668,13.1775457 9.79926492,12.9086997 10.4859864,12.9086997 C11.1727079,12.9086997 11.8143061,13.1775457 12.1428607,13.6188589 C12.2472085,13.7684996 12.3360452,13.9409669 12.3670675,14.103289 C12.3811686,14.1565509 12.3811686,14.2098129 12.3811686,14.2643429 L12.3811686,14.2770244 Z M8.60490535,5.94153049 C8.60490535,5.50782611 8.89115681,5.12865181 9.33393002,4.89024122 C9.59056927,4.7520138 9.91771381,4.9143359 9.91771381,5.18445004 L9.91771381,11.6621167 C9.91771381,11.8168299 9.80490535,11.9550573 9.63851287,11.9918334 C9.46789007,12.0311458 9.3029077,12.0831396 9.14638596,12.1452786 C8.89115681,12.2467299 8.60490535,12.0844078 8.60490535,11.8345839 L8.60490535,5.94153049 Z M12.3670675,5.94153049 L12.3670675,11.8333158 C12.3670675,12.0780671 12.0892767,12.2505343 11.8410981,12.1516193 C11.6775258,12.0844078 11.5054929,12.032414 11.3278196,11.9931016 C11.1529665,11.9550573 11.0373378,11.8054166 11.0373378,11.6443627 L11.0373378,5.18191375 C11.0373378,4.9143359 11.3630722,4.75074565 11.6211216,4.88770493 C12.0709453,5.12611553 12.3670675,5.50528983 12.3670675,5.94153049 Z M10.4859864,1.48528137 C8.82911216,1.48528137 7.48528137,2.53149809 7.48528137,3.82119796 L7.48528137,14.2237624 C7.48528137,14.4786589 7.5148936,14.7335553 7.62065153,14.9757703 L9.1576668,18.7066425 C9.3522614,19.177123 9.87400053,19.4852814 10.4859864,19.4852814 C11.0979723,19.4852814 11.6197115,19.177123 11.8143061,18.7066425 L13.3513213,14.9757703 C13.4556692,14.7335553 13.4852814,14.4786589 13.4852814,14.2237624 L13.4852814,3.82119796 C13.4852814,2.53149809 12.1428607,1.48528137 10.4859864,1.48528137 Z" id="feedback" transform="translate(10.485281, 10.485281) rotate(45.000000) translate(-10.485281, -10.485281) "></path>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.7 KiB

+53 -61
View File
@@ -1,11 +1,14 @@
import classNames from 'classnames';
import {connect} from 'react-redux';
import {compose} from 'redux';
import {defineMessages, FormattedMessage, injectIntl, intlShape} from 'react-intl';
import PropTypes from 'prop-types';
import bindAll from 'lodash.bindall';
import bowser from 'bowser';
import React from 'react';
import VM from 'scratch-vm';
import Box from '../box/box.jsx';
import Button from '../button/button.jsx';
import CommunityButton from './community-button.jsx';
@@ -25,6 +28,7 @@ import LoginDropdown from './login-dropdown.jsx';
import SB3Downloader from '../../containers/sb3-downloader.jsx';
import DeletionRestorer from '../../containers/deletion-restorer.jsx';
import TurboMode from '../../containers/turbo-mode.jsx';
import MenuBarHOC from '../../containers/menu-bar-hoc.jsx';
import {openTipsLibrary} from '../../reducers/modals';
import {setPlayer} from '../../reducers/mode';
@@ -55,11 +59,12 @@ import {
loginMenuOpen
} from '../../reducers/menus';
import collectMetadata from '../../lib/collect-metadata';
import styles from './menu-bar.css';
import helpIcon from '../../lib/assets/icon--tutorials.svg';
import mystuffIcon from './icon--mystuff.png';
import feedbackIcon from './icon--feedback.svg';
import profileIcon from './icon--profile.png';
import remixIcon from './icon--remix.svg';
import dropdownCaret from './dropdown-caret.svg';
@@ -67,13 +72,8 @@ import languageIcon from '../language-selector/language-icon.svg';
import scratchLogo from './scratch-logo.svg';
const messages = defineMessages({
confirmNav: {
id: 'gui.menuBar.confirmNewWithoutSaving',
defaultMessage: 'Replace contents of the current project?',
description: 'message for prompting user to confirm that they want to create new project without saving'
}
});
import sharedMessages from '../../lib/shared-messages';
const ariaMessages = defineMessages({
language: {
id: 'gui.menuBar.LanguageSelector',
@@ -151,10 +151,10 @@ class MenuBar extends React.Component {
'handleClickSaveAsCopy',
'handleClickSeeCommunity',
'handleClickShare',
'handleCloseFileMenuAndThen',
'handleKeyPress',
'handleLanguageMouseUp',
'handleRestoreOption',
'handleSaveToComputer',
'restoreOptionMessage'
]);
}
@@ -165,17 +165,14 @@ class MenuBar extends React.Component {
document.removeEventListener('keydown', this.handleKeyPress);
}
handleClickNew () {
let readyToReplaceProject = true;
// if the project is dirty, and user owns the project, we will autosave.
// but if they are not logged in and can't save, user should consider
// downloading or logging in first.
// Note that if user is logged in and editing someone else's project,
// they'll lose their work.
if (this.props.projectChanged && !this.props.canCreateNew) {
readyToReplaceProject = confirm( // eslint-disable-line no-alert
this.props.intl.formatMessage(messages.confirmNav)
);
}
const readyToReplaceProject = this.props.confirmReadyToReplaceProject(
this.props.intl.formatMessage(sharedMessages.replaceProjectWarning)
);
this.props.onRequestCloseFile();
if (readyToReplaceProject) {
this.props.onClickNew(this.props.canSave && this.props.canCreateNew);
@@ -195,8 +192,8 @@ class MenuBar extends React.Component {
this.props.onRequestCloseFile();
}
handleClickSeeCommunity (waitForUpdate) {
if (this.props.canSave) { // save before transitioning to project page
this.props.autoUpdateProject();
if (this.props.shouldSaveBeforeTransition()) {
this.props.autoUpdateProject(); // save before transitioning to project page
waitForUpdate(true); // queue the transition to project page
} else {
waitForUpdate(false); // immediately transition to project page
@@ -221,12 +218,6 @@ class MenuBar extends React.Component {
this.props.onRequestCloseEdit();
};
}
handleCloseFileMenuAndThen (fn) {
return () => {
this.props.onRequestCloseFile();
fn();
};
}
handleKeyPress (event) {
const modifier = bowser.mac ? event.metaKey : event.ctrlKey;
if (modifier && event.key === 's') {
@@ -234,6 +225,16 @@ class MenuBar extends React.Component {
event.preventDefault();
}
}
handleSaveToComputer (downloadProjectCallback) {
return () => {
this.props.onRequestCloseFile();
downloadProjectCallback();
if (this.props.onProjectTelemetryEvent) {
const metadata = collectMetadata(this.props.vm, this.props.projectTitle, this.props.locale);
this.props.onProjectTelemetryEvent('projectDidSave', metadata);
}
};
}
handleLanguageMouseUp (e) {
if (!this.props.languageMenuOpen) {
this.props.onClickLanguage(e);
@@ -390,27 +391,25 @@ class MenuBar extends React.Component {
</MenuSection>
)}
<MenuSection>
<SBFileUploader onUpdateProjectTitle={this.props.onUpdateProjectTitle}>
<SBFileUploader
canSave={this.props.canSave}
userOwnsProject={this.props.userOwnsProject}
onUpdateProjectTitle={this.props.onUpdateProjectTitle}
>
{(className, renderFileInput, loadProject) => (
<MenuItem
className={className}
onClick={loadProject}
>
<FormattedMessage
defaultMessage="Load from your computer"
description={
'Menu bar item for uploading a project from your computer'
}
id="gui.menuBar.uploadFromComputer"
/>
{this.props.intl.formatMessage(sharedMessages.loadFromComputerTitle)}
{renderFileInput()}
</MenuItem>
)}
</SBFileUploader>
<SB3Downloader>{(className, downloadProject) => (
<SB3Downloader>{(className, downloadProjectCallback) => (
<MenuItem
className={className}
onClick={this.handleCloseFileMenuAndThen(downloadProject)}
onClick={this.handleSaveToComputer(downloadProjectCallback)}
>
<FormattedMessage
defaultMessage="Save to your computer"
@@ -643,25 +642,6 @@ class MenuBar extends React.Component {
) : (
// ******** no login session is available, so don't show login stuff
<React.Fragment>
<div className={classNames(styles.menuBarItem, styles.feedbackButtonWrapper)}>
<a
className={styles.feedbackLink}
href="https://scratch.mit.edu/discuss/topic/312261/"
rel="noopener noreferrer"
target="_blank"
>
<Button
className={styles.feedbackButton}
iconSrc={feedbackIcon}
>
<FormattedMessage
defaultMessage="Give Feedback"
description="Label for feedback form modal button"
id="gui.menuBar.giveFeedback"
/>
</Button>
</a>
</div>
{this.props.showComingSoon ? (
<React.Fragment>
<MenuBarItemTooltip id="mystuff">
@@ -725,6 +705,7 @@ MenuBar.propTypes = {
canSave: PropTypes.bool,
canShare: PropTypes.bool,
className: PropTypes.string,
confirmReadyToReplaceProject: PropTypes.func,
editMenuOpen: PropTypes.bool,
enableCommunity: PropTypes.bool,
fileMenuOpen: PropTypes.bool,
@@ -734,6 +715,7 @@ MenuBar.propTypes = {
isShowingProject: PropTypes.bool,
isUpdating: PropTypes.bool,
languageMenuOpen: PropTypes.bool,
locale: PropTypes.string.isRequired,
loginMenuOpen: PropTypes.bool,
onClickAccount: PropTypes.func,
onClickEdit: PropTypes.func,
@@ -748,6 +730,7 @@ MenuBar.propTypes = {
onLogOut: PropTypes.func,
onOpenRegistration: PropTypes.func,
onOpenTipLibrary: PropTypes.func,
onProjectTelemetryEvent: PropTypes.func,
onRequestCloseAccount: PropTypes.func,
onRequestCloseEdit: PropTypes.func,
onRequestCloseFile: PropTypes.func,
@@ -757,19 +740,21 @@ MenuBar.propTypes = {
onShare: PropTypes.func,
onToggleLoginOpen: PropTypes.func,
onUpdateProjectTitle: PropTypes.func,
projectChanged: PropTypes.bool,
projectTitle: PropTypes.string,
renderLogin: PropTypes.func,
sessionExists: PropTypes.bool,
shouldSaveBeforeTransition: PropTypes.func,
showComingSoon: PropTypes.bool,
username: PropTypes.string
userOwnsProject: PropTypes.bool,
username: PropTypes.string,
vm: PropTypes.instanceOf(VM).isRequired
};
MenuBar.defaultProps = {
onShare: () => {}
};
const mapStateToProps = state => {
const mapStateToProps = (state, ownProps) => {
const loadingState = state.scratchGui.projectState.loadingState;
const user = state.session && state.session.session && state.session.session.user;
return {
@@ -780,11 +765,14 @@ const mapStateToProps = state => {
isUpdating: getIsUpdating(loadingState),
isShowingProject: getIsShowingProject(loadingState),
languageMenuOpen: languageMenuOpen(state),
locale: state.locales.locale,
loginMenuOpen: loginMenuOpen(state),
projectChanged: state.scratchGui.projectChanged,
projectTitle: state.scratchGui.projectTitle,
sessionExists: state.session && typeof state.session.session !== 'undefined',
username: user ? user.username : null
username: user ? user.username : null,
userOwnsProject: ownProps.authorUsername && user &&
(ownProps.authorUsername === user.username),
vm: state.scratchGui.vm
};
};
@@ -808,7 +796,11 @@ const mapDispatchToProps = dispatch => ({
onSeeCommunity: () => dispatch(setPlayer(true))
});
export default injectIntl(connect(
mapStateToProps,
mapDispatchToProps
)(MenuBar));
export default compose(
injectIntl,
MenuBarHOC,
connect(
mapStateToProps,
mapDispatchToProps
)
)(MenuBar);
+4
View File
@@ -37,6 +37,10 @@
background-color: $ui-black-transparent;
}
.menu-item.hoverable {
cursor: pointer;
}
.menu-section {
border-top: 1px solid $ui-black-transparent;
}
+5 -1
View File
@@ -39,7 +39,11 @@ const MenuItem = ({
onClick
}) => (
<li
className={classNames(styles.menuItem, className)}
className={classNames(
styles.menuItem,
styles.hoverable,
className
)}
onClick={onClick}
>
{children}
@@ -27,6 +27,7 @@ const MonitorList = props => (
draggable={props.draggable}
height={monitorData.height}
id={monitorData.id}
isDiscrete={monitorData.isDiscrete}
key={monitorData.id}
max={monitorData.sliderMax}
min={monitorData.sliderMin}
+11 -2
View File
@@ -4,7 +4,7 @@ import PropTypes from 'prop-types';
import Draggable from 'react-draggable';
import {FormattedMessage} from 'react-intl';
import {ContextMenuTrigger} from 'react-contextmenu';
import {ContextMenu, MenuItem} from '../context-menu/context-menu.jsx';
import {BorderedMenuItem, ContextMenu, MenuItem} from '../context-menu/context-menu.jsx';
import Box from '../box/box.jsx';
import DefaultMonitor from './default-monitor.jsx';
import LargeMonitor from './large-monitor.jsx';
@@ -84,6 +84,14 @@ const MonitorComponent = props => (
id="gui.monitor.contextMenu.slider"
/>
</MenuItem>}
{props.onSliderPromptOpen && props.mode === 'slider' &&
<BorderedMenuItem onClick={props.onSliderPromptOpen}>
<FormattedMessage
defaultMessage="change slider range"
description="Menu item to change the slider range"
id="gui.monitor.contextMenu.sliderRange"
/>
</BorderedMenuItem>}
{props.onImport &&
<MenuItem onClick={props.onImport}>
<FormattedMessage
@@ -122,7 +130,8 @@ MonitorComponent.propTypes = {
onNextMode: PropTypes.func.isRequired,
onSetModeToDefault: PropTypes.func,
onSetModeToLarge: PropTypes.func,
onSetModeToSlider: PropTypes.func
onSetModeToSlider: PropTypes.func,
onSliderPromptOpen: PropTypes.func
};
MonitorComponent.defaultProps = {
+4 -1
View File
@@ -4,7 +4,7 @@ import classNames from 'classnames';
import styles from './monitor.css';
const SliderMonitor = ({categoryColor, label, min, max, value, onSliderUpdate}) => (
const SliderMonitor = ({categoryColor, isDiscrete, label, min, max, value, onSliderUpdate}) => (
<div className={styles.defaultMonitor}>
<div className={styles.row}>
<div className={styles.label}>
@@ -22,6 +22,7 @@ const SliderMonitor = ({categoryColor, label, min, max, value, onSliderUpdate})
className={classNames(styles.slider, 'no-drag')} // Class used on parent Draggable to prevent drags
max={max}
min={min}
step={isDiscrete ? 1 : 0.01}
type="range"
value={value}
onChange={onSliderUpdate}
@@ -33,6 +34,7 @@ const SliderMonitor = ({categoryColor, label, min, max, value, onSliderUpdate})
SliderMonitor.propTypes = {
categoryColor: PropTypes.string.isRequired,
isDiscrete: PropTypes.bool,
label: PropTypes.string.isRequired,
max: PropTypes.number,
min: PropTypes.number,
@@ -44,6 +46,7 @@ SliderMonitor.propTypes = {
};
SliderMonitor.defaultProps = {
isDiscrete: true,
min: 0,
max: 100
};
Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

@@ -1,108 +0,0 @@
@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: 500px;
color: $text-primary;
overflow: hidden;
}
.illustration {
width: 100%;
height: 208px;
background-color: $motion-primary;
background-image: url('./welcome.png');
background-size: cover;
}
.body {
background: $ui-white;
padding: 1.5rem 2.25rem;
text-align: center;
}
/* 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.ok-button {
background: $motion-primary;
color: white;
}
.button-row button.no-button {
color: $motion-primary;
}
.button-row button.view-project-button {
background: $extensions-primary;
border-color: $extensions-primary;
color: white;
}
[dir="ltr"] .button-row button + button {
margin-left: 0.5rem;
}
[dir="rtl"] .button-row button + button {
margin-right: 0.5rem;
}
.cat-icon {
width: 1.5rem;
height: 1.5rem;
vertical-align: middle;
}
[dir="ltr"] .cat-icon {
margin-left: .125rem;
}
[dir="rtl"] .cat-icon {
margin-right: .125rem;
}
.faq-link-text {
margin: 2rem 0 .5rem 0;
font-size: .875rem;
color: $text-primary;
}
.faq-link {
color: $motion-primary;
text-decoration: none;
}
@@ -1,144 +0,0 @@
import PropTypes from 'prop-types';
import React from 'react';
import ReactModal from 'react-modal';
import Box from '../box/box.jsx';
import {defineMessages, injectIntl, intlShape, FormattedMessage} from 'react-intl';
import styles from './preview-modal.css';
import catIcon from './happy-cat.png';
const messages = defineMessages({
label: {
id: 'gui.previewInfo.label',
defaultMessage: 'Try Scratch 3.0',
description: 'Scratch 3.0 modal label - for accessibility'
},
previewWelcome: {
defaultMessage: 'Welcome to the Scratch 3.0 Beta',
description: 'Header for Preview Info Modal',
id: 'gui.previewInfo.welcome'
},
notNowTooltip: {
defaultMessage: 'Not Now',
description: 'Tooltip for Not Now button',
id: 'gui.previewModal.notnowtooltip'
},
tryItTooltip: {
defaultMessage: 'Try It',
description: 'Tooltip for Try It button',
id: 'gui.previewModal.tryittooltip'
},
viewProjectTooltip: {
defaultMessage: 'View 2.0 Project',
description: 'Tooltip for View 2.0 Project button',
id: 'gui.previewModal.viewprojecttooltip'
}
});
const PreviewModal = ({intl, ...props}) => (
<ReactModal
isOpen
className={styles.modalContent}
contentLabel={intl.formatMessage({...messages.label})}
overlayClassName={styles.modalOverlay}
onRequestClose={props.onTryIt}
>
<div dir={props.isRtl ? 'rtl' : 'ltr'} >
<Box className={styles.illustration} />
<Box className={styles.body}>
<h2>
<FormattedMessage
defaultMessage="Welcome to the Scratch 3.0 Beta"
description="Header for Beta Info Modal"
id="gui.previewInfo.betawelcome"
/>
</h2>
<p>
{ /* eslint-disable max-len */ }
<FormattedMessage
defaultMessage="We're working on the next generation of Scratch. We're excited for you to try it!"
description="Invitation to try 3.0 Beta"
id="gui.previewInfo.invitation"
/>
{ /* eslint-enable max-len */ }
</p>
<Box className={styles.buttonRow}>
<button
className={styles.noButton}
title={intl.formatMessage(messages.notNowTooltip)}
onClick={props.onCancel}
>
<FormattedMessage
defaultMessage="Not Now"
description="Label for button to back out of trying Scratch 3.0 Beta"
id="gui.previewInfo.notnow"
/>
</button>
<button
className={styles.okButton}
title={intl.formatMessage(messages.tryItTooltip)}
onClick={props.onTryIt}
>
<FormattedMessage
defaultMessage="Try It! {caticon}"
description="Label for button to try Scratch 3.0 Beta"
id="gui.previewModal.tryit"
values={{
caticon: (
<img
className={styles.catIcon}
src={catIcon}
/>
)
}}
/>
</button>
<button
className={styles.viewProjectButton}
title={intl.formatMessage(messages.viewProjectTooltip)}
onClick={props.onViewProject}
>
<FormattedMessage
defaultMessage="View 2.0 Project"
description="Label for button to import a 2.0 project"
id="gui.previewModal.viewproject"
/>
</button>
</Box>
<Box className={styles.faqLinkText}>
<FormattedMessage
defaultMessage="To learn more, go to the {previewFaqLink}."
description="Invitation to try 3.0 Beta"
id="gui.previewInfo.previewfaq"
values={{
previewFaqLink: (
<a
className={styles.faqLink}
href="//scratch.mit.edu/3faq"
>
<FormattedMessage
defaultMessage="FAQ"
description="link to Scratch 3.0 FAQ page"
id="gui.previewInfo.previewfaqlinktext"
/>
</a>
)
}}
/>
</Box>
</Box>
</div>
</ReactModal>
);
PreviewModal.propTypes = {
intl: intlShape.isRequired,
isRtl: PropTypes.bool,
onCancel: PropTypes.func.isRequired,
onTryIt: PropTypes.func.isRequired,
onViewProject: PropTypes.func.isRequired
};
export default injectIntl(PreviewModal);
Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 KiB

@@ -0,0 +1,139 @@
import PropTypes from 'prop-types';
import React from 'react';
import VisibilitySensor from 'react-visibility-sensor';
import storage from '../../lib/storage';
class ScratchImage extends React.PureComponent {
static init () {
this._maxParallelism = 6;
this._currentJobs = 0;
this._pendingImages = new Set();
}
static loadPendingImages () {
if (this._currentJobs >= this._maxParallelism) {
// already busy
return;
}
// Find the first visible image. If there aren't any, find the first non-visible image.
let nextImage;
for (const image of this._pendingImages) {
if (image.isVisible) {
nextImage = image;
break;
} else {
nextImage = nextImage || image;
}
}
// If we found an image to load:
// 1) Remove it from the queue
// 2) Load the image
// 3) Pump the queue again
if (nextImage) {
this._pendingImages.delete(nextImage);
const imageSource = nextImage.props.imageSource;
++this._currentJobs;
storage
.load(imageSource.assetType, imageSource.assetId)
.then(asset => {
if (!nextImage.wasUnmounted) {
const dataURI = asset.encodeDataURI();
nextImage.setState({
imageURI: dataURI
});
}
--this._currentJobs;
this.loadPendingImages();
});
}
}
constructor (props) {
super(props);
this.state = {};
Object.assign(this.state, this._loadImageSource(props.imageSource));
}
componentWillReceiveProps (nextProps) {
const newState = this._loadImageSource(nextProps.imageSource);
this.setState(newState);
}
componentWillUnmount () {
this.wasUnmounted = true;
ScratchImage._pendingImages.delete(this);
}
/**
* Calculate the state changes necessary to load the image specified in the provided source info. If the component
* is mounted, call setState() with the return value of this function. If the component has not yet mounted, use
* the return value of this function as initial state for the component.
*
* @param {object} imageSource - the new source for the image, including either assetId or URI
* @returns {object} - the new state values, if any.
*/
_loadImageSource (imageSource) {
if (imageSource) {
if (imageSource.uri) {
ScratchImage._pendingImages.delete(this);
return {
imageURI: imageSource.uri,
lastRequestedAsset: null
};
}
if (this.state.lastRequestedAsset !== imageSource.assetId) {
ScratchImage._pendingImages.add(this);
return {
lastRequestedAsset: imageSource.assetId
};
}
}
// Nothing to do - don't change any state.
return {};
}
render () {
const {
src: _src,
imageSource: _imageSource,
...imgProps
} = this.props;
return (
<VisibilitySensor
intervalCheck
scrollCheck
>
{
({isVisible}) => {
this.isVisible = isVisible;
ScratchImage.loadPendingImages();
return (
<img
src={this.state.imageURI}
{...imgProps}
/>
);
}
}
</VisibilitySensor>
);
}
}
ScratchImage.ImageSourcePropType = PropTypes.oneOfType([
PropTypes.shape({
assetId: PropTypes.string.isRequired,
assetType: PropTypes.oneOf(Object.values(storage.AssetType)).isRequired
}),
PropTypes.shape({
uri: PropTypes.string.isRequired
})
]);
ScratchImage.propTypes = {
imageSource: ScratchImage.ImageSourcePropType.isRequired
};
ScratchImage.init();
export default ScratchImage;
@@ -0,0 +1,55 @@
@import "../../css/colors.css";
@import "../../css/units.css";
.modal-content {
width: 360px;
}
.body {
background: $ui-white;
padding: 1.5rem 2.25rem;
}
.label {
font-weight: 500;
margin: 0 0 0.75rem;
}
.min-input, .max-input {
margin-bottom: 1.5rem;
width: 100%;
border: 1px solid $ui-black-transparent;
border-radius: 5px;
padding: 0 1rem;
height: 3rem;
color: $text-primary-transparent;
font-size: .875rem;
}
.button-row {
font-weight: bolder;
text-align: right;
}
.button-row button {
padding: 0.75rem 1rem;
border-radius: 0.25rem;
background: white;
border: 1px solid $ui-black-transparent;
font-weight: 600;
font-size: 0.85rem;
}
.button-row button.ok-button {
background: $motion-primary;
border: $motion-primary;
color: white;
}
[dir="ltr"] .button-row button + button {
margin-left: 0.5rem;
}
[dir="rtl"] .button-row button + button {
margin-right: 0.5rem;
}
@@ -0,0 +1,102 @@
import {defineMessages, FormattedMessage, intlShape, injectIntl} from 'react-intl';
import PropTypes from 'prop-types';
import React from 'react';
import Box from '../box/box.jsx';
import Modal from '../../containers/modal.jsx';
import styles from './slider-prompt.css';
const messages = defineMessages({
minValue: {
defaultMessage: 'Minimum value',
description: 'Label of slider modal',
id: 'gui.sliderModal.min'
},
maxValue: {
defaultMessage: 'Maximum value',
description: 'Label of slider modal',
id: 'gui.sliderModal.max'
},
title: {
defaultMessage: 'Change slider range',
description: 'Title of slider modal',
id: 'gui.sliderModal.title'
}
});
const SliderPromptComponent = props => (
<Modal
className={styles.modalContent}
contentLabel={props.intl.formatMessage(messages.title)}
id="sliderPrompt"
onRequestClose={props.onCancel}
>
<Box className={styles.body}>
<Box className={styles.label}>
{props.intl.formatMessage(messages.minValue)}
</Box>
<Box>
<input
className={styles.minInput}
name={props.intl.formatMessage(messages.minValue)}
pattern="-?[0-9]*(\.[0-9]+)?"
type="text"
value={props.minValue}
onChange={props.onChangeMin}
onKeyPress={props.onKeyPress}
/>
</Box>
<Box className={styles.label}>
{props.intl.formatMessage(messages.maxValue)}
</Box>
<Box>
<input
className={styles.maxInput}
name={props.intl.formatMessage(messages.maxValue)}
pattern="-?[0-9]*(\.[0-9]+)?"
type="text"
value={props.maxValue}
onChange={props.onChangeMax}
onKeyPress={props.onKeyPress}
/>
</Box>
<Box className={styles.buttonRow}>
<button
className={styles.cancelButton}
onClick={props.onCancel}
>
<FormattedMessage
defaultMessage="Cancel"
description="Button in prompt for cancelling the dialog"
id="gui.sliderPrompt.cancel"
/>
</button>
<button
className={styles.okButton}
onClick={props.onOk}
>
<FormattedMessage
defaultMessage="OK"
description="Button in prompt for confirming the dialog"
id="gui.sliderPrompt.ok"
/>
</button>
</Box>
</Box>
</Modal>
);
SliderPromptComponent.propTypes = {
intl: intlShape,
maxValue: PropTypes.string,
minValue: PropTypes.string,
onCancel: PropTypes.func.isRequired,
onChangeMax: PropTypes.func.isRequired,
onChangeMin: PropTypes.func.isRequired,
onKeyPress: PropTypes.func.isRequired,
onOk: PropTypes.func.isRequired
};
export default injectIntl(SliderPromptComponent);
@@ -5,7 +5,7 @@ import React from 'react';
import CloseButton from '../close-button/close-button.jsx';
import styles from './sprite-selector-item.css';
import {ContextMenuTrigger} from 'react-contextmenu';
import {BorderedMenuItem, ContextMenu, MenuItem} from '../context-menu/context-menu.jsx';
import {DangerousMenuItem, ContextMenu, MenuItem} from '../context-menu/context-menu.jsx';
import {FormattedMessage} from 'react-intl';
// react-contextmenu requires unique id to match trigger and context menu
@@ -74,13 +74,13 @@ const SpriteSelectorItem = props => (
</MenuItem>
) : null }
{props.onDeleteButtonClick ? (
<BorderedMenuItem onClick={props.onDeleteButtonClick}>
<DangerousMenuItem onClick={props.onDeleteButtonClick}>
<FormattedMessage
defaultMessage="delete"
description="Menu item to delete in the right click menu"
id="gui.spriteSelectorItem.contextMenuDelete"
/>
</BorderedMenuItem>
</DangerousMenuItem>
) : null }
</ContextMenu>
) : null}
@@ -120,9 +120,10 @@ const SpriteSelectorComponent = function (props) {
title: intl.formatMessage(messages.addSpriteFromFile),
img: fileUploadIcon,
onClick: onFileUploadClick,
fileAccept: '.svg, .png, .jpg, .jpeg, .sprite2, .sprite3',
fileAccept: '.svg, .png, .jpg, .jpeg, .sprite2, .sprite3, .gif',
fileChange: onSpriteUpload,
fileInput: spriteFileInput
fileInput: spriteFileInput,
fileMultiple: true
}, {
title: intl.formatMessage(messages.addSpriteFromSurprise),
img: surpriseIcon,
@@ -102,9 +102,10 @@ const StageSelector = props => {
title: intl.formatMessage(messages.addBackdropFromFile),
img: fileUploadIcon,
onClick: onBackdropFileUploadClick,
fileAccept: '.svg, .png, .jpg, .jpeg', // Bitmap coming soon
fileAccept: '.svg, .png, .jpg, .jpeg, .gif',
fileChange: onBackdropFileUpload,
fileInput: fileInputRef
fileInput: fileInputRef,
fileMultiple: true
}, {
title: intl.formatMessage(messages.addBackdropFromSurprise),
img: surpriseIcon,
+1
View File
@@ -128,6 +128,7 @@ to adjust for the border using a different method */
}
.question-wrapper {
z-index: $z-index-stage-question;
pointer-events: auto;
}
+2 -2
View File
@@ -67,7 +67,7 @@ class AutoScanningStep extends React.Component {
render () {
return (
<ScanningStepComponent
peripheralButtonImage={this.props.peripheralButtonImage}
connectionTipIconURL={this.props.connectionTipIconURL}
phase={this.state.phase}
title={this.props.extensionId}
onRefresh={this.handleRefresh}
@@ -78,9 +78,9 @@ class AutoScanningStep extends React.Component {
}
AutoScanningStep.propTypes = {
connectionTipIconURL: PropTypes.string,
extensionId: PropTypes.string.isRequired,
onConnecting: PropTypes.func.isRequired,
peripheralButtonImage: PropTypes.string,
vm: PropTypes.instanceOf(VM).isRequired
};
-6
View File
@@ -5,7 +5,6 @@ import {defineMessages, injectIntl, intlShape} from 'react-intl';
import {connect} from 'react-redux';
import VM from 'scratch-vm';
import analytics from '../lib/analytics';
import backdropLibraryContent from '../lib/libraries/backdrops.json';
import backdropTags from '../lib/libraries/backdrop-tags';
import LibraryComponent from '../components/library/library.jsx';
@@ -36,11 +35,6 @@ class BackdropLibrary extends React.Component {
};
this.props.vm.setEditingTarget(this.props.stageID);
this.props.vm.addBackdrop(item.md5, vmBackdrop);
analytics.event({
category: 'library',
action: 'Select Backdrop',
label: item.name
});
}
render () {
return (
+16
View File
@@ -83,12 +83,15 @@ class Backpack extends React.Component {
}
handleDrop (dragInfo) {
let payloader = null;
let presaveAsset = null;
switch (dragInfo.dragType) {
case DragConstants.COSTUME:
payloader = costumePayload;
presaveAsset = dragInfo.payload.asset;
break;
case DragConstants.SOUND:
payloader = soundPayload;
presaveAsset = dragInfo.payload.asset;
break;
case DragConstants.SPRITE:
payloader = spritePayload;
@@ -102,6 +105,19 @@ class Backpack extends React.Component {
// Creating the payload is async, so set loading before starting
this.setState({loading: true}, () => {
payloader(dragInfo.payload, this.props.vm)
.then(payload => {
// Force the asset to save to the asset server before storing in backpack
// Ensures any asset present in the backpack is also on the asset server
if (presaveAsset && !presaveAsset.clean) {
return storage.store(
presaveAsset.assetType,
presaveAsset.dataFormat,
presaveAsset.data,
presaveAsset.assetId
).then(() => payload);
}
return payload;
})
.then(payload => saveBackpackObject({
host: this.props.host,
token: this.props.token,
+16
View File
@@ -96,6 +96,21 @@ class Blocks extends React.Component {
);
this.workspace = this.ScratchBlocks.inject(this.blocks, workspaceConfig);
// Register buttons under new callback keys for creating variables,
// lists, and procedures from extensions.
const toolboxWorkspace = this.workspace.getFlyout().getWorkspace();
const varListButtonCallback = type =>
(() => this.ScratchBlocks.Variables.createVariable(this.workspace, null, type));
const procButtonCallback = () => {
this.ScratchBlocks.Procedures.createProcedureDefCallback_(this.workspace);
};
toolboxWorkspace.registerButtonCallback('MAKE_A_VARIABLE', varListButtonCallback(''));
toolboxWorkspace.registerButtonCallback('MAKE_A_LIST', varListButtonCallback('list'));
toolboxWorkspace.registerButtonCallback('MAKE_A_PROCEDURE', procButtonCallback);
// Store the xml of the toolbox that is actually rendered.
// This is used in componentDidUpdate instead of prevProps, because
// the xml can change while e.g. on the costumes tab.
@@ -162,6 +177,7 @@ class Blocks extends React.Component {
this.setLocale();
} else {
this.props.vm.refreshWorkspace();
this.requestToolboxUpdate();
}
window.dispatchEvent(new Event('resize'));
+29 -1
View File
@@ -1,8 +1,11 @@
import {connect} from 'react-redux';
import PropTypes from 'prop-types';
import React from 'react';
import {
activateDeck,
closeCards,
shrinkExpandCards,
nextStep,
prevStep,
dragCard,
@@ -15,12 +18,36 @@ import {
} from '../reducers/modals';
import CardsComponent from '../components/cards/cards.jsx';
import {loadImageData} from '../lib/libraries/decks/translate-image.js';
class Cards extends React.Component {
componentDidMount () {
if (this.props.locale !== 'en') {
loadImageData(this.props.locale);
}
}
componentDidUpdate (prevProps) {
if (this.props.locale !== prevProps.locale) {
loadImageData(this.props.locale);
}
}
render () {
return (
<CardsComponent {...this.props} />
);
}
}
Cards.propTypes = {
locale: PropTypes.string.isRequired
};
const mapStateToProps = state => ({
visible: state.scratchGui.cards.visible,
content: state.scratchGui.cards.content,
activeDeckId: state.scratchGui.cards.activeDeckId,
step: state.scratchGui.cards.step,
expanded: state.scratchGui.cards.expanded,
x: state.scratchGui.cards.x,
y: state.scratchGui.cards.y,
isRtl: state.locales.isRtl,
@@ -35,6 +62,7 @@ const mapDispatchToProps = dispatch => ({
dispatch(closeCards());
},
onCloseCards: () => dispatch(closeCards()),
onShrinkExpandCards: () => dispatch(shrinkExpandCards()),
onNextStep: () => dispatch(nextStep()),
onPrevStep: () => dispatch(prevStep()),
onDrag: (e_, data) => dispatch(dragCard(data.x, data.y)),
@@ -45,4 +73,4 @@ const mapDispatchToProps = dispatch => ({
export default connect(
mapStateToProps,
mapDispatchToProps
)(CardsComponent);
)(Cards);
+6 -6
View File
@@ -107,15 +107,15 @@ class ConnectionModal extends React.Component {
render () {
return (
<ConnectionModalComponent
connectingMessage={this.state.extension.connectingMessage}
connectingMessage={this.state.extension && this.state.extension.connectingMessage}
connectionIconURL={this.state.extension && this.state.extension.connectionIconURL}
connectionSmallIconURL={this.state.extension && this.state.extension.connectionSmallIconURL}
connectionTipIconURL={this.state.extension && this.state.extension.connectionTipIconURL}
extensionId={this.props.extensionId}
name={this.state.extension.name}
peripheralButtonImage={this.state.extension.peripheralButtonImage}
peripheralImage={this.state.extension.peripheralImage}
name={this.state.extension && this.state.extension.name}
phase={this.state.phase}
smallPeripheralImage={this.state.extension.smallPeripheralImage}
title={this.props.extensionId}
useAutoScan={this.state.extension.useAutoScan}
useAutoScan={this.state.extension && this.state.extension.useAutoScan}
vm={this.props.vm}
onCancel={this.handleCancel}
onConnected={this.handleConnected}
-9
View File
@@ -4,7 +4,6 @@ import React from 'react';
import VM from 'scratch-vm';
import {connect} from 'react-redux';
import analytics from '../lib/analytics';
import ControlsComponent from '../components/controls/controls.jsx';
class Controls extends React.Component {
@@ -24,19 +23,11 @@ class Controls extends React.Component {
this.props.vm.start();
}
this.props.vm.greenFlag();
analytics.event({
category: 'general',
action: 'Green Flag'
});
}
}
handleStopAllClick (e) {
e.preventDefault();
this.props.vm.stopAll();
analytics.event({
category: 'general',
action: 'Stop Button'
});
}
render () {
const {
-6
View File
@@ -4,7 +4,6 @@ import React from 'react';
import {defineMessages, injectIntl, intlShape} from 'react-intl';
import VM from 'scratch-vm';
import analytics from '../lib/analytics';
import costumeLibraryContent from '../lib/libraries/costumes.json';
import spriteTags from '../lib/libraries/sprite-tags';
import LibraryComponent from '../components/library/library.jsx';
@@ -38,11 +37,6 @@ class CostumeLibrary extends React.PureComponent {
skinId: null
};
this.props.vm.addCostumeFromLibrary(item.md5, vmCostume);
analytics.event({
category: 'library',
action: 'Select Costume',
label: item.name
});
}
render () {
return (
+34 -13
View File
@@ -28,6 +28,7 @@ import {
} from '../reducers/editor-tab';
import {setRestore} from '../reducers/restore-deletion';
import {showStandardAlert, closeAlertWithId} from '../reducers/alerts';
import addLibraryBackdropIcon from '../components/asset-panel/icon--add-backdrop-lib.svg';
import addLibraryCostumeIcon from '../components/asset-panel/icon--add-costume-lib.svg';
@@ -158,11 +159,14 @@ class CostumeTab extends React.Component {
downloadBlob(`${item.name}.${item.asset.dataFormat}`, blob);
}
handleNewCostume (costume, fromCostumeLibrary) {
if (fromCostumeLibrary) {
this.props.vm.addCostumeFromLibrary(costume.md5, costume);
} else {
this.props.vm.addCostume(costume.md5, costume);
}
const costumes = Array.isArray(costume) ? costume : [costume];
return Promise.all(costumes.map(c => {
if (fromCostumeLibrary) {
return this.props.vm.addCostumeFromLibrary(c.md5, c);
}
return this.props.vm.addCostume(c.md5, c);
}));
}
handleNewBlankCostume () {
const name = this.props.vm.editingTarget.isStage ?
@@ -200,14 +204,26 @@ class CostumeTab extends React.Component {
}
handleCostumeUpload (e) {
const storage = this.props.vm.runtime.storage;
handleFileUpload(e.target, (buffer, fileType, fileName) => {
costumeUpload(buffer, fileType, fileName, storage, this.handleNewCostume);
});
this.props.onShowImporting();
handleFileUpload(e.target, (buffer, fileType, fileName, fileIndex, fileCount) => {
costumeUpload(buffer, fileType, storage, vmCostumes => {
vmCostumes.forEach((costume, i) => {
costume.name = `${fileName}${i ? i + 1 : ''}`;
});
this.handleNewCostume(vmCostumes).then(() => {
if (fileIndex === fileCount - 1) {
this.props.onCloseImporting();
}
});
}, this.props.onCloseImporting);
}, this.props.onCloseImporting);
}
handleCameraBuffer (buffer) {
const storage = this.props.vm.runtime.storage;
const name = this.props.intl.formatMessage(messages.costume, {index: 1});
costumeUpload(buffer, 'image/png', name, storage, this.handleNewCostume);
costumeUpload(buffer, 'image/png', storage, vmCostumes => {
vmCostumes[0].name = this.props.intl.formatMessage(messages.costume, {index: 1});
this.handleNewCostume(vmCostumes);
});
}
handleFileUploadClick () {
this.fileInput.click();
@@ -291,9 +307,10 @@ class CostumeTab extends React.Component {
title: intl.formatMessage(addFileMessage),
img: fileUploadIcon,
onClick: this.handleFileUploadClick,
fileAccept: '.svg, .png, .jpg, .jpeg',
fileAccept: '.svg, .png, .jpg, .jpeg, .gif',
fileChange: this.handleCostumeUpload,
fileInput: this.setFileInput
fileInput: this.setFileInput,
fileMultiple: true
},
{
title: intl.formatMessage(messages.addSurpriseCostumeMsg),
@@ -346,10 +363,12 @@ CostumeTab.propTypes = {
intl: intlShape,
isRtl: PropTypes.bool,
onActivateSoundsTab: PropTypes.func.isRequired,
onCloseImporting: PropTypes.func.isRequired,
onNewCostumeFromCameraClick: PropTypes.func.isRequired,
onNewLibraryBackdropClick: PropTypes.func.isRequired,
onNewLibraryCostumeClick: PropTypes.func.isRequired,
onRequestCloseCameraModal: PropTypes.func.isRequired,
onShowImporting: PropTypes.func.isRequired,
sprites: PropTypes.shape({
id: PropTypes.shape({
costumes: PropTypes.arrayOf(PropTypes.shape({
@@ -394,7 +413,9 @@ const mapDispatchToProps = dispatch => ({
},
dispatchUpdateRestore: restoreState => {
dispatch(setRestore(restoreState));
}
},
onCloseImporting: () => dispatch(closeAlertWithId('importingAsset')),
onShowImporting: () => dispatch(showStandardAlert('importingAsset'))
});
export default errorBoundaryHOC('Costume Tab')(
-13
View File
@@ -6,7 +6,6 @@ import {defineMessages, injectIntl, intlShape} from 'react-intl';
import extensionLibraryContent from '../lib/libraries/extensions/index.jsx';
import analytics from '../lib/analytics';
import LibraryComponent from '../components/library/library.jsx';
import extensionIcon from '../components/action-menu/icon--sprite.svg';
@@ -46,18 +45,6 @@ class ExtensionLibrary extends React.PureComponent {
});
}
}
let gaLabel = '';
if (typeof (item.name) === 'string') {
gaLabel = item.name;
} else {
// Name is localized, get the default message for the gaLabel
gaLabel = item.name.props.defaultMessage;
}
analytics.event({
category: 'library',
action: 'Select Extension',
label: gaLabel
});
}
render () {
const extensionLibraryThumbnailData = extensionLibraryContent.map(extension => ({
-4
View File
@@ -119,7 +119,6 @@ GUI.propTypes = {
cloudHost: PropTypes.string,
error: PropTypes.oneOfType([PropTypes.object, PropTypes.string]),
fetchingProject: PropTypes.bool,
importInfoVisible: PropTypes.bool,
intl: intlShape,
isError: PropTypes.bool,
isLoading: PropTypes.bool,
@@ -132,7 +131,6 @@ GUI.propTypes = {
onUpdateProjectId: PropTypes.func,
onUpdateProjectTitle: PropTypes.func,
onUpdateReduxProjectTitle: PropTypes.func,
previewInfoVisible: PropTypes.bool,
projectHost: PropTypes.string,
projectId: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
projectTitle: PropTypes.string,
@@ -159,14 +157,12 @@ const mapStateToProps = state => {
costumeLibraryVisible: state.scratchGui.modals.costumeLibrary,
costumesTabVisible: state.scratchGui.editorTab.activeTabIndex === COSTUMES_TAB_INDEX,
error: state.scratchGui.projectState.error,
importInfoVisible: state.scratchGui.modals.importInfo,
isError: getIsError(loadingState),
isFullScreen: state.scratchGui.mode.isFullScreen,
isPlayerOnly: state.scratchGui.mode.isPlayerOnly,
isRtl: state.locales.isRtl,
isShowingProject: getIsShowingProject(loadingState),
loadingStateVisible: state.scratchGui.modals.loadingProject,
previewInfoVisible: state.scratchGui.modals.previewInfo,
projectId: state.scratchGui.projectState.projectId,
soundsTabVisible: state.scratchGui.editorTab.activeTabIndex === SOUNDS_TAB_INDEX,
targetIsStage: (
-115
View File
@@ -1,115 +0,0 @@
import bindAll from 'lodash.bindall';
import PropTypes from 'prop-types';
import React from 'react';
import {connect} from 'react-redux';
import ImportModalComponent from '../components/import-modal/import-modal.jsx';
import {
closeImportInfo,
openPreviewInfo
} from '../reducers/modals';
class ImportModal extends React.Component {
constructor (props) {
super(props);
bindAll(this, [
'handleViewProject',
'handleCancel',
'handleChange',
'handleGoBack',
'handleKeyPress'
]);
this.state = {
inputValue: '',
hasValidationError: false,
errorMessage: ''
};
}
handleKeyPress (event) {
if (event.key === 'Enter') this.handleViewProject();
}
handleViewProject () {
const inputValue = this.state.inputValue;
const projectId = this.validate(inputValue);
if (projectId) {
const projectLink = document.createElement('a');
document.body.appendChild(projectLink);
projectLink.href = `#${projectId}`;
projectLink.click();
document.body.removeChild(projectLink);
this.props.onViewProject();
} else {
this.setState({
hasValidationError: true,
errorMessage: `invalidFormatError`});
}
}
handleChange (e) {
this.setState({inputValue: e.target.value, hasValidationError: false});
}
validate (input) {
const urlMatches = input.match(/^(?:https?:\/\/)?scratch\.mit\.edu\/projects\/(\d+)/);
if (urlMatches && urlMatches.length > 0) {
return urlMatches[1];
}
const projectIdMatches = input.match(/^#?(\d+)$/);
if (projectIdMatches && projectIdMatches.length > 0) {
return projectIdMatches[1];
}
return null;
}
handleCancel () {
this.props.onCancel();
}
handleGoBack () {
this.props.onBack();
}
render () {
return (
<ImportModalComponent
errorMessage={this.state.errorMessage}
hasValidationError={this.state.hasValidationError}
inputValue={this.state.inputValue}
isRtl={this.props.isRtl}
placeholder="scratch.mit.edu/projects/123456789"
onCancel={this.handleCancel}
onChange={this.handleChange}
onGoBack={this.handleGoBack}
onKeyPress={this.handleKeyPress}
onViewProject={this.handleViewProject}
/>
);
}
}
ImportModal.propTypes = {
isRtl: PropTypes.bool,
onBack: PropTypes.func.isRequired,
onCancel: PropTypes.func.isRequired,
onViewProject: PropTypes.func
};
const mapStateToProps = state => ({
isRtl: state.locales.isRtl
});
const mapDispatchToProps = dispatch => ({
onBack: () => {
dispatch(closeImportInfo());
dispatch(openPreviewInfo());
},
onCancel: () => {
dispatch(closeImportInfo());
},
onViewProject: () => {
dispatch(closeImportInfo());
}
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(ImportModal);
+8 -18
View File
@@ -75,21 +75,17 @@ class LibraryItem extends React.PureComponent {
const nextIconIndex = (this.state.iconIndex + 1) % this.props.icons.length;
this.setState({iconIndex: nextIconIndex});
}
curIconMd5 () {
curIconSource () {
if (this.props.icons &&
this.state.isRotatingIcon &&
this.state.iconIndex < this.props.icons.length &&
this.props.icons[this.state.iconIndex] &&
this.props.icons[this.state.iconIndex].baseLayerMD5) {
return this.props.icons[this.state.iconIndex].baseLayerMD5;
this.props.icons[this.state.iconIndex]) {
return this.props.icons[this.state.iconIndex];
}
return this.props.iconMd5;
return this.props.iconSource;
}
render () {
const iconMd5 = this.curIconMd5();
const iconURL = iconMd5 ?
`https://cdn.assets.scratch.mit.edu/internalapi/asset/${iconMd5}/get/` :
this.props.iconRawURL;
const iconSource = this.curIconSource();
return (
<LibraryItemComponent
bluetoothRequired={this.props.bluetoothRequired}
@@ -99,8 +95,7 @@ class LibraryItem extends React.PureComponent {
extensionId={this.props.extensionId}
featured={this.props.featured}
hidden={this.props.hidden}
iconURL={iconURL}
icons={this.props.icons}
iconSource={iconSource}
id={this.props.id}
insetIconURL={this.props.insetIconURL}
internetConnectionRequired={this.props.internetConnectionRequired}
@@ -127,13 +122,8 @@ LibraryItem.propTypes = {
extensionId: PropTypes.string,
featured: PropTypes.bool,
hidden: PropTypes.bool,
iconMd5: PropTypes.string,
iconRawURL: PropTypes.string,
icons: PropTypes.arrayOf(
PropTypes.shape({
baseLayerMD5: PropTypes.string
})
),
iconSource: LibraryItemComponent.propTypes.iconSource, // single icon
icons: PropTypes.arrayOf(LibraryItemComponent.propTypes.iconSource), // rotating icons
id: PropTypes.number.isRequired,
insetIconURL: PropTypes.string,
internetConnectionRequired: PropTypes.bool,
+66
View File
@@ -0,0 +1,66 @@
import {connect} from 'react-redux';
import PropTypes from 'prop-types';
import bindAll from 'lodash.bindall';
import React from 'react';
const MenuBarHOC = function (WrappedComponent) {
class MenuBarContainer extends React.PureComponent {
constructor (props) {
super(props);
bindAll(this, [
'confirmReadyToReplaceProject',
'shouldSaveBeforeTransition'
]);
}
confirmReadyToReplaceProject (message) {
let readyToReplaceProject = true;
if (this.props.projectChanged && !this.props.canCreateNew) {
readyToReplaceProject = this.props.confirmWithMessage(message);
}
return readyToReplaceProject;
}
shouldSaveBeforeTransition () {
return (this.props.canSave && this.props.projectChanged);
}
render () {
const {
/* eslint-disable no-unused-vars */
projectChanged,
/* eslint-enable no-unused-vars */
...props
} = this.props;
return (<WrappedComponent
confirmReadyToReplaceProject={this.confirmReadyToReplaceProject}
shouldSaveBeforeTransition={this.shouldSaveBeforeTransition}
{...props}
/>);
}
}
MenuBarContainer.propTypes = {
canCreateNew: PropTypes.bool,
canSave: PropTypes.bool,
confirmWithMessage: PropTypes.func,
projectChanged: PropTypes.bool
};
MenuBarContainer.defaultProps = {
// default to using standard js confirm
confirmWithMessage: message => (confirm(message)) // eslint-disable-line no-alert
};
const mapStateToProps = state => ({
projectChanged: state.scratchGui.projectChanged
});
const mapDispatchToProps = () => ({});
// Allow incoming props to override redux-provided props. Used to mock in tests.
const mergeProps = (stateProps, dispatchProps, ownProps) => Object.assign(
{}, stateProps, dispatchProps, ownProps
);
return connect(
mapStateToProps,
mapDispatchToProps,
mergeProps
)(MenuBarContainer);
};
export default MenuBarHOC;
+54 -18
View File
@@ -9,6 +9,7 @@ import {addMonitorRect, getInitialPosition, resizeMonitorRect, removeMonitorRect
import {getVariable, setVariableValue} from '../lib/variable-utils';
import importCSV from '../lib/import-csv';
import downloadBlob from '../lib/download-blob';
import SliderPrompt from './slider-prompt.jsx';
import {connect} from 'react-redux';
import {Map} from 'immutable';
@@ -42,10 +43,16 @@ class Monitor extends React.Component {
'handleSetModeToDefault',
'handleSetModeToLarge',
'handleSetModeToSlider',
'handleSliderPromptClose',
'handleSliderPromptOk',
'handleSliderPromptOpen',
'handleImport',
'handleExport',
'setElement'
]);
this.state = {
sliderPrompt: false
};
}
componentDidMount () {
let rect;
@@ -135,6 +142,23 @@ class Monitor extends React.Component {
mode: 'slider'
}));
}
handleSliderPromptClose () {
this.setState({sliderPrompt: false});
}
handleSliderPromptOpen () {
this.setState({sliderPrompt: true});
}
handleSliderPromptOk (min, max, isDiscrete) {
const realMin = Math.min(min, max);
const realMax = Math.max(min, max);
this.props.vm.runtime.requestUpdateMonitor(Map({
id: this.props.id,
sliderMin: realMin,
sliderMax: realMax,
isDiscrete: isDiscrete
}));
this.handleSliderPromptClose();
}
setElement (monitorElt) {
this.element = monitorElt;
}
@@ -164,24 +188,35 @@ class Monitor extends React.Component {
const showSliderOption = availableModes(this.props.opcode).indexOf('slider') !== -1;
const isList = this.props.mode === 'list';
return (
<MonitorComponent
componentRef={this.setElement}
{...monitorProps}
draggable={this.props.draggable}
height={this.props.height}
max={this.props.max}
min={this.props.min}
mode={this.props.mode}
targetId={this.props.targetId}
width={this.props.width}
onDragEnd={this.handleDragEnd}
onExport={isList ? this.handleExport : null}
onImport={isList ? this.handleImport : null}
onNextMode={this.handleNextMode}
onSetModeToDefault={isList ? null : this.handleSetModeToDefault}
onSetModeToLarge={isList ? null : this.handleSetModeToLarge}
onSetModeToSlider={showSliderOption ? this.handleSetModeToSlider : null}
/>
<React.Fragment>
{this.state.sliderPrompt && <SliderPrompt
isDiscrete={this.props.isDiscrete}
maxValue={parseFloat(this.props.max)}
minValue={parseFloat(this.props.min)}
onCancel={this.handleSliderPromptClose}
onOk={this.handleSliderPromptOk}
/>}
<MonitorComponent
componentRef={this.setElement}
{...monitorProps}
draggable={this.props.draggable}
height={this.props.height}
isDiscrete={this.props.isDiscrete}
max={this.props.max}
min={this.props.min}
mode={this.props.mode}
targetId={this.props.targetId}
width={this.props.width}
onDragEnd={this.handleDragEnd}
onExport={isList ? this.handleExport : null}
onImport={isList ? this.handleImport : null}
onNextMode={this.handleNextMode}
onSetModeToDefault={isList ? null : this.handleSetModeToDefault}
onSetModeToLarge={isList ? null : this.handleSetModeToLarge}
onSetModeToSlider={showSliderOption ? this.handleSetModeToSlider : null}
onSliderPromptOpen={this.handleSliderPromptOpen}
/>
</React.Fragment>
);
}
}
@@ -192,6 +227,7 @@ Monitor.propTypes = {
height: PropTypes.number,
id: PropTypes.string.isRequired,
intl: intlShape,
isDiscrete: PropTypes.bool,
max: PropTypes.number,
min: PropTypes.number,
mode: PropTypes.oneOf(['default', 'slider', 'large', 'list']),
-76
View File
@@ -1,76 +0,0 @@
import bindAll from 'lodash.bindall';
import PropTypes from 'prop-types';
import React from 'react';
import {connect} from 'react-redux';
import tabletFullScreen from '../lib/tablet-full-screen';
import PreviewModalComponent from '../components/preview-modal/preview-modal.jsx';
import {
closePreviewInfo,
openImportInfo
} from '../reducers/modals';
class PreviewModal extends React.Component {
constructor (props) {
super(props);
bindAll(this, [
'handleTryIt',
'handleCancel',
'handleViewProject'
]);
this.state = {
previewing: false
};
}
handleTryIt () {
this.setState({previewing: true});
// try to run in fullscreen mode on tablets.
tabletFullScreen();
this.props.onTryIt();
}
handleCancel () {
window.location.replace('https://scratch.mit.edu');
}
handleViewProject () {
this.props.onViewProject();
}
render () {
return (
<PreviewModalComponent
isRtl={this.props.isRtl}
previewing={this.state.previewing}
onCancel={this.handleCancel}
onTryIt={this.handleTryIt}
onViewProject={this.handleViewProject}
/>
);
}
}
PreviewModal.propTypes = {
isRtl: PropTypes.bool,
onTryIt: PropTypes.func,
onViewProject: PropTypes.func
};
const mapStateToProps = state => ({
isRtl: state.locales.isRtl
});
const mapDispatchToProps = dispatch => ({
onTryIt: () => {
dispatch(closePreviewInfo());
},
onViewProject: () => {
dispatch(closePreviewInfo());
dispatch(openImportInfo());
}
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(PreviewModal);
+33 -9
View File
@@ -4,11 +4,13 @@ import React from 'react';
import {connect} from 'react-redux';
import {defineMessages, injectIntl, intlShape} from 'react-intl';
import analytics from '../lib/analytics';
import log from '../lib/log';
import sharedMessages from '../lib/shared-messages';
import {
LoadingStates,
getIsLoadingUpload,
getIsShowingWithoutId,
onLoadedProject,
requestProjectUpload
} from '../reducers/project-state';
@@ -88,10 +90,32 @@ class SBFileUploader extends React.Component {
}
// called when user has finished selecting a file to upload
handleChange (e) {
const {
intl,
isShowingWithoutId,
loadingState,
projectChanged,
userOwnsProject
} = this.props;
const thisFileInput = e.target;
if (thisFileInput.files) { // Don't attempt to load if no file was selected
this.fileToUpload = thisFileInput.files[0];
this.props.requestProjectUpload(this.props.loadingState);
// If user owns the project, or user has changed the project,
// we must confirm with the user that they really intend to replace it.
// (If they don't own the project and haven't changed it, no need to confirm.)
let uploadAllowed = true;
if (userOwnsProject || (projectChanged && isShowingWithoutId)) {
uploadAllowed = confirm( // eslint-disable-line no-alert
intl.formatMessage(sharedMessages.replaceProjectWarning)
);
}
if (uploadAllowed) {
this.props.requestProjectUpload(loadingState);
} else {
this.props.closeFileMenu();
}
}
}
// called when file upload raw data is available in the reader
@@ -101,13 +125,6 @@ class SBFileUploader extends React.Component {
const filename = this.fileToUpload && this.fileToUpload.name;
this.props.vm.loadProject(this.reader.result)
.then(() => {
analytics.event({
category: 'project',
action: 'Import Project File',
nonInteraction: true
});
// Remove the hash if any (without triggering a hash change event or a reload)
history.replaceState({}, document.title, '.');
this.props.onLoadingFinished(this.props.loadingState, true);
// Reset the file input after project is loaded
// This is necessary in case the user wants to reload a project
@@ -154,13 +171,17 @@ SBFileUploader.propTypes = {
canSave: PropTypes.bool, // eslint-disable-line react/no-unused-prop-types
children: PropTypes.func,
className: PropTypes.string,
closeFileMenu: PropTypes.func,
intl: intlShape.isRequired,
isLoadingUpload: PropTypes.bool,
isShowingWithoutId: PropTypes.bool,
loadingState: PropTypes.oneOf(LoadingStates),
onLoadingFinished: PropTypes.func,
onLoadingStarted: PropTypes.func,
onUpdateProjectTitle: PropTypes.func,
projectChanged: PropTypes.bool,
requestProjectUpload: PropTypes.func,
userOwnsProject: PropTypes.bool,
vm: PropTypes.shape({
loadProject: PropTypes.func
})
@@ -172,12 +193,15 @@ const mapStateToProps = state => {
const loadingState = state.scratchGui.projectState.loadingState;
return {
isLoadingUpload: getIsLoadingUpload(loadingState),
isShowingWithoutId: getIsShowingWithoutId(loadingState),
loadingState: loadingState,
projectChanged: state.scratchGui.projectChanged,
vm: state.scratchGui.vm
};
};
const mapDispatchToProps = (dispatch, ownProps) => ({
closeFileMenu: () => dispatch(closeFileMenu()),
onLoadingFinished: (loadingState, success) => {
dispatch(onLoadedProject(loadingState, ownProps.canSave, success));
dispatch(closeLoadingProject());
+2 -2
View File
@@ -54,10 +54,10 @@ class ScanningStep extends React.Component {
render () {
return (
<ScanningStepComponent
connectionSmallIconURL={this.props.connectionSmallIconURL}
peripheralList={this.state.peripheralList}
phase={this.state.phase}
scanning={this.state.scanning}
smallPeripheralImage={this.props.smallPeripheralImage}
title={this.props.extensionId}
onConnected={this.props.onConnected}
onConnecting={this.props.onConnecting}
@@ -68,10 +68,10 @@ class ScanningStep extends React.Component {
}
ScanningStep.propTypes = {
connectionSmallIconURL: PropTypes.string,
extensionId: PropTypes.string.isRequired,
onConnected: PropTypes.func.isRequired,
onConnecting: PropTypes.func.isRequired,
smallPeripheralImage: PropTypes.string,
vm: PropTypes.instanceOf(VM).isRequired
};
+85
View File
@@ -0,0 +1,85 @@
import PropTypes from 'prop-types';
import React from 'react';
import bindAll from 'lodash.bindall';
import SliderPromptComponent from '../components/slider-prompt/slider-prompt.jsx';
class SliderPrompt extends React.Component {
constructor (props) {
super(props);
bindAll(this, [
'handleOk',
'handleCancel',
'handleChangeMin',
'handleChangeMax',
'handleKeyPress',
'validates',
'shouldBeDiscrete'
]);
const {isDiscrete, minValue, maxValue} = this.props;
this.state = {
// For internal use, convert values to strings based on isDiscrete
// This is because `<input />` always returns values as strings.
minValue: isDiscrete ? minValue.toFixed(0) : minValue.toFixed(2),
maxValue: isDiscrete ? maxValue.toFixed(0) : maxValue.toFixed(2)
};
}
handleKeyPress (event) {
if (event.key === 'Enter') this.handleOk();
}
handleOk () {
const {minValue, maxValue} = this.state;
if (!this.validates(minValue, maxValue)) {
this.props.onCancel();
return;
}
this.props.onOk(
parseFloat(minValue),
parseFloat(maxValue),
this.shouldBeDiscrete(minValue, maxValue));
}
handleCancel () {
this.props.onCancel();
}
handleChangeMin (e) {
this.setState({minValue: e.target.value});
}
handleChangeMax (e) {
this.setState({maxValue: e.target.value});
}
shouldBeDiscrete (min, max) {
return min.indexOf('.') + max.indexOf('.') === -2; // Both -1
}
validates (min, max) {
return isFinite(min) && isFinite(max);
}
render () {
return (
<SliderPromptComponent
maxValue={this.state.maxValue}
minValue={this.state.minValue}
onCancel={this.handleCancel}
onChangeMax={this.handleChangeMax}
onChangeMin={this.handleChangeMin}
onKeyPress={this.handleKeyPress}
onOk={this.handleOk}
/>
);
}
}
SliderPrompt.propTypes = {
isDiscrete: PropTypes.bool,
maxValue: PropTypes.number,
minValue: PropTypes.number,
onCancel: PropTypes.func.isRequired,
onOk: PropTypes.func.isRequired
};
SliderPrompt.defaultProps = {
maxValue: 100,
minValue: 0,
isDiscrete: true
};
export default SliderPrompt;
-6
View File
@@ -5,7 +5,6 @@ import {defineMessages, injectIntl, intlShape} from 'react-intl';
import VM from 'scratch-vm';
import AudioEngine from 'scratch-audio';
import analytics from '../lib/analytics';
import LibraryComponent from '../components/library/library.jsx';
import soundIcon from '../components/asset-panel/icon--sound.svg';
@@ -125,11 +124,6 @@ class SoundLibrary extends React.PureComponent {
this.props.vm.addSound(vmSound).then(() => {
this.props.onNewSound();
});
analytics.event({
category: 'library',
action: 'Select Sound',
label: soundItem.name
});
}
render () {
// @todo need to use this hack to avoid library using md5 for image
+20 -8
View File
@@ -37,6 +37,7 @@ import {
} from '../reducers/editor-tab';
import {setRestore} from '../reducers/restore-deletion';
import {showStandardAlert, closeAlertWithId} from '../reducers/alerts';
class SoundTab extends React.Component {
constructor (props) {
@@ -129,12 +130,18 @@ class SoundTab extends React.Component {
handleSoundUpload (e) {
const storage = this.props.vm.runtime.storage;
const handleSound = newSound => this.props.vm.addSound(newSound)
.then(() => this.handleNewSound());
handleFileUpload(e.target, (buffer, fileType, fileName) => {
soundUpload(buffer, fileType, fileName, storage, handleSound);
});
this.props.onShowImporting();
handleFileUpload(e.target, (buffer, fileType, fileName, fileIndex, fileCount) => {
soundUpload(buffer, fileType, storage, newSound => {
newSound.name = fileName;
this.props.vm.addSound(newSound).then(() => {
this.handleNewSound();
if (fileIndex === fileCount - 1) {
this.props.onCloseImporting();
}
});
});
}, this.props.onCloseImporting);
}
handleDrop (dropInfo) {
@@ -223,7 +230,8 @@ class SoundTab extends React.Component {
onClick: this.handleFileUploadClick,
fileAccept: '.wav, .mp3',
fileChange: this.handleSoundUpload,
fileInput: this.setFileInput
fileInput: this.setFileInput,
fileMultiple: true
}, {
title: intl.formatMessage(messages.surpriseSound),
img: surpriseIcon,
@@ -273,9 +281,11 @@ SoundTab.propTypes = {
intl: intlShape,
isRtl: PropTypes.bool,
onActivateCostumesTab: PropTypes.func.isRequired,
onCloseImporting: PropTypes.func.isRequired,
onNewSoundFromLibraryClick: PropTypes.func.isRequired,
onNewSoundFromRecordingClick: PropTypes.func.isRequired,
onRequestCloseSoundLibrary: PropTypes.func.isRequired,
onShowImporting: PropTypes.func.isRequired,
soundLibraryVisible: PropTypes.bool,
soundRecorderVisible: PropTypes.bool,
sprites: PropTypes.shape({
@@ -316,7 +326,9 @@ const mapDispatchToProps = dispatch => ({
},
dispatchUpdateRestore: restoreState => {
dispatch(setRestore(restoreState));
}
},
onCloseImporting: () => dispatch(closeAlertWithId('importingAsset')),
onShowImporting: () => dispatch(showStandardAlert('importingAsset'))
});
export default errorBoundaryHOC('Sound Tab')(
-6
View File
@@ -4,7 +4,6 @@ import React from 'react';
import {injectIntl, intlShape, defineMessages} from 'react-intl';
import VM from 'scratch-vm';
import analytics from '../lib/analytics';
import spriteLibraryContent from '../lib/libraries/sprites.json';
import randomizeSpritePosition from '../lib/randomize-sprite-position';
import spriteTags from '../lib/libraries/sprite-tags';
@@ -32,11 +31,6 @@ class SpriteLibrary extends React.PureComponent {
this.props.vm.addSprite(JSON.stringify(item.json)).then(() => {
this.props.onActivateBlocksTab();
});
analytics.event({
category: 'library',
action: 'Select Sprite',
label: item.name
});
}
render () {
return (
+29 -9
View File
@@ -7,6 +7,7 @@ import {intlShape, injectIntl} from 'react-intl';
import {connect} from 'react-redux';
import {openBackdropLibrary} from '../reducers/modals';
import {activateTab, COSTUMES_TAB_INDEX} from '../reducers/editor-tab';
import {showStandardAlert, closeAlertWithId} from '../reducers/alerts';
import {setHoveredSprite} from '../reducers/hovered-target';
import DragConstants from '../lib/drag-constants';
import DropAreaHOC from '../lib/drop-area-hoc.jsx';
@@ -63,9 +64,13 @@ class StageSelector extends React.Component {
handleClick () {
this.props.onSelect(this.props.id);
}
handleNewBackdrop (backdrop) {
this.props.vm.addBackdrop(backdrop.md5, backdrop).then(() =>
this.props.onActivateTab(COSTUMES_TAB_INDEX));
handleNewBackdrop (backdrops_) {
const backdrops = Array.isArray(backdrops_) ? backdrops_ : [backdrops_];
return Promise.all(backdrops.map(backdrop =>
this.props.vm.addBackdrop(backdrop.md5, backdrop)
)).then(() =>
this.props.onActivateTab(COSTUMES_TAB_INDEX)
);
}
handleSurpriseBackdrop () {
// @todo should this not add a backdrop you already have?
@@ -77,9 +82,19 @@ class StageSelector extends React.Component {
}
handleBackdropUpload (e) {
const storage = this.props.vm.runtime.storage;
handleFileUpload(e.target, (buffer, fileType, fileName) => {
costumeUpload(buffer, fileType, fileName, storage, this.handleNewBackdrop);
});
this.props.onShowImporting();
handleFileUpload(e.target, (buffer, fileType, fileName, fileIndex, fileCount) => {
costumeUpload(buffer, fileType, storage, vmCostumes => {
vmCostumes.forEach((costume, i) => {
costume.name = `${fileName}${i ? i + 1 : ''}`;
});
this.handleNewBackdrop(vmCostumes).then(() => {
if (fileIndex === fileCount - 1) {
this.props.onCloseImporting();
}
});
}, this.props.onCloseImporting);
}, this.props.onCloseImporting);
}
handleFileUploadClick () {
this.fileInput.click();
@@ -117,7 +132,8 @@ class StageSelector extends React.Component {
}
render () {
const componentProps = omit(this.props, [
'asset', 'dispatchSetHoveredSprite', 'id', 'intl', 'onActivateTab', 'onSelect']);
'asset', 'dispatchSetHoveredSprite', 'id', 'intl',
'onActivateTab', 'onSelect', 'onShowImporting', 'onCloseImporting']);
return (
<DroppableThrottledStage
fileInputRef={this.setFileInput}
@@ -138,7 +154,9 @@ StageSelector.propTypes = {
...StageSelectorComponent.propTypes,
id: PropTypes.string,
intl: intlShape.isRequired,
onSelect: PropTypes.func
onCloseImporting: PropTypes.func,
onSelect: PropTypes.func,
onShowImporting: PropTypes.func
};
const mapStateToProps = (state, {asset, id}) => ({
@@ -159,7 +177,9 @@ const mapDispatchToProps = dispatch => ({
},
dispatchSetHoveredSprite: spriteId => {
dispatch(setHoveredSprite(spriteId));
}
},
onCloseImporting: () => dispatch(closeAlertWithId('importingAsset')),
onShowImporting: () => dispatch(showStandardAlert('importingAsset'))
});
export default injectIntl(connect(
+58 -37
View File
@@ -112,7 +112,9 @@ class Stage extends React.Component {
}
startColorPickingLoop () {
this.intervalId = setInterval(() => {
this.setState({colorInfo: this.getColorInfo(this.pickX, this.pickY)});
if (typeof this.pickX === 'number') {
this.setState({colorInfo: this.getColorInfo(this.pickX, this.pickY)});
}
}, 30);
}
stopColorPickingLoop () {
@@ -175,9 +177,11 @@ class Stage extends React.Component {
const {x, y} = getEventXY(e);
const mousePosition = [x - this.rect.left, y - this.rect.top];
// Set the pickX/Y for the color picker loop to pick up
this.pickX = mousePosition[0];
this.pickY = mousePosition[1];
if (this.props.isColorPicking) {
// Set the pickX/Y for the color picker loop to pick up
this.pickX = mousePosition[0];
this.pickY = mousePosition[1];
}
if (this.state.mouseDown && !this.state.isDragging) {
const distanceFromMouseDown = Math.sqrt(
@@ -231,38 +235,11 @@ class Stage extends React.Component {
this.onStopDrag(mousePosition[0], mousePosition[1]);
}
this.props.vm.postIOData('mouse', data);
}
onMouseDown (e) {
this.updateRect();
const {x, y} = getEventXY(e);
const mousePosition = [x - this.rect.left, y - this.rect.top];
if (e.button === 0 || (window.TouchEvent && e instanceof TouchEvent)) {
this.setState({
mouseDown: true,
mouseDownPosition: mousePosition,
mouseDownTimeoutId: setTimeout(
this.onStartDrag.bind(this, mousePosition[0], mousePosition[1]),
400
)
});
}
const data = {
isDown: true,
x: mousePosition[0],
y: mousePosition[1],
canvasWidth: this.rect.width,
canvasHeight: this.rect.height
};
this.props.vm.postIOData('mouse', data);
if (e.preventDefault) {
// Prevent default to prevent touch from dragging page
e.preventDefault();
// But we do want any active input to be blurred
if (document.activeElement && document.activeElement.blur) {
document.activeElement.blur();
}
}
if (this.props.isColorPicking) {
if (this.props.isColorPicking &&
mousePosition[0] > 0 && mousePosition[0] < this.rect.width &&
mousePosition[1] > 0 && mousePosition[1] < this.rect.height
) {
const {r, g, b} = this.state.colorInfo.color;
const componentToString = c => {
const hex = c.toString(16);
@@ -271,6 +248,47 @@ class Stage extends React.Component {
const colorString = `#${componentToString(r)}${componentToString(g)}${componentToString(b)}`;
this.props.onDeactivateColorPicker(colorString);
this.setState({colorInfo: null});
this.pickX = null;
this.pickY = null;
}
}
onMouseDown (e) {
this.updateRect();
const {x, y} = getEventXY(e);
const mousePosition = [x - this.rect.left, y - this.rect.top];
if (this.props.isColorPicking) {
// Set the pickX/Y for the color picker loop to pick up
this.pickX = mousePosition[0];
this.pickY = mousePosition[1];
// Immediately update the color picker info
this.setState({colorInfo: this.getColorInfo(this.pickX, this.pickY)});
} else {
if (e.button === 0 || (window.TouchEvent && e instanceof TouchEvent)) {
this.setState({
mouseDown: true,
mouseDownPosition: mousePosition,
mouseDownTimeoutId: setTimeout(
this.onStartDrag.bind(this, mousePosition[0], mousePosition[1]),
400
)
});
}
const data = {
isDown: true,
x: mousePosition[0],
y: mousePosition[1],
canvasWidth: this.rect.width,
canvasHeight: this.rect.height
};
this.props.vm.postIOData('mouse', data);
if (e.preventDefault) {
// Prevent default to prevent touch from dragging page
e.preventDefault();
// But we do want any active input to be blurred
if (document.activeElement && document.activeElement.blur) {
document.activeElement.blur();
}
}
}
}
onWheel (e) {
@@ -320,7 +338,6 @@ class Stage extends React.Component {
if (this.state.dragId) return;
const drawableId = this.renderer.pick(x, y);
if (drawableId === null) return;
const drawableData = this.renderer.extractDrawable(drawableId, x, y);
const targetId = this.props.vm.getTargetIdForDrawableId(drawableId);
if (targetId === null) return;
@@ -332,6 +349,9 @@ class Stage extends React.Component {
// Dragging always brings the target to the front
target.goToFront();
// Extract the drawable art
const drawableData = this.renderer.extractDrawable(drawableId, x, y);
this.props.vm.startDrag(targetId);
this.setState({
isDragging: true,
@@ -403,6 +423,7 @@ class Stage extends React.Component {
Stage.propTypes = {
isColorPicking: PropTypes.bool,
isFullScreen: PropTypes.bool.isRequired,
isStarted: PropTypes.bool,
micIndicator: PropTypes.bool,
onActivateColorPicker: PropTypes.func,
onDeactivateColorPicker: PropTypes.func,
+20 -8
View File
@@ -1,6 +1,6 @@
import bindAll from 'lodash.bindall';
import React from 'react';
import PropTypes from 'prop-types';
import {connect} from 'react-redux';
import {intlShape, injectIntl} from 'react-intl';
@@ -8,9 +8,9 @@ import {
openSpriteLibrary,
closeSpriteLibrary
} from '../reducers/modals';
import {activateTab, COSTUMES_TAB_INDEX, BLOCKS_TAB_INDEX} from '../reducers/editor-tab';
import {setReceivedBlocks} from '../reducers/hovered-target';
import {showStandardAlert, closeAlertWithId} from '../reducers/alerts';
import {setRestore} from '../reducers/restore-deletion';
import DragConstants from '../lib/drag-constants';
import TargetPaneComponent from '../components/target-pane/target-pane.jsx';
@@ -127,7 +127,7 @@ class TargetPane extends React.Component {
this.props.onActivateTab(BLOCKS_TAB_INDEX);
}
handleNewSprite (spriteJSONString) {
this.props.vm.addSprite(spriteJSONString)
return this.props.vm.addSprite(spriteJSONString)
.then(this.handleActivateBlocksTab);
}
handleFileUploadClick () {
@@ -135,10 +135,16 @@ class TargetPane extends React.Component {
}
handleSpriteUpload (e) {
const storage = this.props.vm.runtime.storage;
const costumeSuffix = this.props.intl.formatMessage(sharedMessages.costume, {index: 1});
handleFileUpload(e.target, (buffer, fileType, fileName) => {
spriteUpload(buffer, fileType, fileName, storage, this.handleNewSprite, costumeSuffix);
});
this.props.onShowImporting();
handleFileUpload(e.target, (buffer, fileType, fileName, fileIndex, fileCount) => {
spriteUpload(buffer, fileType, fileName, storage, newSprite => {
this.handleNewSprite(newSprite).then(() => {
if (fileIndex === fileCount - 1) {
this.props.onCloseImporting();
}
});
}, this.props.onCloseImporting);
}, this.props.onCloseImporting);
}
setFileInput (input) {
this.fileInput = input;
@@ -197,6 +203,8 @@ class TargetPane extends React.Component {
onReceivedBlocks, // eslint-disable-line no-unused-vars
onHighlightTarget, // eslint-disable-line no-unused-vars
dispatchUpdateRestore, // eslint-disable-line no-unused-vars
onShowImporting, // eslint-disable-line no-unused-vars
onCloseImporting, // eslint-disable-line no-unused-vars
...componentProps
} = this.props;
return (
@@ -233,6 +241,8 @@ const {
TargetPane.propTypes = {
intl: intlShape.isRequired,
onCloseImporting: PropTypes.func,
onShowImporting: PropTypes.func,
...targetPaneProps
};
@@ -264,7 +274,9 @@ const mapDispatchToProps = dispatch => ({
},
onHighlightTarget: id => {
dispatch(highlightTarget(id));
}
},
onCloseImporting: () => dispatch(closeAlertWithId('importingAsset')),
onShowImporting: () => dispatch(showStandardAlert('importingAsset'))
});
export default injectIntl(connect(
+1
View File
@@ -10,6 +10,7 @@ $z-index-stage-indicator: 45;
$z-index-add-button: 46;
$z-index-tooltip: 47; /* tooltips should go over add buttons if they overlap */
$z-index-monitor: 48; /* monitors go over add buttons */
$z-index-stage-question: 49; /* "ask" block text input goes above monitors */
$z-index-card: 480;
$z-index-alerts: 490;
+14
View File
@@ -198,6 +198,20 @@ const alerts = [
closeButton: true,
level: AlertLevels.SUCCESS,
maxDisplaySecs: 15
},
{
alertId: 'importingAsset',
alertType: AlertTypes.STANDARD,
clearList: [],
content: (
<FormattedMessage
defaultMessage="Importing…"
description="Message indicating that project is in process of importing"
id="gui.alerts.importing"
/>
),
iconSpinner: true,
level: AlertLevels.SUCCESS
}
];
+1 -5
View File
@@ -51,7 +51,6 @@ const AppStateHOC = function (WrappedComponent, localesOnly) {
guiMiddleware,
initFullScreen,
initPlayer,
initPreviewInfo,
initTelemetryModal
} = guiRedux;
const {ScratchPaintReducer} = require('scratch-paint');
@@ -66,8 +65,6 @@ const AppStateHOC = function (WrappedComponent, localesOnly) {
}
} else if (props.showTelemetryModal) {
initializedGui = initTelemetryModal(initializedGui);
} else if (props.showPreviewInfo) {
initializedGui = initPreviewInfo(initializedGui);
}
reducers = {
locales: localesReducer,
@@ -100,7 +97,6 @@ const AppStateHOC = function (WrappedComponent, localesOnly) {
const {
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;
@@ -118,7 +114,7 @@ const AppStateHOC = function (WrappedComponent, localesOnly) {
AppStateWrapper.propTypes = {
isFullScreen: PropTypes.bool,
isPlayerOnly: PropTypes.bool,
showPreviewInfo: PropTypes.bool
showTelemetryModal: PropTypes.bool
};
return AppStateWrapper;
};
+26
View File
@@ -245,8 +245,12 @@ export default function (vm) {
// The block was in the flyout so look up future block info there.
lookupBlocks = vm.runtime.flyoutBlocks;
}
const sort = function (options) {
options.sort(ScratchBlocks.scratchBlocksUtils.compareStrings);
};
// Get all the stage variables (no lists) so we can add them to menu when the stage is selected.
const stageVariableOptions = vm.runtime.getTargetForStage().getAllVariableNamesInScopeByType('');
sort(stageVariableOptions);
const stageVariableMenuItems = stageVariableOptions.map(variable => [variable, variable]);
if (sensingOfBlock.inputs.OBJECT.shadow !== sensingOfBlock.inputs.OBJECT.block) {
// There's a block dropped on top of the menu. It'd be nice to evaluate it and
@@ -265,6 +269,7 @@ export default function (vm) {
// The target should exist, but there are ways for it not to (e.g. #4203).
if (target) {
spriteVariableOptions = target.getAllVariableNamesInScopeByType('', true);
sort(spriteVariableOptions);
}
const spriteVariableMenuItems = spriteVariableOptions.map(variable => [variable, variable]);
return spriteOptions.concat(spriteVariableMenuItems);
@@ -315,5 +320,26 @@ export default function (vm) {
vm.runtime.emit('PLAY_NOTE', noteNum, extensionId);
};
// Use a collator's compare instead of localeCompare which internally
// creates a collator. Using this is a lot faster in browsers that create a
// collator for every localeCompare call.
const collator = new Intl.Collator([], {
sensitivity: 'base',
numeric: true
});
ScratchBlocks.scratchBlocksUtils.compareStrings = function (str1, str2) {
return collator.compare(str1, str2);
};
// Blocks wants to know if 3D CSS transforms are supported. The cross
// section of browsers Scratch supports and browsers that support 3D CSS
// transforms will make the return always true.
//
// Shortcutting to true lets us skip an expensive style recalculation when
// first loading the Scratch editor.
ScratchBlocks.utils.is3dSupported = function () {
return true;
};
return ScratchBlocks;
}
+39
View File
@@ -0,0 +1,39 @@
/**
* Report a telemetry event.
* @param {string} event - one of `projectWasCreated`, `projectDidLoad`, `projectDidSave`, `projectWasUploaded`
*/
// TODO make a telemetry HOC and move this stuff there
const collectMetadata = function (vm, projectName = '', locale = '') {
// TODO move most or all of this into a collectMetadata() method on the VM/Runtime
const metadata = {
projectName: projectName,
language: locale,
spriteCount: 0,
blocksCount: 0,
costumesCount: 0,
listsCount: 0,
scriptCount: 0,
soundsCount: 0,
variablesCount: 0
};
for (const target of vm.runtime.targets) {
++metadata.spriteCount;
metadata.blocksCount += Object.keys(target.sprite.blocks._blocks).length;
metadata.costumesCount += target.sprite.costumes_.length;
metadata.scriptCount += target.sprite.blocks._scripts.length;
metadata.soundsCount += target.sprite.sounds.length;
for (const variableName in target.variables) {
const variable = target.variables[variableName];
if (variable.type === 'list') {
++metadata.listsCount;
} else {
++metadata.variablesCount;
}
}
}
return metadata;
};
export default collectMetadata;
+9 -2
View File
@@ -1,4 +1,3 @@
import {TextEncoder} from 'text-encoding';
import projectData from './project-data';
/* eslint-disable import/no-unresolved */
@@ -9,8 +8,16 @@ import costume1 from '!raw-loader!./09dc888b0b7df19f70d81588ae73420e.svg';
import costume2 from '!raw-loader!./3696356a03a8d938318876a593572843.svg';
/* eslint-enable import/no-unresolved */
const encoder = new TextEncoder();
const defaultProject = translator => {
let _TextEncoder;
if (typeof TextEncoder === 'undefined') {
_TextEncoder = require('text-encoding').TextEncoder;
} else {
/* global TextEncoder */
_TextEncoder = TextEncoder;
}
const encoder = new _TextEncoder();
const projectJson = projectData(translator);
return [{
id: 0,
+52 -37
View File
@@ -1,6 +1,7 @@
import {BitmapAdapter} from 'scratch-svg-renderer';
import log from './log.js';
import randomizeSpritePosition from './randomize-sprite-position.js';
import gifDecoder from './gif-decoder';
/**
* Extract the file name given a string of the form fileName + ext
@@ -19,24 +20,29 @@ const extractFileName = function (nameExt) {
* and a function to handle loading the file.
* @param {Input} fileInput The <input/> element that contains the file being loaded
* @param {Function} onload The function that handles loading the file
* @param {Function} onerror The function that handles any error loading the file
*/
const handleFileUpload = function (fileInput, onload) {
let thisFile = null;
const reader = new FileReader();
reader.onload = () => {
// Reset the file input value now that we have everything we need
// so that the user can upload the same sound multiple times if
// they choose
fileInput.value = null;
const fileType = thisFile.type;
const fileName = extractFileName(thisFile.name);
onload(reader.result, fileType, fileName);
const handleFileUpload = function (fileInput, onload, onerror) {
const readFile = (i, files) => {
if (i === files.length) {
// Reset the file input value now that we have everything we need
// so that the user can upload the same sound multiple times if
// they choose
fileInput.value = null;
return;
}
const file = files[i];
const reader = new FileReader();
reader.onload = () => {
const fileType = file.type;
const fileName = extractFileName(file.name);
onload(reader.result, fileType, fileName, i, files.length);
readFile(i + 1, files);
};
reader.onerror = onerror;
reader.readAsArrayBuffer(file);
};
if (fileInput.files) {
thisFile = fileInput.files[0];
reader.readAsArrayBuffer(thisFile);
}
readFile(0, fileInput.files);
};
/**
@@ -55,7 +61,6 @@ const handleFileUpload = function (fileInput, onload) {
* Create an asset (costume, sound) with storage and return an object representation
* of the asset to track in the VM.
* @param {ScratchStorage} storage The storage to cache the asset in
* @param {string} fileName The name of the asset
* @param {AssetType} assetType A ScratchStorage AssetType indicating what kind of
* asset this is.
* @param {string} dataFormat The format of this data (typically the file extension)
@@ -63,7 +68,7 @@ const handleFileUpload = function (fileInput, onload) {
* @return {VMAsset} An object representing this asset and relevant information
* which can be used to look up the data in storage
*/
const createVMAsset = function (storage, fileName, assetType, dataFormat, data) {
const createVMAsset = function (storage, assetType, dataFormat, data) {
const asset = storage.createAsset(
assetType,
dataFormat,
@@ -73,7 +78,7 @@ const createVMAsset = function (storage, fileName, assetType, dataFormat, data)
);
return {
name: fileName,
name: null, // Needs to be set by caller
dataFormat: dataFormat,
asset: asset,
md5: `${asset.assetId}.${dataFormat}`,
@@ -86,13 +91,13 @@ const createVMAsset = function (storage, fileName, assetType, dataFormat, data)
* @param {ArrayBuffer | string} fileData The costume data to load (this can be a base64 string
* iff the image is a bitmap)
* @param {string} fileType The MIME type of this file
* @param {string} costumeName The user-readable name to use for the costume.
* @param {ScratchStorage} storage The ScratchStorage instance to cache the costume data
* @param {Function} handleCostume The function to execute on the costume object returned after
* caching this costume in storage - This function should be responsible for
* adding the costume to the VM and handling other UI flow that should come after adding the costume
* @param {Function} handleError The function to execute if there is an error parsing the costume
*/
const costumeUpload = function (fileData, fileType, costumeName, storage, handleCostume) {
const costumeUpload = function (fileData, fileType, storage, handleCostume, handleError = () => {}) {
let costumeFormat = null;
let assetType = null;
switch (fileType) {
@@ -111,8 +116,20 @@ const costumeUpload = function (fileData, fileType, costumeName, storage, handle
assetType = storage.AssetType.ImageBitmap;
break;
}
case 'image/gif': {
let costumes = [];
gifDecoder(fileData, (frameNumber, dataUrl, numFrames) => {
costumeUpload(dataUrl, 'image/png', storage, costumes_ => {
costumes = costumes.concat(costumes_);
if (frameNumber === numFrames - 1) {
handleCostume(costumes);
}
}, handleError);
});
return; // Abandon this load, do not try to load gif itself
}
default:
log.warn(`Encountered unexpected file type: ${fileType}`);
handleError(`Encountered unexpected file type: ${fileType}`);
return;
}
@@ -120,12 +137,11 @@ const costumeUpload = function (fileData, fileType, costumeName, storage, handle
const addCostumeFromBuffer = function (dataBuffer) {
const vmCostume = createVMAsset(
storage,
costumeName,
assetType,
costumeFormat,
dataBuffer
);
handleCostume(vmCostume);
handleCostume([vmCostume]);
};
if (costumeFormat === storage.DataFormat.SVG) {
@@ -137,9 +153,7 @@ const costumeUpload = function (fileData, fileType, costumeName, storage, handle
} else {
// otherwise it's a bitmap
bitmapAdapter.importBitmap(fileData, fileType).then(addCostumeFromBuffer)
.catch(e => {
log.error(e);
});
.catch(handleError);
}
};
@@ -148,13 +162,12 @@ const costumeUpload = function (fileData, fileType, costumeName, storage, handle
* @param {ArrayBuffer} fileData The sound data to load
* @param {string} fileType The MIME type of this file; This function will exit
* early if the fileType is unexpected.
* @param {string} soundName The user-readable name to use for the sound.
* @param {ScratchStorage} storage The ScratchStorage instance to cache the sound data
* @param {Function} handleSound The function to execute on the sound object of type VMAsset
* This function should be responsible for adding the sound to the VM
* as well as handling other UI flow that should come after adding the sound
*/
const soundUpload = function (fileData, fileType, soundName, storage, handleSound) {
const soundUpload = function (fileData, fileType, storage, handleSound) {
let soundFormat;
switch (fileType) {
case 'audio/mp3':
@@ -176,7 +189,6 @@ const soundUpload = function (fileData, fileType, soundName, storage, handleSoun
const vmSound = createVMAsset(
storage,
soundName,
storage.AssetType.Sound,
soundFormat,
new Uint8Array(fileData));
@@ -184,8 +196,7 @@ const soundUpload = function (fileData, fileType, soundName, storage, handleSoun
handleSound(vmSound);
};
const spriteUpload = function (fileData, fileType, spriteName, storage, handleSprite, costumeSuffix) {
const costumeName = costumeSuffix || 'costume1';
const spriteUpload = function (fileData, fileType, spriteName, storage, handleSprite, handleError = () => {}) {
switch (fileType) {
case '':
case 'application/zip': { // We think this is a .sprite2 or .sprite3 file
@@ -194,9 +205,13 @@ const spriteUpload = function (fileData, fileType, spriteName, storage, handleSp
}
case 'image/svg+xml':
case 'image/png':
case 'image/jpeg': {
case 'image/jpeg':
case 'image/gif': {
// Make a sprite from an image by making it a costume first
costumeUpload(fileData, fileType, `${spriteName}-${costumeName}`, storage, (vmCostume => {
costumeUpload(fileData, fileType, storage, vmCostumes => {
vmCostumes.forEach((costume, i) => {
costume.name = `${spriteName}${i ? i + 1 : ''}`;
});
const newSprite = {
name: spriteName,
isStage: false,
@@ -210,17 +225,17 @@ const spriteUpload = function (fileData, fileType, spriteName, storage, handleSp
currentCostume: 0,
blocks: {},
variables: {},
costumes: [vmCostume],
costumes: vmCostumes,
sounds: [] // TODO are all of these necessary?
};
randomizeSpritePosition(newSprite);
// TODO probably just want sprite upload to handle this object directly
handleSprite(JSON.stringify(newSprite));
}));
}, handleError);
return;
}
default: {
log.warn(`Encountered unexpected file type: ${fileType}`);
handleError(`Encountered unexpected file type: ${fileType}`);
return;
}
}
+60
View File
@@ -0,0 +1,60 @@
import {GifReader} from 'omggif';
export default (arrayBuffer, onFrame) => {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const gifReader = new GifReader(new Uint8Array(arrayBuffer));
const numFrames = gifReader.numFrames();
canvas.width = gifReader.width;
canvas.height = gifReader.height;
let imageData = ctx.createImageData(canvas.width, canvas.height);
let previousData = ctx.createImageData(canvas.width, canvas.height);
const loadFrame = i => {
const framePixels = [];
gifReader.decodeAndBlitFrameRGBA(i, framePixels);
const {x, y, width, height, disposal} = gifReader.frameInfo(i);
for (let row = 0; row < height; row++) {
for (let column = 0; column < width; column++) {
const indexOffset = 4 * (x + (y * canvas.width));
const j = indexOffset + (4 * (column + (row * canvas.width)));
if (framePixels[j + 3]) {
imageData.data[j + 0] = framePixels[j + 0];
imageData.data[j + 1] = framePixels[j + 1];
imageData.data[j + 2] = framePixels[j + 2];
imageData.data[j + 3] = framePixels[j + 3];
}
}
}
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.putImageData(imageData, 0, 0);
const dataUrl = canvas.toDataURL();
switch (disposal) {
case 2: // "Return to background", blank out the current frame
ctx.clearRect(x, y, width, height);
imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
break;
case 3: // "Restore to previous", copy previous data to current
imageData = ctx.createImageData(canvas.width, canvas.height);
imageData.data.set(previousData.data);
break;
default: // 0 and 1, as well as 4+ modes = do-not-dispose, so cache frame
previousData = ctx.getImageData(0, 0, canvas.width, canvas.height);
break;
}
onFrame(i, dataUrl, numFrames);
if (i < numFrames - 1) {
setTimeout(() => {
loadFrame(i + 1);
});
}
};
loadFrame(0);
};
-5
View File
@@ -9,8 +9,6 @@ import {
setProjectId
} from '../reducers/project-state';
import {closePreviewInfo} from '../reducers/modals';
/* Higher Order Component to get the project id from location.hash
* @param {React.Component} WrappedComponent: component to render
* @returns {React.Component} component with hash parsing behavior
@@ -74,9 +72,6 @@ const HashParserHOC = function (WrappedComponent) {
const mapDispatchToProps = dispatch => ({
setProjectId: projectId => {
dispatch(setProjectId(projectId));
if (projectId !== defaultProjectId) {
dispatch(closePreviewInfo());
}
}
});
// Allow incoming props to override redux-provided props. Used to mock in tests.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 308 KiB

+188
View File
@@ -0,0 +1,188 @@
// Intro
import introMove from './steps/intro-move.en.gif';
import introSayHello from './steps/intro-say-hello.en.gif';
// Text to Speech
import speechAddExtension from './steps/speech-add-extension.en.gif';
import speechSaySomething from './steps/speech-say-something.en.gif';
import speechSetVoice from './steps/speech-set-voice.en.gif';
import speechMoveAround from './steps/speech-move-around.en.gif';
import speechAddBackdrop from './steps/speech-add-backdrop.en.gif';
import speechAddSprite from './steps/speech-add-sprite.en.gif';
import speechSong from './steps/speech-song.en.gif';
import speechChangeColor from './steps/speech-change-color.en.gif';
import speechSpin from './steps/speech-spin.en.gif';
import speechGrowShrink from './steps/speech-grow-shrink.en.gif';
// Cartoon Network
import cnShowCharacter from './steps/cn-show-character.en.gif';
import cnSay from './steps/cn-say.en.gif';
import cnGlide from './steps/cn-glide.en.gif';
import cnPickSprite from './steps/cn-pick-sprite.en.gif';
import cnCollect from './steps/cn-collect.en.gif';
import cnVariable from './steps/cn-variable.en.gif';
import cnScore from './steps/cn-score.en.gif';
import cnBackdrop from './steps/cn-backdrop.en.gif';
// Add sprite
import addSprite from './steps/add-sprite.en.gif';
// Animate a name
import namePickLetter from './steps/name-pick-letter.en.gif';
import namePlaySound from './steps/name-play-sound.en.gif';
import namePickLetter2 from './steps/name-pick-letter2.en.gif';
import nameChangeColor from './steps/name-change-color.en.gif';
import nameSpin from './steps/name-spin.en.gif';
import nameGrow from './steps/name-grow.en.gif';
// Make-Music
import musicPickInstrument from './steps/music-pick-instrument.en.gif';
import musicPlaySound from './steps/music-play-sound.en.gif';
import musicMakeSong from './steps/music-make-song.en.gif';
import musicMakeBeat from './steps/music-make-beat.en.gif';
import musicMakeBeatbox from './steps/music-make-beatbox.en.gif';
// Chase-Game
import chaseGameAddBackdrop from './steps/chase-game-add-backdrop.en.gif';
import chaseGameAddSprite1 from './steps/chase-game-add-sprite1.en.gif';
import chaseGameRightLeft from './steps/chase-game-right-left.en.gif';
import chaseGameUpDown from './steps/chase-game-up-down.en.gif';
import chaseGameAddSprite2 from './steps/chase-game-add-sprite2.en.gif';
import chaseGameMoveRandomly from './steps/chase-game-move-randomly.en.gif';
import chaseGamePlaySound from './steps/chase-game-play-sound.en.gif';
import chaseGameAddVariable from './steps/chase-game-add-variable.en.gif';
import chaseGameChangeScore from './steps/chase-game-change-score.en.gif';
// Clicker-Game (Pop Game)
import popGamePickSprite from './steps/pop-game-pick-sprite.en.gif';
import popGamePlaySound from './steps/pop-game-play-sound.en.gif';
import popGameAddScore from './steps/pop-game-add-score.en.gif';
import popGameChangeScore from './steps/pop-game-change-score.en.gif';
import popGameRandomPosition from './steps/pop-game-random-position.en.gif';
import popGameChangeColor from './steps/pop-game-change-color.en.gif';
import popGameResetScore from './steps/pop-game-reset-score.en.gif';
// Animate A Character
import animateCharPickBackdrop from './steps/animate-char-pick-backdrop.en.gif';
import animateCharPickSprite from './steps/animate-char-pick-sprite.en.gif';
import animateCharSaySomething from './steps/animate-char-say-something.en.gif';
import animateCharAddSound from './steps/animate-char-add-sound.en.gif';
import animateCharTalk from './steps/animate-char-talk.en.gif';
import animateCharMove from './steps/animate-char-move.en.gif';
import animateCharJump from './steps/animate-char-jump.en.gif';
import animateCharChangeColor from './steps/animate-char-change-color.en.gif';
// Tell A Story
import storyPickBackdrop from './steps/story-pick-backdrop.en.gif';
import storyPickSprite from './steps/story-pick-sprite.en.gif';
import storySaySomething from './steps/story-say-something.en.gif';
import storyPickSprite2 from './steps/story-pick-sprite2.en.gif';
import storyFlip from './steps/story-flip.en.gif';
import storyConversation from './steps/story-conversation.en.gif';
import storyPickBackdrop2 from './steps/story-pick-backdrop2.en.gif';
import storySwitchBackdrop from './steps/story-switch-backdrop.en.gif';
import storyHideCharacter from './steps/story-hide-character.en.gif';
import storyShowCharacter from './steps/story-show-character.en.gif';
// Video Sensing
import videoAddExtension from './steps/video-add-extension.en.gif';
import videoPet from './steps/video-pet.en.gif';
import videoAnimate from './steps/video-animate.en.gif';
import videoPop from './steps/video-pop.en.gif';
const enImages = {
// Intro
introMove: introMove,
introSayHello: introSayHello,
// Text to Speech
speechAddExtension: speechAddExtension,
speechSaySomething: speechSaySomething,
speechSetVoice: speechSetVoice,
speechMoveAround: speechMoveAround,
speechAddBackdrop: speechAddBackdrop,
speechAddSprite: speechAddSprite,
speechSong: speechSong,
speechChangeColor: speechChangeColor,
speechSpin: speechSpin,
speechGrowShrink: speechGrowShrink,
// Cartoon Network
cnShowCharacter: cnShowCharacter,
cnSay: cnSay,
cnGlide: cnGlide,
cnPickSprite: cnPickSprite,
cnCollect: cnCollect,
cnVariable: cnVariable,
cnScore: cnScore,
cnBackdrop: cnBackdrop,
// Add sprite
addSprite: addSprite,
// Animate a name
namePickLetter: namePickLetter,
namePlaySound: namePlaySound,
namePickLetter2: namePickLetter2,
nameChangeColor: nameChangeColor,
nameSpin: nameSpin,
nameGrow: nameGrow,
// Make-Music
musicPickInstrument: musicPickInstrument,
musicPlaySound: musicPlaySound,
musicMakeSong: musicMakeSong,
musicMakeBeat: musicMakeBeat,
musicMakeBeatbox: musicMakeBeatbox,
// Chase-Game
chaseGameAddBackdrop: chaseGameAddBackdrop,
chaseGameAddSprite1: chaseGameAddSprite1,
chaseGameRightLeft: chaseGameRightLeft,
chaseGameUpDown: chaseGameUpDown,
chaseGameAddSprite2: chaseGameAddSprite2,
chaseGameMoveRandomly: chaseGameMoveRandomly,
chaseGamePlaySound: chaseGamePlaySound,
chaseGameAddVariable: chaseGameAddVariable,
chaseGameChangeScore: chaseGameChangeScore,
// Make-A-Pop/Clicker Game
popGamePickSprite: popGamePickSprite,
popGamePlaySound: popGamePlaySound,
popGameAddScore: popGameAddScore,
popGameChangeScore: popGameChangeScore,
popGameRandomPosition: popGameRandomPosition,
popGameChangeColor: popGameChangeColor,
popGameResetScore: popGameResetScore,
// Animate A Character
animateCharPickBackdrop: animateCharPickBackdrop,
animateCharPickSprite: animateCharPickSprite,
animateCharSaySomething: animateCharSaySomething,
animateCharAddSound: animateCharAddSound,
animateCharTalk: animateCharTalk,
animateCharMove: animateCharMove,
animateCharJump: animateCharJump,
animateCharChangeColor: animateCharChangeColor,
// Tell A Story
storyPickBackdrop: storyPickBackdrop,
storyPickSprite: storyPickSprite,
storySaySomething: storySaySomething,
storyPickSprite2: storyPickSprite2,
storyFlip: storyFlip,
storyConversation: storyConversation,
storyPickBackdrop2: storyPickBackdrop2,
storySwitchBackdrop: storySwitchBackdrop,
storyHideCharacter: storyHideCharacter,
storyShowCharacter: storyShowCharacter,
// Video Sensing
videoAddExtension: videoAddExtension,
videoPet: videoPet,
videoAnimate: videoAnimate,
videoPop: videoPop
};
export {enImages};
+188
View File
@@ -0,0 +1,188 @@
// Intro
import introMove from './steps/intro-move.es.gif';
import introSayHello from './steps/intro-say-hello.es.gif';
// Text to Speech
// import speechAddExtension from './steps/speech-add-extension.es.gif';
// import speechSaySomething from './steps/speech-say-something.es.gif';
// import speechSetVoice from './steps/speech-set-voice.es.gif';
// import speechMoveAround from './steps/speech-move-around.es.gif';
// import speechAddBackdrop from './steps/speech-add-backdrop.es.gif';
// import speechAddSprite from './steps/speech-add-sprite.es.gif';
// import speechSong from './steps/speech-song.es.gif';
// import speechChangeColor from './steps/speech-change-color.es.gif';
// import speechSpin from './steps/speech-spin.es.gif';
// import speechGrowShrink from './steps/speech-grow-shrink.es.gif';
//
// // Cartoon Network
// import cnShowCharacter from './steps/cn-show-character.es.gif';
// import cnSay from './steps/cn-say.es.gif';
// import cnGlide from './steps/cn-glide.es.gif';
// import cnPickSprite from './steps/cn-pick-sprite.es.gif';
// import cnCollect from './steps/cn-collect.es.gif';
// import cnVariable from './steps/cn-variable.es.gif';
// import cnScore from './steps/cn-score.es.gif';
// import cnBackdrop from './steps/cn-backdrop.es.gif';
// Add sprite
import addSprite from './steps/add-sprite.es.gif';
// Animate a name
import namePickLetter from './steps/name-pick-letter.es.gif';
import namePlaySound from './steps/name-play-sound.es.gif';
import namePickLetter2 from './steps/name-pick-letter2.es.gif';
import nameChangeColor from './steps/name-change-color.es.gif';
import nameSpin from './steps/name-spin.es.gif';
import nameGrow from './steps/name-grow.es.gif';
// Make-Music
import musicPickInstrument from './steps/music-pick-instrument.es.gif';
import musicPlaySound from './steps/music-play-sound.es.gif';
import musicMakeSong from './steps/music-make-song.es.gif';
import musicMakeBeat from './steps/music-make-beat.es.gif';
import musicMakeBeatbox from './steps/music-make-beatbox.es.gif';
// Chase-Game
import chaseGameAddBackdrop from './steps/chase-game-add-backdrop.es.gif';
import chaseGameAddSprite1 from './steps/chase-game-add-sprite1.es.gif';
import chaseGameRightLeft from './steps/chase-game-right-left.es.gif';
import chaseGameUpDown from './steps/chase-game-up-down.es.gif';
import chaseGameAddSprite2 from './steps/chase-game-add-sprite2.es.gif';
import chaseGameMoveRandomly from './steps/chase-game-move-randomly.es.gif';
import chaseGamePlaySound from './steps/chase-game-play-sound.es.gif';
import chaseGameAddVariable from './steps/chase-game-add-variable.es.gif';
import chaseGameChangeScore from './steps/chase-game-change-score.es.gif';
// Clicker-Game (Pop Game)
import popGamePickSprite from './steps/pop-game-pick-sprite.es.gif';
import popGamePlaySound from './steps/pop-game-play-sound.es.gif';
import popGameAddScore from './steps/pop-game-add-score.es.gif';
import popGameChangeScore from './steps/pop-game-change-score.es.gif';
import popGameRandomPosition from './steps/pop-game-random-position.es.gif';
import popGameChangeColor from './steps/pop-game-change-color.es.gif';
import popGameResetScore from './steps/pop-game-reset-score.es.gif';
// Animate A Character
import animateCharPickBackdrop from './steps/animate-char-pick-backdrop.es.gif';
import animateCharPickSprite from './steps/animate-char-pick-sprite.es.gif';
import animateCharSaySomething from './steps/animate-char-say-something.es.gif';
import animateCharAddSound from './steps/animate-char-add-sound.es.gif';
import animateCharTalk from './steps/animate-char-talk.es.gif';
import animateCharMove from './steps/animate-char-move.es.gif';
import animateCharJump from './steps/animate-char-jump.es.gif';
import animateCharChangeColor from './steps/animate-char-change-color.es.gif';
// Tell A Story
import storyPickBackdrop from './steps/story-pick-backdrop.es.gif';
import storyPickSprite from './steps/story-pick-sprite.es.gif';
import storySaySomething from './steps/story-say-something.es.gif';
import storyPickSprite2 from './steps/story-pick-sprite2.es.gif';
import storyFlip from './steps/story-flip.es.gif';
import storyConversation from './steps/story-conversation.es.gif';
import storyPickBackdrop2 from './steps/story-pick-backdrop2.es.gif';
import storySwitchBackdrop from './steps/story-switch-backdrop.es.gif';
import storyHideCharacter from './steps/story-hide-character.es.gif';
import storyShowCharacter from './steps/story-show-character.es.gif';
// Video Sensing
import videoAddExtension from './steps/video-add-extension.es.gif';
import videoPet from './steps/video-pet.es.gif';
import videoAnimate from './steps/video-animate.es.gif';
import videoPop from './steps/video-pop.es.gif';
const esImages = {
// Intro
introMove: introMove,
introSayHello: introSayHello,
// // Text to Speech
// speechAddExtension: speechAddExtension,
// speechSaySomething: speechSaySomething,
// speechSetVoice: speechSetVoice,
// speechMoveAround: speechMoveAround,
// speechAddBackdrop: speechAddBackdrop,
// speechAddSprite: speechAddSprite,
// speechSong: speechSong,
// speechChangeColor: speechChangeColor,
// speechSpin: speechSpin,
// speechGrowShrink: speechGrowShrink,
//
// // Cartoon Network
// cnShowCharacter: cnShowCharacter,
// cnSay: cnSay,
// cnGlide: cnGlide,
// cnPickSprite: cnPickSprite,
// cnCollect: cnCollect,
// cnVariable: cnVariable,
// cnScore: cnScore,
// cnBackdrop: cnBackdrop,
// Add sprite
addSprite: addSprite,
// Animate a name
namePickLetter: namePickLetter,
namePlaySound: namePlaySound,
namePickLetter2: namePickLetter2,
nameChangeColor: nameChangeColor,
nameSpin: nameSpin,
nameGrow: nameGrow,
// Make-Music
musicPickInstrument: musicPickInstrument,
musicPlaySound: musicPlaySound,
musicMakeSong: musicMakeSong,
musicMakeBeat: musicMakeBeat,
musicMakeBeatbox: musicMakeBeatbox,
// Chase-Game
chaseGameAddBackdrop: chaseGameAddBackdrop,
chaseGameAddSprite1: chaseGameAddSprite1,
chaseGameRightLeft: chaseGameRightLeft,
chaseGameUpDown: chaseGameUpDown,
chaseGameAddSprite2: chaseGameAddSprite2,
chaseGameMoveRandomly: chaseGameMoveRandomly,
chaseGamePlaySound: chaseGamePlaySound,
chaseGameAddVariable: chaseGameAddVariable,
chaseGameChangeScore: chaseGameChangeScore,
// Make-A-Pop/Clicker Game
popGamePickSprite: popGamePickSprite,
popGamePlaySound: popGamePlaySound,
popGameAddScore: popGameAddScore,
popGameChangeScore: popGameChangeScore,
popGameRandomPosition: popGameRandomPosition,
popGameChangeColor: popGameChangeColor,
popGameResetScore: popGameResetScore,
// Animate A Character
animateCharPickBackdrop: animateCharPickBackdrop,
animateCharPickSprite: animateCharPickSprite,
animateCharSaySomething: animateCharSaySomething,
animateCharAddSound: animateCharAddSound,
animateCharTalk: animateCharTalk,
animateCharMove: animateCharMove,
animateCharJump: animateCharJump,
animateCharChangeColor: animateCharChangeColor,
// Tell A Story
storyPickBackdrop: storyPickBackdrop,
storyPickSprite: storyPickSprite,
storySaySomething: storySaySomething,
storyPickSprite2: storyPickSprite2,
storyFlip: storyFlip,
storyConversation: storyConversation,
storyPickBackdrop2: storyPickBackdrop2,
storySwitchBackdrop: storySwitchBackdrop,
storyHideCharacter: storyHideCharacter,
storyShowCharacter: storyShowCharacter,
// Video Sensing
videoAddExtension: videoAddExtension,
videoPet: videoPet,
videoAnimate: videoAnimate,
videoPop: videoPop
};
export {esImages};
+93 -162
View File
@@ -1,120 +1,51 @@
import React from 'react';
import {FormattedMessage} from 'react-intl';
// Tutorial thumbnails: Avoid using any text that would need to be
// translated in thumbnails.
// Intro
import libraryIntro from './intro/lib-getting-started.jpg';
import stepMove from './intro/intro1.gif';
import stepMoveSayHello from './intro/intro2.gif';
import libraryIntro from './thumbnails/getting-started.jpg';
// Text to Speech
import libraryTXTSpeech from './txt/lib_txt-to-speech.jpg';
import stepAddTXTextension from './txt/01_hoc-add-extensions.gif';
import stepTXTSpeech from './txt/02_hoc-say-something.gif';
import stepTXTSetVoice from './txt/03_hoc-set-voice.gif';
import stepTXTMove from './txt/04_hoc-move-around.gif';
import stepTXTBackdrop from './txt/05_hoc-add-backdrop.gif';
import stepTXTAddSprite from './txt/06_hoc-add-character.gif';
import stepTXTSong from './txt/07_hoc-perform-song.gif';
import stepTXTColor from './txt/08_hoc-color-click.gif';
import stepTXTSpin from './txt/09_hoc-spin.gif';
import stepTXTGrow from './txt/10_hoc-grow-shrink.gif';
import libraryTXTSpeech from './thumbnails/text-to-speech.jpg';
// Cartoon Network
import libraryCartoonNetwork from './cartoonnetwork/lib_CartoonNetwork.jpg';
import stepCNcharacter from './cartoonnetwork/1-cn-hideshow.gif';
import stepCNsay from './cartoonnetwork/2-cn-say.gif';
import stepCNglide from './cartoonnetwork/3-cn-glide.gif';
import stepCNpicksprite from './cartoonnetwork/4-cn-pick-gem.gif';
import stepCNcollect from './cartoonnetwork/5-cn-collect.gif';
import stepCNvariable from './cartoonnetwork/6-cn-variable.gif';
import stepCNscore from './cartoonnetwork/7-cn-score.gif';
import stepCNbackdrop from './cartoonnetwork/8-cn-change-backdrop.gif';
import libraryCartoonNetwork from './thumbnails/cartoon-network.jpg';
// Add sprite
import libraryAddSprite from './sprite/cover-add-sprite.jpg';
import stepAddSprite from './sprite/intro-choose-sprite.gif';
import libraryAddSprite from './thumbnails/add-sprite.jpg';
// Animate a name
import libraryAnimate from './animate/lib_animate-a-name.jpg';
import stepAnimatePickLetter from './animate/animate-name-pick-a-letter.gif';
import stepAnimatePlaySound from './animate/animate-name-play-sound.gif';
import stepAnimatePickLetter2 from './animate/animate-name-pick-a-letter2.gif';
import stepAnimateChangeColor from './animate/animate-name-change-color.gif';
import stepAnimateSpin from './animate/animate-name-spin.gif';
import stepAnimateGrow from './animate/animate-name-grow.gif';
import libraryAnimate from './thumbnails/animate-a-name.jpg';
// Make-Music
import libraryMakeMusic from './make-music/lib-make-music.jpg';
import stepPickInstrument from './make-music/make-music-pick-instrument.gif';
import stepPlaySoundClick from './make-music/make-music-play-sound.gif';
import stepMakeSong from './make-music/make-music-make-song.gif';
import stepMakeBeat from './make-music/make-music-make-beat.gif';
import stepMakeBeatbox from './make-music/make-music-beatbox.gif';
import libraryMakeMusic from './thumbnails/make-music.jpg';
// Chase-Game
import libraryChaseGame from './chase-game/lib-chasegame.jpg';
import stepBG from './chase-game/chase-game-add-backdrop.gif';
import stepAddOcto from './chase-game/chase-game-add-sprite1.gif';
import stepLeftRight from './chase-game/chase-game-move-rightleft.gif';
import stepUpDown from './chase-game/chase-game-move-updown.gif';
import stepAddStar from './chase-game/chase-game-add-sprite2.gif';
import stepMoveRandom from './chase-game/chase-game-move-randomly.gif';
import stepWhenTouch from './chase-game/chase-game-touching.gif';
import stepScoreVariable from './chase-game/chase-game-add-variable.gif';
import stepScoreWhenTouch from './chase-game/chase-game-change-score.gif';
import libraryChaseGame from './thumbnails/chase-game.jpg';
// Make-A-Game
import libraryMakeAGame from './game/lib-pop.jpg';
import stepGamePickSprite from './game/game-pick-sprite.gif';
import stepGamePlaySound from './game/game-play-sound.gif';
import stepGameAddScore from './game/game-add-score.gif';
import stepGameChangeScore from './game/game-change-score.gif';
import stepRandom from './game/game-random-position.gif';
import stepGameChangeColor from './game/game-change-color.gif';
import stepResetScore from './game/game-reset-score.gif';
import libraryMakeAGame from './thumbnails/pop-game.jpg';
// Animate A Character
import libraryAnimateChar from './animate-char/lib_Animate_a_Character.jpg';
import stepAnimateCharPickBackdrop from './animate-char/animate-char-pick-backdrop.gif';
import stepAnimateCharPickSprite from './animate-char/animate-char-pick-sprite.gif';
import stepAnimateCharSaySomething from './animate-char/animate-char-say-something.gif';
import stepAnimateCharAddSound from './animate-char/animate-char-add-sound.gif';
import stepAnimateCharTalk from './animate-char/animate-char-talk.gif';
import stepAnimateCharMove from './animate-char/animate-char-move.gif';
import stepAnimateCharJump from './animate-char/animate-char-jump.gif';
import stepAnimateCharChangeColor from './animate-char/animate-char-change-color.gif';
import libraryAnimateChar from './thumbnails/animate-a-character.jpg';
// Tell A Story
import libraryStory from './story/lib-tell-a-story.jpg';
import stepStoryPickBackdrop from './story/story-pick-backdrop.gif';
import stepStoryPickSprite from './story/story-pick-sprite.gif';
import stepStoryCharSaySomething from './story/story-say-something.gif';
import stepStoryCharAddAnotherSprite from './story/story-pick-another-sprite.gif';
import stepStoryFlip from './story/story-flip.gif';
import stepStoryConversation from './story/story-conversation.gif';
import stepStoryAddAnotherBackdrop from './story/story-add-background-2.gif';
import stepStorySwitchBackdrop from './story/story-switch-backdrop.gif';
import stepStoryHide from './story/story-hide-character.gif';
import stepStoryShow from './story/story-show-character.gif';
import libraryStory from './thumbnails/tell-a-story.jpg';
// Video Sensing
import libraryVideoSens from './videosensing/lib_video_sensing.jpg';
import stepVideoSensAddExtension from './videosensing/videosens-add-extension.gif';
import stepVideoSensPet from './videosensing/videosens-pet-cat.gif';
import stepVideoSensAnimate from './videosensing/videosens-animate.gif';
import stepVideoSensPop from './videosensing/videosens-pop.gif';
import libraryVideoSens from './thumbnails/video-sensing.jpg';
// Videos
import recordASound from './videos/record-a-sound.jpg';
import glideAroundThumb from './videos/glide-around.jpg';
import changeSizeThumb from './videos/change-size.jpg';
import switchCostumeThumb from './videos/animate-sprite.jpg';
import hideAndShowThumb from './videos/hide-and-show.jpg';
import addBackdropThumb from './videos/add-backdrop.jpg';
import addEffectsThumb from './videos/add-effects.jpg';
import moveArrowKeysThumb from './videos/move-arrow-keys.jpg';
import spinThumb from './videos/spin.jpg';
import recordASound from './thumbnails/record-a-sound.jpg';
import glideAroundThumb from './thumbnails/glide-around.jpg';
import changeSizeThumb from './thumbnails/change-size.jpg';
import switchCostumeThumb from './thumbnails/animate-sprite.jpg';
import hideAndShowThumb from './thumbnails/hide-and-show.jpg';
import addBackdropThumb from './thumbnails/add-backdrop.jpg';
import addEffectsThumb from './thumbnails/add-effects.jpg';
import moveArrowKeysThumb from './thumbnails/move-arrow-keys.jpg';
import spinThumb from './thumbnails/spin.jpg';
export default {
@@ -138,7 +69,7 @@ export default {
id="gui.howtos.intro-move.step_stepMove"
/>
),
image: stepMove
image: 'introMove'
}, {
title: (
<FormattedMessage
@@ -147,7 +78,7 @@ export default {
id="gui.howtos.add-a-move-block.step_stepMoveSayHello"
/>
),
image: stepMoveSayHello
image: 'introSayHello'
}, {
deckIds: [
'add-a-backdrop',
@@ -180,16 +111,16 @@ export default {
id="gui.howtos.say-it-out-loud.step_AddTXTextension"
/>
),
image: stepAddTXTextension
image: 'speechAddExtension'
}, {
title: (
<FormattedMessage
defaultMessage="Say Something"
description="Step name for 'Say Something' step"
id="gui.howtos. say-it-out-loud.step_TXTSpeech"
id="gui.howtos.say-it-out-loud.step_TXTSpeech"
/>
),
image: stepTXTSpeech
image: 'speechSaySomething'
}, {
title: (
<FormattedMessage
@@ -198,7 +129,7 @@ export default {
id="gui.howtos.say-it-out-loud_TXTSetVoice"
/>
),
image: stepTXTSetVoice
image: 'speechSetVoice'
}, {
title: (
<FormattedMessage
@@ -207,7 +138,7 @@ export default {
id="gui.howtos.say-it-out-loud.step_TXTMove"
/>
),
image: stepTXTMove
image: 'speechMoveAround'
}, {
title: (
<FormattedMessage
@@ -216,7 +147,7 @@ export default {
id="gui.howtos.say-it-out-loud.step_TXTBackdrop"
/>
),
image: stepTXTBackdrop
image: 'speechAddBackdrop'
}, {
title: (
<FormattedMessage
@@ -225,7 +156,7 @@ export default {
id="gui.howtos.say-it-out-loud.step_TXTAddSprite"
/>
),
image: stepTXTAddSprite
image: 'speechAddSprite'
}, {
title: (
<FormattedMessage
@@ -234,7 +165,7 @@ export default {
id="gui.howtos.say-it-out-loud.step_TXTSong"
/>
),
image: stepTXTSong
image: 'speechSong'
}, {
title: (
<FormattedMessage
@@ -243,7 +174,7 @@ export default {
id="gui.howtos.say-it-out-loud.step_TXTColor"
/>
),
image: stepTXTColor
image: 'speechChangeColor'
}, {
title: (
<FormattedMessage
@@ -252,7 +183,7 @@ export default {
id="gui.howtos.say-it-out-loud.step_TXTSpin"
/>
),
image: stepTXTSpin
image: 'speechSpin'
}, {
title: (
<FormattedMessage
@@ -261,7 +192,7 @@ export default {
id="gui.howtos.say-it-out-loud.step_TXTGrow"
/>
),
image: stepTXTGrow
image: 'speechGrowShrink'
}, {
deckIds: [
'animate-a-name',
@@ -295,7 +226,7 @@ export default {
id="gui.howtos.cartoon-network.step_CNcharacter"
/>
),
image: stepCNcharacter
image: 'cnShowCharacter'
}, {
title: (
<FormattedMessage
@@ -304,7 +235,7 @@ export default {
id="gui.howtos.cartoon-network.step_CNsay"
/>
),
image: stepCNsay
image: 'cnSay'
}, {
title: (
<FormattedMessage
@@ -313,7 +244,7 @@ export default {
id="gui.howtos.cartoon-network.step_CNglide"
/>
),
image: stepCNglide
image: 'cnGlide'
}, {
title: (
<FormattedMessage
@@ -322,7 +253,7 @@ export default {
id="gui.howtos.cartoon-network.step_CNpicksprite"
/>
),
image: stepCNpicksprite
image: 'cnPickSprite'
}, {
title: (
<FormattedMessage
@@ -331,7 +262,7 @@ export default {
id="gui.howtos.cartoon-network.step_CNcollect"
/>
),
image: stepCNcollect
image: 'cnCollect'
}, {
title: (
<FormattedMessage
@@ -340,7 +271,7 @@ export default {
id="gui.howtos.cartoon-network.step_CNvariable"
/>
),
image: stepCNvariable
image: 'cnVariable'
}, {
title: (
<FormattedMessage
@@ -349,7 +280,7 @@ export default {
id="gui.howtos.cartoon-network.step_CNscore"
/>
),
image: stepCNscore
image: 'cnScore'
}, {
title: (
<FormattedMessage
@@ -358,7 +289,7 @@ export default {
id="gui.howtos.cartoon-network.step_CNbackdrop"
/>
),
image: stepCNbackdrop
image: 'cnBackdrop'
},
{
video: '6o76f5ivo1'
@@ -392,7 +323,7 @@ export default {
id="gui.howtos.animate-a-name.step_AnimatePickLetter"
/>
),
image: stepAnimatePickLetter
image: 'namePickLetter'
}, {
title: (
<FormattedMessage
@@ -401,7 +332,7 @@ export default {
id="gui.howtos.animate-a-name.step_AnimatePlaySound"
/>
),
image: stepAnimatePlaySound
image: 'namePlaySound'
}, {
title: (
<FormattedMessage
@@ -410,7 +341,7 @@ export default {
id="gui.howtos.animate-a-name.step_AnimatePickLetter2"
/>
),
image: stepAnimatePickLetter2
image: 'namePickLetter2'
}, {
title: (
<FormattedMessage
@@ -419,7 +350,7 @@ export default {
id="gui.howtos.animate-a-name.step_AnimateChangeColor"
/>
),
image: stepAnimateChangeColor
image: 'nameChangeColor'
}, {
title: (
<FormattedMessage
@@ -428,7 +359,7 @@ export default {
id="gui.howtos.animate-a-name.step_AnimateSpin"
/>
),
image: stepAnimateSpin
image: 'nameSpin'
}, {
title: (
<FormattedMessage
@@ -437,7 +368,7 @@ export default {
id="gui.howtos.animate-a-name.step_AnimateGrow"
/>
),
image: stepAnimateGrow
image: 'nameGrow'
}, {
deckIds: [
'add-a-backdrop',
@@ -468,7 +399,7 @@ export default {
id="gui.howtos.Make-Music.step_PickInstrument"
/>
),
image: stepPickInstrument
image: 'musicPickInstrument'
},
{
title: (
@@ -478,7 +409,7 @@ export default {
id="gui.howtos.Make-Music.step_PlaySoundClick"
/>
),
image: stepPlaySoundClick
image: 'musicPlaySound'
},
{
title: (
@@ -488,7 +419,7 @@ export default {
id="gui.howtos.Make-Music.step_MakeSong"
/>
),
image: stepMakeSong
image: 'musicMakeSong'
}, {
title: (
<FormattedMessage
@@ -497,7 +428,7 @@ export default {
id="gui.howtos.make-music.step_MakeBeat"
/>
),
image: stepMakeBeat
image: 'musicMakeBeat'
}, {
title: (
<FormattedMessage
@@ -506,7 +437,7 @@ export default {
id="gui.howtos.make-music.step_MakeBeatBox"
/>
),
image: stepMakeBeatbox
image: 'musicMakeBeatbox'
}, {
deckIds: [
'add-a-backdrop',
@@ -537,7 +468,7 @@ export default {
id="gui.howtos.Make-A-Game.step_GamePickSprite"
/>
),
image: stepGamePickSprite
image: 'popGamePickSprite'
}, {
title: (
<FormattedMessage
@@ -546,7 +477,7 @@ export default {
id="gui.howtos.make-a-game.step_GamePlaySound"
/>
),
image: stepGamePlaySound
image: 'popGamePlaySound'
}, {
title: (
<FormattedMessage
@@ -555,7 +486,7 @@ export default {
id="gui.howtos.make-a-game.step_GameAddScore"
/>
),
image: stepGameAddScore
image: 'popGameAddScore'
}, {
title: (
<FormattedMessage
@@ -565,7 +496,7 @@ export default {
/>
),
image: stepGameChangeScore
image: 'popGameChangeScore'
}, {
title: (
<FormattedMessage
@@ -574,7 +505,7 @@ export default {
id="gui.howtos.make-a-game.step_Random"
/>
),
image: stepRandom
image: 'popGameRandomPosition'
}, {
title: (
<FormattedMessage
@@ -583,7 +514,7 @@ export default {
id="gui.howtos.make-music.step_GameChangeColor"
/>
),
image: stepGameChangeColor
image: 'popGameChangeColor'
}, {
title: (
<FormattedMessage
@@ -592,7 +523,7 @@ export default {
id="gui.howtos.make-music.step_ResetScore"
/>
),
image: stepResetScore
image: 'popGameResetScore'
}, {
deckIds: [
'add-a-backdrop',
@@ -624,7 +555,7 @@ export default {
id="gui.howtos.Chase-Game.step_BG"
/>
),
image: stepBG
image: 'chaseGameAddBackdrop'
}, {
title: (
<FormattedMessage
@@ -633,7 +564,7 @@ export default {
id="gui.howtos.chase-game.step_AddOcto"
/>
),
image: stepAddOcto
image: 'chaseGameAddSprite1'
}, {
title: (
<FormattedMessage
@@ -642,7 +573,7 @@ export default {
id="gui.howtos.make-music.step_LeftRight"
/>
),
image: stepLeftRight
image: 'chaseGameRightLeft'
}, {
title: (
<FormattedMessage
@@ -651,7 +582,7 @@ export default {
id="gui.howtos.Chase-Game.step_UpDown"
/>
),
image: stepUpDown
image: 'chaseGameUpDown'
}, {
title: (
<FormattedMessage
@@ -660,7 +591,7 @@ export default {
id="gui.howtos.Chase-Game.step_AddStar"
/>
),
image: stepAddStar
image: 'chaseGameAddSprite2'
}, {
title: (
<FormattedMessage
@@ -669,7 +600,7 @@ export default {
id="gui.howtos.Chase-Game.step_MoveRandom"
/>
),
image: stepMoveRandom
image: 'chaseGameMoveRandomly'
}, {
title: (
<FormattedMessage
@@ -678,7 +609,7 @@ export default {
id="gui.howtos.Chase-Game.step_WhenTouch"
/>
),
image: stepWhenTouch
image: 'chaseGamePlaySound'
}, {
title: (
<FormattedMessage
@@ -687,7 +618,7 @@ export default {
id="gui.howtos.Chase-Game.step_ScoreVariable"
/>
),
image: stepScoreVariable
image: 'chaseGameAddVariable'
}, {
title: (
<FormattedMessage
@@ -696,7 +627,7 @@ export default {
id="gui.howtos.Chase-Game.ScoreWhenTouch"
/>
),
image: stepScoreWhenTouch
image: 'chaseGameChangeScore'
}, {
deckIds: [
'add-effects',
@@ -726,7 +657,7 @@ export default {
id="gui.howtos.animate-char.step_addbg"
/>
),
image: stepAnimateCharPickBackdrop
image: 'animateCharPickBackdrop'
}, {
title: (
<FormattedMessage
@@ -735,7 +666,7 @@ export default {
id="gui.howtos.animate-char.step_addsprite"
/>
),
image: stepAnimateCharPickSprite
image: 'animateCharPickSprite'
}, {
title: (
<FormattedMessage
@@ -744,7 +675,7 @@ export default {
id="gui.howtos.animate-char.step_saysomething"
/>
),
image: stepAnimateCharSaySomething
image: 'animateCharSaySomething'
}, {
title: (
<FormattedMessage
@@ -753,7 +684,7 @@ export default {
id="gui.howtos.animate-char.step_addsound"
/>
),
image: stepAnimateCharAddSound
image: 'animateCharAddSound'
}, {
title: (
<FormattedMessage
@@ -762,7 +693,7 @@ export default {
id="gui.howtos.animate-char.step_animatetalking"
/>
),
image: stepAnimateCharTalk
image: 'animateCharTalk'
}, {
title: (
<FormattedMessage
@@ -771,7 +702,7 @@ export default {
id="gui.howtos.animate-char.step_arrowkeys"
/>
),
image: stepAnimateCharMove
image: 'animateCharMove'
}, {
title: (
<FormattedMessage
@@ -780,7 +711,7 @@ export default {
id="gui.howtos.animate-char.step_jump"
/>
),
image: stepAnimateCharJump
image: 'animateCharJump'
}, {
title: (
<FormattedMessage
@@ -789,7 +720,7 @@ export default {
id="gui.howtos.animate-char.step_changecolor"
/>
),
image: stepAnimateCharChangeColor
image: 'animateCharChangeColor'
}, {
deckIds: [
'Chase-Game',
@@ -819,7 +750,7 @@ export default {
id="gui.howtos.story.step_addbg"
/>
),
image: stepStoryPickBackdrop
image: 'storyPickBackdrop'
}, {
title: (
<FormattedMessage
@@ -828,7 +759,7 @@ export default {
id="gui.howtos.story.step_addsprite"
/>
),
image: stepStoryPickSprite
image: 'storyPickSprite'
}, {
title: (
<FormattedMessage
@@ -837,7 +768,7 @@ export default {
id="gui.howtos.story.step_saysomething"
/>
),
image: stepStoryCharSaySomething
image: 'storySaySomething'
}, {
title: (
<FormattedMessage
@@ -846,7 +777,7 @@ export default {
id="gui.howtos.story.step_addanothersprite"
/>
),
image: stepStoryCharAddAnotherSprite
image: 'storyPickSprite2'
}, {
title: (
<FormattedMessage
@@ -855,7 +786,7 @@ export default {
id="gui.howtos.story.step_flip"
/>
),
image: stepStoryFlip
image: 'storyFlip'
}, {
title: (
<FormattedMessage
@@ -864,7 +795,7 @@ export default {
id="gui.howtos.story.step_conversation"
/>
),
image: stepStoryConversation
image: 'storyConversation'
}, {
title: (
<FormattedMessage
@@ -873,7 +804,7 @@ export default {
id="gui.howtos.story.addanotherbg"
/>
),
image: stepStoryAddAnotherBackdrop
image: 'storyPickBackdrop2'
}, {
title: (
<FormattedMessage
@@ -882,7 +813,7 @@ export default {
id="gui.howtos.story.step_swithbg"
/>
),
image: stepStorySwitchBackdrop
image: 'storySwitchBackdrop'
}, {
title: (
<FormattedMessage
@@ -891,7 +822,7 @@ export default {
id="gui.howtos.story.step_hidewizard"
/>
),
image: stepStoryHide
image: 'storyHideCharacter'
}, {
title: (
<FormattedMessage
@@ -900,7 +831,7 @@ export default {
id="gui.howtos.story.step_showwizard"
/>
),
image: stepStoryShow
image: 'storyShowCharacter'
},
{
deckIds: [
@@ -931,7 +862,7 @@ export default {
id="gui.howtos.videosens.step_addextension"
/>
),
image: stepVideoSensAddExtension
image: 'videoAddExtension'
}, {
title: (
<FormattedMessage
@@ -940,7 +871,7 @@ export default {
id="gui.howtos.videosens.step_pet"
/>
),
image: stepVideoSensPet
image: 'videoPet'
}, {
title: (
<FormattedMessage
@@ -949,7 +880,7 @@ export default {
id="gui.howtos.videosens.step_animate"
/>
),
image: stepVideoSensAnimate
image: 'videoAnimate'
}, {
title: (
<FormattedMessage
@@ -958,7 +889,7 @@ export default {
id="gui.howtos.videosens.step_pop"
/>
),
image: stepVideoSensPop
image: 'videoPop'
}, {
deckIds: [
'Make-Music',
@@ -987,7 +918,7 @@ export default {
id="gui.howtos.add-sprite.step_addSprite"
/>
),
image: stepAddSprite
image: 'addSprite'
},
{
deckIds: [
Binary file not shown.

Before

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 652 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 802 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 718 KiB

Loaded 100 of 300 files, more files were not shown because too many files have changed in this diff. Show more