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 7709580710 Move project saving to server to helper, allow external save action 2019-03-18 12:00:01 -04: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 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
Christopher Willis-Ford ce5aa9fb7a Fix ScratchImage visibility check on Windows Firefox ESR 2019-03-13 11:09:36 -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
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
81 changed files with 1165 additions and 386 deletions

No files matched your search

+3 -1
View File
@@ -25,6 +25,7 @@ before_deploy:
if [ -z "$BEFORE_DEPLOY_RAN" ]; then
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
@@ -36,6 +37,7 @@ deploy:
- master
- develop
- smoke
- hotfix/*
condition: $TRAVIS_EVENT_TYPE != cron
skip_cleanup: true
email: $NPM_EMAIL
@@ -65,7 +67,7 @@ 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
+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)
+11 -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",
@@ -90,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",
@@ -100,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.1552662801",
"scratch-l10n": "3.1.20190220143732",
"scratch-paint": "0.2.0-prerelease.20190311150519",
"scratch-render": "0.1.0-prerelease.20190312125229",
"scratch-storage": "1.2.2",
"scratch-svg-renderer": "0.2.0-prerelease.20190304180800",
"scratch-vm": "0.2.0-prerelease.20190318152140",
"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",
+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;
}
+124 -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';
@@ -14,8 +18,8 @@ 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}
@@ -41,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>
);
@@ -69,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
@@ -107,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
@@ -125,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
@@ -141,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
};
@@ -219,6 +249,7 @@ const Cards = props => {
locale,
onActivateDeckFactory,
onCloseCards,
onShrinkExpandCards,
onDrag,
onStartDrag,
onEndDrag,
@@ -226,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={translateImage(steps[step].image, locale)}
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>
);
};
@@ -309,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,
@@ -318,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;
@@ -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 = {
+10 -7
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}>
@@ -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 -7
View File
@@ -9,6 +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 storage from '../../lib/storage';
import styles from './library.css';
@@ -28,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);
@@ -162,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}
@@ -171,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}
@@ -182,8 +225,8 @@ class LibraryComponent extends React.Component {
onMouseEnter={this.handleMouseEnter}
onMouseLeave={this.handleMouseLeave}
onSelect={this.handleSelect}
/>
))}
/>);
})}
</div>
</Modal>
);
@@ -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

+28 -43
View File
@@ -1,5 +1,6 @@
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';
@@ -27,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';
@@ -63,7 +65,6 @@ 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';
@@ -164,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(sharedMessages.replaceProjectWarning)
);
}
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);
@@ -194,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
@@ -393,19 +391,17 @@ 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>
)}
@@ -646,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">
@@ -728,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,
@@ -737,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,
@@ -761,11 +740,12 @@ 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,
userOwnsProject: PropTypes.bool,
username: PropTypes.string,
vm: PropTypes.instanceOf(VM).isRequired
};
@@ -774,7 +754,7 @@ 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 {
@@ -787,10 +767,11 @@ const mapStateToProps = state => {
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,
userOwnsProject: ownProps.authorUsername && user &&
(ownProps.authorUsername === user.username),
vm: state.scratchGui.vm
};
};
@@ -815,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);
@@ -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;
+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
};
+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,
+15
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.
+3
View File
@@ -5,6 +5,7 @@ import React from 'react';
import {
activateDeck,
closeCards,
shrinkExpandCards,
nextStep,
prevStep,
dragCard,
@@ -46,6 +47,7 @@ const mapStateToProps = state => ({
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,
@@ -60,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)),
+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}
+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;
+19 -15
View File
@@ -94,21 +94,28 @@ class SBFileUploader extends React.Component {
intl,
isShowingWithoutId,
loadingState,
projectChanged
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];
// Allow upload to continue only after confirmation if the project
// has changed and is not showing with ID. If it has an ID, this operation
// does not currently overwrite that project, so it is safe to do without confirmation.
const uploadAllowed = (isShowingWithoutId && projectChanged) ?
confirm(intl.formatMessage(sharedMessages.replaceProjectWarning)) : // eslint-disable-line no-alert
true;
if (uploadAllowed) this.props.requestProjectUpload(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
@@ -118,12 +125,6 @@ class SBFileUploader extends React.Component {
const filename = this.fileToUpload && this.fileToUpload.name;
this.props.vm.loadProject(this.reader.result)
.then(() => {
// Remove the hash if any (without triggering a hash change event or a reload)
try { // Can fail e.g. when GUI is loaded from static file (integration tests)
history.replaceState({}, document.title, '.');
} catch {
// No fallback, just do not trigger promise catch below
}
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
@@ -170,6 +171,7 @@ 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,
@@ -179,6 +181,7 @@ SBFileUploader.propTypes = {
onUpdateProjectTitle: PropTypes.func,
projectChanged: PropTypes.bool,
requestProjectUpload: PropTypes.func,
userOwnsProject: PropTypes.bool,
vm: PropTypes.shape({
loadProject: PropTypes.func
})
@@ -198,6 +201,7 @@ const mapStateToProps = state => {
};
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
};
+1
View File
@@ -423,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,
+2 -1
View File
@@ -113,7 +113,8 @@ const AppStateHOC = function (WrappedComponent, localesOnly) {
}
AppStateWrapper.propTypes = {
isFullScreen: PropTypes.bool,
isPlayerOnly: PropTypes.bool
isPlayerOnly: PropTypes.bool,
showTelemetryModal: PropTypes.bool
};
return AppStateWrapper;
};
+22 -4
View File
@@ -246,10 +246,7 @@ export default function (vm) {
lookupBlocks = vm.runtime.flyoutBlocks;
}
const sort = function (options) {
options.sort((str1, str2) => str1.localeCompare(str2, [], {
sensitivity: 'base',
numeric: true
}));
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('');
@@ -323,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;
}
@@ -0,0 +1 @@
<svg width="204" height="92" viewBox="0 0 204 92" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><style>@-webkit-keyframes wiggle{0%{transform:translate(0,-10px)}to{transform:translate(0,0)}}@keyframes wiggle{0%{transform:translate(0,-10px)}to{transform:translate(0,0)}}</style><defs><path d="M66.5 45c-.7 0-1.3-.2-1.7-.7l-9.1-8.8c-.7-.7-.9-1.7-.5-2.6.4-.8 1.2-1.4 2.2-1.4H61l1.7-12.2a4 4 0 0 1 4.5-3.3 4 4 0 0 1 3.3 3.3l1.8 12.2h3.3c1 0 2 .6 2.3 1.5.4.9.2 1.8-.5 2.5l-9.1 8.8c-.5.5-1 .7-1.7.7z" id="path-1"/><filter x="-15.2%" y="-12.1%" width="130.4%" height="124.1%" filterUnits="objectBoundingBox" id="filter-2"><feMorphology radius="1.5" operator="dilate" in="SourceAlpha" result="shadowSpreadOuter1"/><feOffset in="shadowSpreadOuter1" result="shadowOffsetOuter1"/><feComposite in="shadowOffsetOuter1" in2="SourceAlpha" operator="out" result="shadowOffsetOuter1"/><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" in="shadowOffsetOuter1"/></filter></defs><g id="boost-button-illustration" fill="none" fill-rule="evenodd"><g id="boost-top" transform="translate(4 4)" fill-rule="nonzero"><path d="M94 84H2a2 2 0 0 1-2-2V68h96v14a2 2 0 0 1-2 2z" id="Path" stroke="#7C87A5" fill="#FFF" stroke-linecap="round" stroke-linejoin="round"/><path id="Rectangle" stroke="#7C87A5" fill="#FFF" stroke-linecap="round" stroke-linejoin="round" transform="rotate(-90 98 42)" d="M72-56h52v196H72z"/><g id="Group" transform="translate(54 34)"><g opacity=".1" fill="#231F20"><path d="M4 15a3 3 0 0 1-3-3V4a3 3 0 0 1 3-3h8a3 3 0 0 1 3 3v8a3 3 0 0 1-3 3H4z" id="Path"/><path d="M12 2a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V4c0-1.1.9-2 2-2h8zm0-2H4a4 4 0 0 0-4 4v8a4 4 0 0 0 4 4h8a4 4 0 0 0 4-4V4a4 4 0 0 0-4-4z" id="Shape"/></g><path d="M2 4v8c0 1.1.9 2 2 2h8a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2z" id="Path" stroke="#389438" fill="#46B946" stroke-linecap="round" stroke-linejoin="round"/></g><path id="Rectangle" stroke="#231F20" fill="#231F20" opacity=".1" stroke-linecap="round" stroke-linejoin="round" transform="rotate(-90 86 42)" d="M84 32h4v20h-4z"/><g id="Group" transform="translate(76 16)" opacity=".5" stroke="#7C87A5" stroke-linecap="round" stroke-linejoin="round"><path d="M4 10H2a2 2 0 0 1-2-2V0h20v8a2 2 0 0 1-2 2h-2" id="Path" fill="#FFF"/><path id="Path" fill="#7C87A5" d="M14 12l-2-4-2 4zM6 8l2 4 2-4z"/></g><g id="Group" transform="translate(76 56)" opacity=".5" stroke="#7C87A5" stroke-linecap="round" stroke-linejoin="round"><path d="M4 2H2a2 2 0 0 0-2 2v8h20V4a2 2 0 0 0-2-2h-2" id="Path" fill="#FFF"/><path id="Path" fill="#7C87A5" d="M14 0l-2 4-2-4zM6 4l2-4 2 4z"/></g><g id="Group" opacity=".5"><g transform="translate(4 20)"><circle id="Oval" fill="#FFF" cx="4" cy="4" r="3.5"/><path d="M4 1a3 3 0 0 1 3 3 3 3 0 0 1-3 3 3 3 0 0 1-3-3 3 3 0 0 1 3-3zm0-1a4 4 0 1 0 0 8 4 4 0 0 0 0-8z" id="Shape" fill="#7C87A5"/></g><g transform="translate(16 20)"><circle id="Oval" fill="#FFF" cx="4" cy="4" r="3.5"/><path d="M4 1a3 3 0 0 1 3 3 3 3 0 0 1-3 3 3 3 0 0 1-3-3 3 3 0 0 1 3-3zm0-1a4 4 0 1 0 0 8 4 4 0 0 0 0-8z" id="Shape" fill="#7C87A5"/></g><g transform="translate(28 20)"><circle id="Oval" fill="#FFF" cx="4" cy="4" r="3.5"/><path d="M4 1a3 3 0 0 1 3 3 3 3 0 0 1-3 3 3 3 0 0 1-3-3 3 3 0 0 1 3-3zm0-1a4 4 0 1 0 0 8 4 4 0 0 0 0-8z" id="Shape" fill="#7C87A5"/></g><g transform="translate(40 20)"><circle id="Oval" fill="#FFF" cx="4" cy="4" r="3.5"/><path d="M4 1a3 3 0 0 1 3 3 3 3 0 0 1-3 3 3 3 0 0 1-3-3 3 3 0 0 1 3-3zm0-1a4 4 0 1 0 0 8 4 4 0 0 0 0-8z" id="Shape" fill="#7C87A5"/></g><g transform="translate(52 20)"><circle id="Oval" fill="#FFF" cx="4" cy="4" r="3.5"/><path d="M4 1a3 3 0 0 1 3 3 3 3 0 0 1-3 3 3 3 0 0 1-3-3 3 3 0 0 1 3-3zm0-1a4 4 0 1 0 0 8 4 4 0 0 0 0-8z" id="Shape" fill="#7C87A5"/></g><g transform="translate(64 20)"><circle id="Oval" fill="#FFF" cx="4" cy="4" r="3.5"/><path d="M4 1a3 3 0 0 1 3 3 3 3 0 0 1-3 3 3 3 0 0 1-3-3 3 3 0 0 1 3-3zm0-1a4 4 0 1 0 0 8 4 4 0 0 0 0-8z" id="Shape" fill="#7C87A5"/></g></g><g id="Group" opacity=".5"><g transform="translate(124 20)"><circle id="Oval" fill="#FFF" cx="4" cy="4" r="3.5"/><path d="M4 1a3 3 0 0 1 3 3 3 3 0 0 1-3 3 3 3 0 0 1-3-3 3 3 0 0 1 3-3zm0-1a4 4 0 1 0 0 8 4 4 0 0 0 0-8z" id="Shape" fill="#7C87A5"/></g><g transform="translate(136 20)"><circle id="Oval" fill="#FFF" cx="4" cy="4" r="3.5"/><path d="M4 1a3 3 0 0 1 3 3 3 3 0 0 1-3 3 3 3 0 0 1-3-3 3 3 0 0 1 3-3zm0-1a4 4 0 1 0 0 8 4 4 0 0 0 0-8z" id="Shape" fill="#7C87A5"/></g><g transform="translate(100 20)"><circle id="Oval" fill="#FFF" cx="4" cy="4" r="3.5"/><path d="M4 1a3 3 0 0 1 3 3 3 3 0 0 1-3 3 3 3 0 0 1-3-3 3 3 0 0 1 3-3zm0-1a4 4 0 1 0 0 8 4 4 0 0 0 0-8z" id="Shape" fill="#7C87A5"/></g><g transform="translate(112 20)"><circle id="Oval" fill="#FFF" cx="4" cy="4" r="3.5"/><path d="M4 1a3 3 0 0 1 3 3 3 3 0 0 1-3 3 3 3 0 0 1-3-3 3 3 0 0 1 3-3zm0-1a4 4 0 1 0 0 8 4 4 0 0 0 0-8z" id="Shape" fill="#7C87A5"/></g><g transform="translate(148 20)"><circle id="Oval" fill="#FFF" cx="4"Line truncated

After

Width:  |  Height:  |  Size: 24 KiB

@@ -0,0 +1 @@
<svg width="204" height="72" viewBox="0 0 204 72" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><path stroke="#7C87A5" fill="#FFF" fill-rule="nonzero" stroke-linecap="round" stroke-linejoin="round" d="M100 24h100v40H100z"/><path d="M76 8h-8V5c0-.6.5-1 1-1h6c.6 0 1 .5 1 1v3z" fill="#FFF" fill-rule="nonzero"/><path d="M73.3 4v4H76V4.7c0-.4-.4-.7-1-.7h-1.7z" stroke="#231F20" fill="#231F20" fill-rule="nonzero" opacity=".1" stroke-linecap="round" stroke-linejoin="round"/><path d="M76 8h-8V5c0-.6.5-1 1-1h6c.6 0 1 .5 1 1v3z" stroke="#7C87A5" stroke-linecap="round" stroke-linejoin="round"/><path d="M64 8h-8V5c0-.6.5-1 1-1h6c.5 0 1 .5 1 1v3z" fill="#FFF" fill-rule="nonzero"/><path d="M61.3 4v4H64V4.7c0-.4-.4-.7-1-.7h-1.7z" stroke="#231F20" fill="#231F20" fill-rule="nonzero" opacity=".1" stroke-linecap="round" stroke-linejoin="round"/><path d="M64 8h-8V5c0-.6.5-1 1-1h6c.5 0 1 .5 1 1v3z" stroke="#7C87A5" stroke-linecap="round" stroke-linejoin="round"/><path d="M52 8h-8V5c0-.6.5-1 1-1h6c.5 0 1 .5 1 1v3z" fill="#FFF" fill-rule="nonzero"/><path d="M49.3 4v4H52V4.7c0-.4-.4-.7-1-.7h-1.7z" stroke="#231F20" fill="#231F20" fill-rule="nonzero" opacity=".1" stroke-linecap="round" stroke-linejoin="round"/><path d="M52 8h-8V5c0-.6.5-1 1-1h6c.5 0 1 .5 1 1v3z" stroke="#7C87A5" stroke-linecap="round" stroke-linejoin="round"/><path d="M40 8h-8V5c0-.6.5-1 1-1h6c.5 0 1 .5 1 1v3z" fill="#FFF" fill-rule="nonzero"/><path d="M37.3 4v4H40V4.7c0-.4-.4-.7-1-.7h-1.7z" stroke="#231F20" fill="#231F20" fill-rule="nonzero" opacity=".1" stroke-linecap="round" stroke-linejoin="round"/><path d="M40 8h-8V5c0-.6.5-1 1-1h6c.5 0 1 .5 1 1v3z" stroke="#7C87A5" stroke-linecap="round" stroke-linejoin="round"/><path d="M28 8h-8V5c0-.6.5-1 1-1h6c.6 0 1 .5 1 1v3z" fill="#FFF" fill-rule="nonzero"/><path d="M25.3 4v4H28V4.7c0-.4-.4-.7-1-.7h-1.7z" stroke="#231F20" fill="#231F20" fill-rule="nonzero" opacity=".1" stroke-linecap="round" stroke-linejoin="round"/><path d="M28 8h-8V5c0-.6.5-1 1-1h6c.6 0 1 .5 1 1v3z" stroke="#7C87A5" stroke-linecap="round" stroke-linejoin="round"/><path d="M16 8H8V5c0-.6.5-1 1-1h6c.5 0 1 .5 1 1v3z" fill="#FFF" fill-rule="nonzero"/><path d="M13.3 4v4H16V4.7c0-.4-.4-.7-1-.7h-1.7z" stroke="#231F20" fill="#231F20" fill-rule="nonzero" opacity=".1" stroke-linecap="round" stroke-linejoin="round"/><path d="M16 8H8V5c0-.6.5-1 1-1h6c.5 0 1 .5 1 1v3z" stroke="#7C87A5" stroke-linecap="round" stroke-linejoin="round"/><path d="M172 8h-8V5c0-.6.5-1 1-1h6c.5 0 1 .5 1 1v3z" fill="#FFF" fill-rule="nonzero"/><path d="M169.3 4v4h2.7V4.7c0-.4-.4-.7-1-.7h-1.7z" stroke="#231F20" fill="#231F20" fill-rule="nonzero" opacity=".1" stroke-linecap="round" stroke-linejoin="round"/><path d="M172 8h-8V5c0-.6.5-1 1-1h6c.5 0 1 .5 1 1v3z" stroke="#7C87A5" stroke-linecap="round" stroke-linejoin="round"/><path d="M160 8h-8V5c0-.6.5-1 1-1h6c.5 0 1 .5 1 1v3z" fill="#FFF" fill-rule="nonzero"/><path d="M157.3 4v4h2.7V4.7c0-.4-.4-.7-1-.7h-1.7z" stroke="#231F20" fill="#231F20" fill-rule="nonzero" opacity=".1" stroke-linecap="round" stroke-linejoin="round"/><path d="M160 8h-8V5c0-.6.5-1 1-1h6c.5 0 1 .5 1 1v3z" stroke="#7C87A5" stroke-linecap="round" stroke-linejoin="round"/><path d="M148 8h-8V5c0-.6.5-1 1-1h6c.5 0 1 .5 1 1v3z" fill="#FFF" fill-rule="nonzero"/><path d="M145.3 4v4h2.7V4.7c0-.4-.4-.7-1-.7h-1.7z" stroke="#231F20" fill="#231F20" fill-rule="nonzero" opacity=".1" stroke-linecap="round" stroke-linejoin="round"/><path d="M148 8h-8V5c0-.6.5-1 1-1h6c.5 0 1 .5 1 1v3z" stroke="#7C87A5" stroke-linecap="round" stroke-linejoin="round"/><path d="M136 8h-8V5c0-.6.5-1 1-1h6c.5 0 1 .5 1 1v3z" fill="#FFF" fill-rule="nonzero"/><path d="M133.3 4v4h2.7V4.7c0-.4-.4-.7-1-.7h-1.7z" stroke="#231F20" fill="#231F20" fill-rule="nonzero" opacity=".1" stroke-linecap="round" stroke-linejoin="round"/><path d="M136 8h-8V5c0-.6.5-1 1-1h6c.5 0 1 .5 1 1v3z" stroke="#7C87A5" stroke-linecap="round" stroke-linejoin="round"/><path d="M124 8h-8V5c0-.6.5-1 1-1h6c.6 0 1 .5 1 1v3z" fill="#FFF" fill-rule="nonzero"/><path d="M121.3 4v4h2.7V4.7c0-.4-.4-.7-1-.7h-1.7z" stroke="#231F20" fill="#231F20" fill-rule="nonzero" opacity=".1" stroke-linecap="round" stroke-linejoin="round"/><path d="M124 8h-8V5c0-.6.5-1 1-1h6c.6 0 1 .5 1 1v3z" stroke="#7C87A5" stroke-linecap="round" stroke-linejoin="round"/><path d="M112 8h-8V5c0-.6.5-1 1-1h6c.6 0 1 .5 1 1v3z" fill="#FFF" fill-rule="nonzero"/><path d="M109.3 4v4h2.7V4.7c0-.4-.4-.7-1-.7h-1.7z" stroke="#231F20" fill="#231F20" fill-rule="nonzero" opacity=".1" stroke-linecap="round" stroke-linejoin="round"/><path d="M112 8h-8V5c0-.6.5-1 1-1h6c.6 0 1 .5 1 1v3z" stroke="#7C87A5" stroke-linecap="round" stroke-linejoin="round"/><path d="M198 8H6a2 2 0 0 0-2 2v14h196V10a2 2 0 0 0-2-2z" stroke="#7C87A5" fill="#FFF" fill-rule="nonzero" stroke-linecap="round" stroke-linejoin="round"/><g stroke-linecap="round" stroke-linejoin="round"><path stroke="#7C87A5" fill="#E6E7E8" fill-rule="nonzero" d="M100 20h-2v-2H82v2h-2V10h20z"/><path stroke="#7C87A5" fill="#7C87A5" filLine truncated

After

Width:  |  Height:  |  Size: 13 KiB

@@ -0,0 +1 @@
<svg width="76" height="133" xmlns="http://www.w3.org/2000/svg"><g transform="translate(2 2)" fill="none" fill-rule="evenodd"><g fill="#414757" fill-rule="nonzero" stroke="#414757" stroke-linecap="round" stroke-linejoin="round"><path d="M42 0v3H30V0h4.5v1.5h3V0zM34.5 3h3v6h-3zM34.5 3H33l.8.8-.8.7.8.8-.8.7h1.5zM34.5 6H33l.8.8-.8.7.8.8-.8.7h1.5z"/><path d="M37.5 2.3H39l-.8.7.8.8-.8.7.8.8h-1.5zM37.5 5.3H39l-.8.7.8.8-.8.7.8.8-.8.7h-.7zM34.5 3h3v6h-3zM34.5 3H33l.8.8-.8.7.8.8-.8.7h1.5zM34.5 6H33l.8.8-.8.7.8.8-.8.7h1.5z"/><path d="M37.5 2.3H39l-.8.7.8.8-.8.7.8.8h-1.5zM37.5 5.3H39l-.8.7.8.8-.8.7.8.8-.8.7h-.7z"/></g><g fill-rule="nonzero" stroke-linecap="round" stroke-linejoin="round"><path d="M36 129a7.5 7.5 0 0 1-7.5-7.5c0-.8.7-1.5 1.5-1.5s1.5.7 1.5 1.5a4.5 4.5 0 0 0 9 0v-.5c0-1.2-.5-2.4-1.3-3.2l-2.5-2.5a7.4 7.4 0 0 1-2.2-5.3v-3.5c0-.8.7-1.5 1.5-1.5s1.5.7 1.5 1.5v3.5c0 1.2.5 2.4 1.3 3.2l2.5 2.5a7.4 7.4 0 0 1 2.2 5.3v.5c0 4.1-3.4 7.5-7.5 7.5zM31.5 93h9v8.3h-9z" stroke="#7C87A5" fill="#E6E7E8"/><path stroke="#231F20" fill="#231F20" opacity=".1" d="M31.5 93h2.3v8.3h-2.3zM38.3 93h2.3v8.3h-2.3z"/><path d="M42 105.8v1.4c0 .5-.3.8-.8.8H30.8a.8.8 0 0 1-.7-.8v-1.4c0-.5.3-.8.8-.8h1c.7 0 1.2-.5 1.2-1.1 0-.6-.5-1.1-1.1-1.1h-.4v-1.5h9v1.5h-.4c-.6 0-1.1.5-1.1 1 0 .7.5 1.2 1.1 1.2h1.1c.5 0 .8.3.8.8z" stroke="#7C87A5" fill="#8592AF"/><path d="M31.9 105c.6 0 1.1-.5 1.1-1.1 0-.6-.5-1.1-1.1-1.1h-.4 9-.4c-.6 0-1.1.5-1.1 1 0 .7.5 1.2 1.1 1.2H32z" stroke="#7C87A5" fill="#7C87A5"/></g><path d="M70.5 9h-69C.7 9 0 9.7 0 10.5v84c0 .8.7 1.5 1.5 1.5h69c.8 0 1.5-.7 1.5-1.5v-84c0-.8-.7-1.5-1.5-1.5zM39.9 39.2a4.2 4.2 0 0 1-7.8 0l-2.3-6.7a6.5 6.5 0 1 1 12.4 0l-2.3 6.7z" stroke="#7C87A5" fill="#8592AF" fill-rule="nonzero" stroke-linecap="round" stroke-linejoin="round"/><path d="M6 15v75h60V15H6zm34 24.2a4.2 4.2 0 0 1-8 0l-2.2-6.7a6.5 6.5 0 1 1 12.4 0l-2.3 6.7z" stroke="#7C87A5" fill="#8592AF" fill-rule="nonzero" stroke-linecap="round" stroke-linejoin="round"/><path d="M9 18v69h54V18H9zm27 6c4.4 0 7.6 4.3 6.2 8.5l-2.3 6.7a4.2 4.2 0 0 1-7.8 0l-2.3-6.7A6.5 6.5 0 0 1 36 24z" stroke="#7C87A5" fill="#5CB1D6" fill-rule="nonzero" stroke-linecap="round" stroke-linejoin="round"/><path stroke="#7C87A5" fill="#8592AF" fill-rule="nonzero" stroke-linecap="round" stroke-linejoin="round" d="M63 87H9V69l54-12z"/><path d="M6 15L.7 9.7M66 15l5.3-5.3M.8 95.3L6 90M71.3 95.3L66 90M15 91.5V93M57 91.5V93M15 12v1.5M57 12v1.5M69 27h-1.5M69 81h-1.5M4.5 27H3M4.5 81H3" stroke="#7C87A5" stroke-linecap="round" stroke-linejoin="round"/><circle fill="#FFBF00" fill-rule="nonzero" cx="36" cy="52.5" r="4.5"/><circle stroke="#7C87A5" fill="#8592AF" fill-rule="nonzero" stroke-linecap="round" stroke-linejoin="round" cx="25.5" cy="52.5" r="1.5"/><circle stroke="#7C87A5" fill="#8592AF" fill-rule="nonzero" stroke-linecap="round" stroke-linejoin="round" cx="46.5" cy="52.5" r="1.5"/><path d="M46.5 75h-21c-.8 0-1.5-.7-1.5-1.5s.7-1.5 1.5-1.5h21c.8 0 1.5.7 1.5 1.5s-.7 1.5-1.5 1.5zM52.5 81h-33c-.8 0-1.5-.7-1.5-1.5s.7-1.5 1.5-1.5h33c.8 0 1.5.7 1.5 1.5s-.7 1.5-1.5 1.5z" fill="#FFBF00" fill-rule="nonzero"/><path d="M1.3 94.7L6 90h60V15l4.7-4.7a.8.8 0 0 1 1.3.5v83.7c0 .8-.7 1.5-1.5 1.5H1.8c-.7 0-1-.8-.5-1.3z" stroke="#231F20" fill="#231F20" fill-rule="nonzero" opacity=".1" stroke-linecap="round" stroke-linejoin="round"/></g></svg>

After

Width:  |  Height:  |  Size: 3.2 KiB

@@ -0,0 +1 @@
<svg width="40" height="40" xmlns="http://www.w3.org/2000/svg"><g transform="translate(8 .5)" fill="none" fill-rule="evenodd"><path d="M12 39a3 3 0 0 1-3-3c0-.6.4-1 1-1 .5 0 1 .4 1 1a1 1 0 1 0 2 0v-.2a1 1 0 0 0-.3-.7l-.8-.8a3 3 0 0 1-1-2.1V30c0-.6.5-1 1-1 .6 0 1 .4 1 1v2.2l.4.7.8.8c.6.6.9 1.3.9 2.1v.2a3 3 0 0 1-3 3z" stroke-opacity=".1" stroke="#000" fill="#E6E7E8"/><path d="M23.7.3A1 1 0 0 0 23 0H1a1 1 0 0 0-.7.3A1 1 0 0 0 0 1v26c0 .3.1.5.3.7.2.2.4.3.7.3h22c.3 0 .5-.1.7-.3.2-.2.3-.4.3-.7V1a1 1 0 0 0-.3-.7zM12 4c2 0 3.3 2 2.6 3.8L13.3 11a1.4 1.4 0 0 1-2.7 0L9.5 7.7l-.2-1C9.2 5.4 10.4 4 12 4z" stroke="#7C87A5" fill="#8592AF" fill-rule="nonzero" stroke-linecap="round" stroke-linejoin="round"/><path d="M2 2v24h20V2H2zm10 2c2 0 3.3 2 2.6 3.8L13.3 11a1.4 1.4 0 0 1-2.7 0L9.5 7.7l-.2-1C9.2 5.4 10.4 4 12 4z" stroke="#7C87A5" fill="#5CB1D6" fill-rule="nonzero" stroke-linecap="round" stroke-linejoin="round"/><path stroke="#7C87A5" fill="#8592AF" stroke-linecap="round" stroke-linejoin="round" d="M22 26H2v-6l20-4z"/><path d="M.3 27.7L2 26M.3.3L2 2M22 2L23.7.3M23.7 27.7L22 26" stroke="#7C87A5" stroke-linecap="round" stroke-linejoin="round"/><circle fill="#FFBF00" cx="12" cy="14.8" r="1.2"/><path stroke="#7C87A5" fill="#E6E7E8" stroke-linecap="round" stroke-linejoin="round" d="M10 28h4v4h-4z"/><path d="M15.5 22h-7a.5.5 0 0 1-.5-.5c0-.3.2-.5.5-.5h7c.3 0 .5.2.5.5s-.2.5-.5.5zM17.5 24h-11a.5.5 0 0 1-.5-.5c0-.3.2-.5.5-.5h11c.3 0 .5.2.5.5s-.2.5-.5.5z" fill="#FFBF00"/></g></svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

+121 -59
View File
@@ -1,31 +1,50 @@
import React from 'react';
import {FormattedMessage} from 'react-intl';
import musicImage from './music.png';
import musicInsetImage from './music-small.svg';
import penImage from './pen.png';
import penInsetImage from './pen-small.svg';
import videoImage from './video-sensing.png';
import videoInsetImage from './video-sensing-small.svg';
import translateImage from './translate.png';
import translateInsetImage from './translate-small.png';
import microbitImage from './microbit.png';
import ev3Image from './ev3.png';
import wedoImage from './wedo.png';
import text2speechImage from './text2speech.png';
import text2speechInsetImage from './text2speech-small.svg';
import makeymakeyImage from './makeymakey.png';
import makeymakeyInsetImage from './makeymakey-small.svg';
import boostImage from './boost.png';
import boostInsetImage from './boost-small.svg';
import musicIconURL from './music/music.png';
import musicInsetIconURL from './music/music-small.svg';
import microbitPeripheralImage from './peripheral-connection/microbit/microbit-illustration.svg';
import microbitMenuImage from './peripheral-connection/microbit/microbit-small.svg';
import ev3PeripheralImage from './peripheral-connection/ev3/ev3-hub-illustration.svg';
import ev3MenuImage from './peripheral-connection/ev3/ev3-small.svg';
import wedoPeripheralImage from './peripheral-connection/wedo/wedo-illustration.svg';
import wedoMenuImage from './peripheral-connection/wedo/wedo-small.svg';
import wedoButtonImage from './peripheral-connection/wedo/wedo-button-illustration.svg';
import penIconURL from './pen/pen.png';
import penInsetIconURL from './pen/pen-small.svg';
import videoSensingIconURL from './videoSensing/video-sensing.png';
import videoSensingInsetIconURL from './videoSensing/video-sensing-small.svg';
import text2speechIconURL from './text2speech/text2speech.png';
import text2speechInsetIconURL from './text2speech/text2speech-small.svg';
import translateIconURL from './translate/translate.png';
import translateInsetIconURL from './translate/translate-small.png';
import makeymakeyIconURL from './makeymakey/makeymakey.png';
import makeymakeyInsetIconURL from './makeymakey/makeymakey-small.svg';
import microbitIconURL from './microbit/microbit.png';
import microbitInsetIconURL from './microbit/microbit-small.svg';
import microbitConnectionIconURL from './microbit/microbit-illustration.svg';
import microbitConnectionSmallIconURL from './microbit/microbit-small.svg';
import ev3IconURL from './ev3/ev3.png';
import ev3InsetIconURL from './ev3/ev3-small.svg';
import ev3ConnectionIconURL from './ev3/ev3-hub-illustration.svg';
import ev3ConnectionSmallIconURL from './ev3/ev3-small.svg';
import wedo2IconURL from './wedo2/wedo.png'; // TODO: Rename file names to match variable/prop names?
import wedo2InsetIconURL from './wedo2/wedo-small.svg';
import wedo2ConnectionIconURL from './wedo2/wedo-illustration.svg';
import wedo2ConnectionSmallIconURL from './wedo2/wedo-small.svg';
import wedo2ConnectionTipIconURL from './wedo2/wedo-button-illustration.svg';
import boostIconURL from './boost/boost.png';
import boostInsetIconURL from './boost/boost-small.svg';
import boostConnectionIconURL from './boost/boost-illustration.svg';
import boostConnectionSmallIconURL from './boost/boost-small.svg';
import boostConnectionTipIconURL from './boost/boost-button-illustration.svg';
import gdxforIconURL from './gdxfor/gdxfor.png';
import gdxforInsetIconURL from './gdxfor/gdxfor-small.svg';
import gdxforConnectionIconURL from './gdxfor/gdxfor-illustration.svg';
import gdxforConnectionSmallIconURL from './gdxfor/gdxfor-small.svg';
export default [
{
@@ -37,8 +56,8 @@ export default [
/>
),
extensionId: 'music',
iconURL: musicImage,
insetIconURL: musicInsetImage,
iconURL: musicIconURL,
insetIconURL: musicInsetIconURL,
description: (
<FormattedMessage
defaultMessage="Play instruments and drums."
@@ -57,8 +76,8 @@ export default [
/>
),
extensionId: 'pen',
iconURL: penImage,
insetIconURL: penInsetImage,
iconURL: penIconURL,
insetIconURL: penInsetIconURL,
description: (
<FormattedMessage
defaultMessage="Draw with your sprites."
@@ -77,8 +96,8 @@ export default [
/>
),
extensionId: 'videoSensing',
iconURL: videoImage,
insetIconURL: videoInsetImage,
iconURL: videoSensingIconURL,
insetIconURL: videoSensingInsetIconURL,
description: (
<FormattedMessage
defaultMessage="Sense motion with the camera."
@@ -98,8 +117,8 @@ export default [
),
extensionId: 'text2speech',
collaborator: 'Amazon Web Services',
iconURL: text2speechImage,
insetIconURL: text2speechInsetImage,
iconURL: text2speechIconURL,
insetIconURL: text2speechInsetIconURL,
description: (
<FormattedMessage
defaultMessage="Make your projects talk."
@@ -120,8 +139,8 @@ export default [
),
extensionId: 'translate',
collaborator: 'Google',
iconURL: translateImage,
insetIconURL: translateInsetImage,
iconURL: translateIconURL,
insetIconURL: translateInsetIconURL,
description: (
<FormattedMessage
defaultMessage="Translate text into many languages."
@@ -136,8 +155,8 @@ export default [
name: 'Makey Makey',
extensionId: 'makeymakey',
collaborator: 'JoyLabz',
iconURL: makeymakeyImage,
insetIconURL: makeymakeyInsetImage,
iconURL: makeymakeyIconURL,
insetIconURL: makeymakeyInsetIconURL,
description: (
<FormattedMessage
defaultMessage="Make anything into a key."
@@ -151,8 +170,8 @@ export default [
name: 'micro:bit',
extensionId: 'microbit',
collaborator: 'micro:bit',
iconURL: microbitImage,
insetIconURL: microbitMenuImage,
iconURL: microbitIconURL,
insetIconURL: microbitInsetIconURL,
description: (
<FormattedMessage
defaultMessage="Connect your projects with the world."
@@ -166,8 +185,8 @@ export default [
internetConnectionRequired: true,
launchPeripheralConnectionFlow: true,
useAutoScan: false,
peripheralImage: microbitPeripheralImage,
smallPeripheralImage: microbitMenuImage,
connectionIconURL: microbitConnectionIconURL,
connectionSmallIconURL: microbitConnectionSmallIconURL,
connectingMessage: (
<FormattedMessage
defaultMessage="Connecting"
@@ -181,8 +200,8 @@ export default [
name: 'LEGO MINDSTORMS EV3',
extensionId: 'ev3',
collaborator: 'LEGO',
iconURL: ev3Image,
insetIconURL: ev3MenuImage,
iconURL: ev3IconURL,
insetIconURL: ev3InsetIconURL,
description: (
<FormattedMessage
defaultMessage="Build interactive robots and more."
@@ -196,8 +215,8 @@ export default [
internetConnectionRequired: true,
launchPeripheralConnectionFlow: true,
useAutoScan: false,
peripheralImage: ev3PeripheralImage,
smallPeripheralImage: ev3MenuImage,
connectionIconURL: ev3ConnectionIconURL,
connectionSmallIconURL: ev3ConnectionSmallIconURL,
connectingMessage: (
<FormattedMessage
defaultMessage="Connecting. Make sure the pin on your EV3 is set to 1234."
@@ -207,12 +226,43 @@ export default [
),
helpLink: 'https://scratch.mit.edu/ev3'
},
{
name: 'LEGO BOOST',
extensionId: 'boost',
collaborator: 'LEGO',
iconURL: boostIconURL,
insetIconURL: boostInsetIconURL,
description: (
<FormattedMessage
defaultMessage="Bring robotic creations to life."
description="Description for the 'LEGO BOOST' extension"
id="gui.extension.boost.description"
/>
),
featured: true,
disabled: false,
bluetoothRequired: true,
internetConnectionRequired: true,
launchPeripheralConnectionFlow: true,
useAutoScan: true,
connectionIconURL: boostConnectionIconURL,
connectionSmallIconURL: boostConnectionSmallIconURL,
connectionTipIconURL: boostConnectionTipIconURL,
connectingMessage: (
<FormattedMessage
defaultMessage="Connecting"
description="Message to help people connect to their BOOST."
id="gui.extension.boost.connectingMessage"
/>
),
helpLink: 'https://scratch.mit.edu/boost'
},
{
name: 'LEGO Education WeDo 2.0',
extensionId: 'wedo2',
collaborator: 'LEGO',
iconURL: wedoImage,
insetIconURL: wedoMenuImage,
iconURL: wedo2IconURL,
insetIconURL: wedo2InsetIconURL,
description: (
<FormattedMessage
defaultMessage="Build with motors and sensors."
@@ -226,9 +276,9 @@ export default [
internetConnectionRequired: true,
launchPeripheralConnectionFlow: true,
useAutoScan: true,
peripheralImage: wedoPeripheralImage,
smallPeripheralImage: wedoMenuImage,
peripheralButtonImage: wedoButtonImage,
connectionIconURL: wedo2ConnectionIconURL,
connectionSmallIconURL: wedo2ConnectionSmallIconURL,
connectionTipIconURL: wedo2ConnectionTipIconURL,
connectingMessage: (
<FormattedMessage
defaultMessage="Connecting"
@@ -239,21 +289,33 @@ export default [
helpLink: 'https://scratch.mit.edu/wedo'
},
{
name: 'LEGO BOOST',
extensionId: 'boost',
collaborator: 'LEGO',
iconURL: boostImage,
insetIconURL: boostInsetImage,
name: 'Go Direct Force & Acceleration',
extensionId: 'gdxfor',
collaborator: 'Vernier',
iconURL: gdxforIconURL,
insetIconURL: gdxforInsetIconURL,
description: (
<FormattedMessage
defaultMessage="Bring robotic creations to life."
description="Description for the 'LEGO BOOST' extension"
id="gui.extension.boost.description"
defaultMessage="Sense push, pull, motion, and spin."
description="Description for the Vernier Go Direct Force and Acceleration sensor extension"
id="gui.extension.gdxfor.description"
/>
),
featured: true,
disabled: true,
disabled: false,
bluetoothRequired: true,
internetConnectionRequired: true
internetConnectionRequired: true,
launchPeripheralConnectionFlow: true,
useAutoScan: false,
connectionIconURL: gdxforConnectionIconURL,
connectionSmallIconURL: gdxforConnectionSmallIconURL,
connectingMessage: (
<FormattedMessage
defaultMessage="Connecting"
description="Message to help people connect to their force and acceleration sensor."
id="gui.extension.gdxfor.connectingMessage"
/>
),
helpLink: 'https://scratch.mit.edu/vernier'
}
];
+2
View File
@@ -115,8 +115,10 @@ const ProjectFetcherHOC = function (WrappedComponent) {
assetHost: PropTypes.string,
canSave: PropTypes.bool,
intl: intlShape.isRequired,
isCreatingNew: PropTypes.bool,
isFetchingWithId: PropTypes.bool,
isLoadingProject: PropTypes.bool,
isShowingProject: PropTypes.bool,
loadingState: PropTypes.oneOf(LoadingStates),
onActivateTab: PropTypes.func,
onError: PropTypes.func,
+25 -48
View File
@@ -1,15 +1,14 @@
import bindAll from 'lodash.bindall';
import React from 'react';
import PropTypes from 'prop-types';
import queryString from 'query-string';
import {connect} from 'react-redux';
import VM from 'scratch-vm';
import xhr from 'xhr';
import collectMetadata from '../lib/collect-metadata';
import log from '../lib/log';
import storage from '../lib/storage';
import dataURItoBlob from '../lib/data-uri-to-blob';
import saveProjectToServer from '../lib/save-project-to-server';
import {
showAlertWithTimeout,
@@ -60,7 +59,12 @@ const ProjectSaverHOC = function (WrappedComponent) {
// but then it'd be hard to turn this listening off in our tests
window.onbeforeunload = e => this.leavePageConfirm(e);
}
// Allow the GUI consumer to pass in a function to receive a trigger
// for triggering thumbnail or whole project saves.
// These functions are called with null on unmount to prevent stale references.
this.props.onSetProjectThumbnailer(this.getProjectThumbnail);
this.props.onSetProjectSaver(this.tryToAutoSave);
}
componentDidUpdate (prevProps) {
if (!this.props.isAnyCreatingNewState && prevProps.isAnyCreatingNewState) {
@@ -117,6 +121,7 @@ const ProjectSaverHOC = function (WrappedComponent) {
// window.onbeforeunload = undefined; // eslint-disable-line no-undefined
// Remove project thumbnailer function since the components are unmounting
this.props.onSetProjectThumbnailer(null);
this.props.onSetProjectSaver(null);
}
leavePageConfirm (e) {
if (this.props.projectChanged) {
@@ -176,8 +181,8 @@ const ProjectSaverHOC = function (WrappedComponent) {
createCopyToStorage () {
this.props.onShowCreatingCopyAlert();
return this.storeProject(null, {
original_id: this.props.reduxProjectId,
is_copy: 1,
originalId: this.props.reduxProjectId,
isCopy: 1,
title: this.props.reduxProjectTitle
})
.then(response => {
@@ -192,8 +197,8 @@ const ProjectSaverHOC = function (WrappedComponent) {
createRemixToStorage () {
this.props.onShowCreatingRemixAlert();
return this.storeProject(null, {
original_id: this.props.reduxProjectId,
is_remix: 1,
originalId: this.props.reduxProjectId,
isRemix: 1,
title: this.props.reduxProjectTitle
})
.then(response => {
@@ -233,47 +238,8 @@ const ProjectSaverHOC = function (WrappedComponent) {
() => (asset.clean = true)
)
)
).then(() => {
const opts = {
body: savedVMState,
// If we set json:true then the body is double-stringified, so don't
headers: {
'Content-Type': 'application/json'
},
withCredentials: true
};
const creatingProject = projectId === null || typeof projectId === 'undefined';
let qs = queryString.stringify(requestParams);
if (qs) qs = `?${qs}`;
if (creatingProject) {
Object.assign(opts, {
method: 'post',
url: `${storage.projectHost}/${qs}`
});
} else {
Object.assign(opts, {
method: 'put',
url: `${storage.projectHost}/${projectId}${qs}`
});
}
return new Promise((resolve, reject) => {
xhr(opts, (err, response) => {
if (err) return reject(err);
let body;
try {
// Since we didn't set json: true, we have to parse manually
body = JSON.parse(response.body);
} catch (e) {
return reject(e);
}
body.id = projectId;
if (creatingProject) {
body.id = body['content-name'];
}
resolve(body);
});
});
})
)
.then(() => this.props.onUpdateProjectData(projectId, savedVMState, requestParams))
.then(response => {
this.props.onSetProjectUnchanged();
const id = response.id.toString();
@@ -350,6 +316,7 @@ const ProjectSaverHOC = function (WrappedComponent) {
onRemixing,
onSetProjectUnchanged,
onSetProjectThumbnailer,
onSetProjectSaver,
onShowAlert,
onShowCopySuccessAlert,
onShowRemixSuccessAlert,
@@ -358,6 +325,7 @@ const ProjectSaverHOC = function (WrappedComponent) {
onShowSaveSuccessAlert,
onShowSavingAlert,
onUpdatedProject,
onUpdateProjectData,
onUpdateProjectThumbnail,
reduxProjectId,
reduxProjectTitle,
@@ -375,6 +343,7 @@ const ProjectSaverHOC = function (WrappedComponent) {
}
ProjectSaverComponent.propTypes = {
autoSaveIntervalSecs: PropTypes.number.isRequired,
autoSaveTimeoutId: PropTypes.number,
canCreateNew: PropTypes.bool,
canSave: PropTypes.bool,
@@ -390,12 +359,16 @@ const ProjectSaverHOC = function (WrappedComponent) {
isShowingWithoutId: PropTypes.bool,
isUpdating: PropTypes.bool,
loadingState: PropTypes.oneOf(LoadingStates),
locale: PropTypes.string.isRequired,
onAutoUpdateProject: PropTypes.func,
onCreateProject: PropTypes.func,
onCreatedProject: PropTypes.func,
onProjectError: PropTypes.func,
onProjectTelemetryEvent: PropTypes.func,
onRemixing: PropTypes.func,
onSetProjectSaver: PropTypes.func.isRequired,
onSetProjectThumbnailer: PropTypes.func.isRequired,
onSetProjectUnchanged: PropTypes.func.isRequired,
onShowAlert: PropTypes.func,
onShowCopySuccessAlert: PropTypes.func,
onShowCreatingCopyAlert: PropTypes.func,
@@ -403,17 +376,21 @@ const ProjectSaverHOC = function (WrappedComponent) {
onShowRemixSuccessAlert: PropTypes.func,
onShowSaveSuccessAlert: PropTypes.func,
onShowSavingAlert: PropTypes.func,
onUpdateProjectData: PropTypes.func.isRequired,
onUpdateProjectThumbnail: PropTypes.func,
onUpdatedProject: PropTypes.func,
projectChanged: PropTypes.bool,
reduxProjectId: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
reduxProjectTitle: PropTypes.string,
setAutoSaveTimeoutId: PropTypes.func.isRequired,
vm: PropTypes.instanceOf(VM).isRequired
};
ProjectSaverComponent.defaultProps = {
autoSaveIntervalSecs: 120,
onRemixing: () => {},
onSetProjectThumbnailer: () => {}
onSetProjectThumbnailer: () => {},
onSetProjectSaver: () => {},
onUpdateProjectData: saveProjectToServer
};
const mapStateToProps = (state, ownProps) => {
const loadingState = state.scratchGui.projectState.loadingState;
+62
View File
@@ -0,0 +1,62 @@
import queryString from 'query-string';
import xhr from 'xhr';
import storage from '../lib/storage';
/**
* Save a project JSON to the project server.
* This should eventually live in scratch-www.
* @param {number} projectId the ID of the project, null if a new project.
* @param {object} vmState the JSON project representation.
* @param {object} params the request params.
* @property {?number} params.originalId the original project ID if a copy/remix.
* @property {?boolean} params.isCopy a flag indicating if this save is creating a copy.
* @property {?boolean} params.isRemix a flag indicating if this save is creating a remix.
* @property {?string} params.title the title of the project.
* @return {Promise} A promise that resolves when the network request resolves.
*/
export default function (projectId, vmState, params) {
const opts = {
body: vmState,
// If we set json:true then the body is double-stringified, so don't
headers: {
'Content-Type': 'application/json'
},
withCredentials: true
};
const creatingProject = projectId === null || typeof projectId === 'undefined';
const queryParams = {};
if (params.hasOwnProperty('originalId')) queryParams.original_id = params.originalId;
if (params.hasOwnProperty('isCopy')) queryParams.is_copy = params.isCopy;
if (params.hasOwnProperty('isRemix')) queryParams.is_remix = params.isRemix;
if (params.hasOwnProperty('title')) queryParams.title = params.title;
let qs = queryString.stringify(queryParams);
if (qs) qs = `?${qs}`;
if (creatingProject) {
Object.assign(opts, {
method: 'post',
url: `${storage.projectHost}/${qs}`
});
} else {
Object.assign(opts, {
method: 'put',
url: `${storage.projectHost}/${projectId}${qs}`
});
}
return new Promise((resolve, reject) => {
xhr(opts, (err, response) => {
if (err) return reject(err);
let body;
try {
// Since we didn't set json: true, we have to parse manually
body = JSON.parse(response.body);
} catch (e) {
return reject(e);
}
body.id = projectId;
if (creatingProject) {
body.id = body['content-name'];
}
resolve(body);
});
});
}
+5
View File
@@ -25,5 +25,10 @@ export default defineMessages({
id: 'gui.sharedMessages.replaceProjectWarning',
defaultMessage: 'Replace contents of the current project?',
description: 'Confirmation that user wants to overwrite the current project contents'
},
loadFromComputerTitle: {
id: 'gui.sharedMessages.loadFromComputerTitle',
defaultMessage: 'Load from your computer',
description: 'Title for uploading a project from your computer'
}
});
+8 -3
View File
@@ -60,9 +60,9 @@ const vmListenerHOC = function (WrappedComponent) {
this.props.vm.postIOData('userData', {username: this.props.username});
}
// Re-request a targets update when the shouldEmitTargetsUpdate state changes to true
// Re-request a targets update when the shouldEmitUpdate state changes to true
// i.e. when the editor transitions out of fullscreen/player only modes
if (this.props.shouldEmitTargetsUpdate && !prevProps.shouldEmitTargetsUpdate) {
if (this.props.shouldEmitUpdates && !prevProps.shouldEmitUpdates) {
this.props.vm.emitTargetsUpdate(false /* Emit the event, but do not trigger project change */);
}
}
@@ -74,7 +74,7 @@ const vmListenerHOC = function (WrappedComponent) {
}
}
handleProjectChanged () {
if (this.props.shouldEmitUpdates) {
if (this.props.shouldEmitUpdates && !this.props.projectChanged) {
this.props.onProjectChanged();
}
}
@@ -117,6 +117,7 @@ const vmListenerHOC = function (WrappedComponent) {
const {
/* eslint-disable no-unused-vars */
attachKeyboardEvents,
projectChanged,
shouldEmitUpdates,
onBlockDragUpdate,
onGreenFlag,
@@ -147,13 +148,16 @@ const vmListenerHOC = function (WrappedComponent) {
onKeyUp: PropTypes.func,
onMicListeningUpdate: PropTypes.func.isRequired,
onMonitorsUpdate: PropTypes.func.isRequired,
onProjectChanged: PropTypes.func.isRequired,
onProjectRunStart: PropTypes.func.isRequired,
onProjectRunStop: PropTypes.func.isRequired,
onProjectSaved: PropTypes.func.isRequired,
onRuntimeStarted: PropTypes.func.isRequired,
onShowExtensionAlert: PropTypes.func.isRequired,
onTargetsUpdate: PropTypes.func.isRequired,
onTurboModeOff: PropTypes.func.isRequired,
onTurboModeOn: PropTypes.func.isRequired,
projectChanged: PropTypes.bool,
shouldEmitUpdates: PropTypes.bool,
username: PropTypes.string,
vm: PropTypes.instanceOf(VM).isRequired
@@ -163,6 +167,7 @@ const vmListenerHOC = function (WrappedComponent) {
onGreenFlag: () => ({})
};
const mapStateToProps = state => ({
projectChanged: state.scratchGui.projectChanged,
// Do not emit target or project updates in fullscreen or player only mode
// or when recording sounds (it leads to garbled recordings on low-power machines)
shouldEmitUpdates: !state.scratchGui.mode.isFullScreen && !state.scratchGui.mode.isPlayerOnly &&
+1
View File
@@ -107,6 +107,7 @@ const vmManagerHOC = function (WrappedComponent) {
fontsLoaded: PropTypes.bool,
isLoadingWithId: PropTypes.bool,
isPlayerOnly: PropTypes.bool,
isStarted: PropTypes.bool,
loadingState: PropTypes.oneOf(LoadingStates),
locale: PropTypes.string,
messages: PropTypes.objectOf(PropTypes.string),
+1 -1
View File
@@ -87,7 +87,7 @@ const reducer = function (state, action) {
closeButton: true,
extensionId: extensionId,
extensionName: extension.name,
iconURL: extension.smallPeripheralImage,
iconURL: extension.connectionSmallIconURL,
level: AlertLevels.WARN,
showReconnect: true
};
+14
View File
@@ -3,6 +3,7 @@ import analytics from '../lib/analytics';
import decks from '../lib/libraries/decks/index.jsx';
const CLOSE_CARDS = 'scratch-gui/cards/CLOSE_CARDS';
const SHRINK_EXPAND_CARDS = 'scratch-gui/cards/SHRINK_EXPAND_CARDS';
const VIEW_CARDS = 'scratch-gui/cards/VIEW_CARDS';
const ACTIVATE_DECK = 'scratch-gui/cards/ACTIVATE_DECK';
const NEXT_STEP = 'scratch-gui/cards/NEXT_STEP';
@@ -18,6 +19,7 @@ const initialState = {
step: 0,
x: 0,
y: 0,
expanded: true,
dragging: false
};
@@ -28,6 +30,10 @@ const reducer = function (state, action) {
return Object.assign({}, state, {
visible: false
});
case SHRINK_EXPAND_CARDS:
return Object.assign({}, state, {
expanded: !state.expanded
});
case VIEW_CARDS:
return Object.assign({}, state, {
visible: true
@@ -36,6 +42,9 @@ const reducer = function (state, action) {
return Object.assign({}, state, {
activeDeckId: action.activeDeckId,
step: 0,
x: 0,
y: 0,
expanded: true,
visible: true
});
case NEXT_STEP:
@@ -92,6 +101,10 @@ const closeCards = function () {
return {type: CLOSE_CARDS};
};
const shrinkExpandCards = function () {
return {type: SHRINK_EXPAND_CARDS};
};
const nextStep = function () {
return {type: NEXT_STEP};
};
@@ -118,6 +131,7 @@ export {
activateDeck,
viewCards,
closeCards,
shrinkExpandCards,
nextStep,
prevStep,
dragCard,
+1
View File
@@ -107,6 +107,7 @@ const initTutorialCard = function (currentState, deckId) {
visible: true,
content: decks,
activeDeckId: deckId,
expanded: true,
step: 0,
x: 0,
y: 0,
+1 -27
View File
@@ -10,7 +10,6 @@ const DONE_LOADING_VM_WITHOUT_ID = 'scratch-gui/project-state/DONE_LOADING_VM_WI
const DONE_REMIXING = 'scratch-gui/project-state/DONE_REMIXING';
const DONE_UPDATING = 'scratch-gui/project-state/DONE_UPDATING';
const DONE_UPDATING_BEFORE_COPY = 'scratch-gui/project-state/DONE_UPDATING_BEFORE_COPY';
const DONE_UPDATING_BEFORE_FILE_UPLOAD = 'scratch-gui/project-state/DONE_UPDATING_BEFORE_FILE_UPLOAD';
const DONE_UPDATING_BEFORE_NEW = 'scratch-gui/project-state/DONE_UPDATING_BEFORE_NEW';
const RETURN_TO_SHOWING = 'scratch-gui/project-state/RETURN_TO_SHOWING';
const SET_PROJECT_ID = 'scratch-gui/project-state/SET_PROJECT_ID';
@@ -23,7 +22,6 @@ const START_MANUAL_UPDATING = 'scratch-gui/project-state/START_MANUAL_UPDATING';
const START_REMIXING = 'scratch-gui/project-state/START_REMIXING';
const START_UPDATING_BEFORE_CREATING_COPY = 'scratch-gui/project-state/START_UPDATING_BEFORE_CREATING_COPY';
const START_UPDATING_BEFORE_CREATING_NEW = 'scratch-gui/project-state/START_UPDATING_BEFORE_CREATING_NEW';
const START_UPDATING_BEFORE_FILE_UPLOAD = 'scratch-gui/project-state/START_UPDATING_BEFORE_FILE_UPLOAD';
const defaultProjectId = '0'; // hardcoded id of default project
@@ -43,7 +41,6 @@ const LoadingState = keyMirror({
SHOWING_WITH_ID: null,
SHOWING_WITHOUT_ID: null,
UPDATING_BEFORE_COPY: null,
UPDATING_BEFORE_FILE_UPLOAD: null,
UPDATING_BEFORE_NEW: null
});
@@ -91,7 +88,6 @@ const getIsUpdating = loadingState => (
loadingState === LoadingState.AUTO_UPDATING ||
loadingState === LoadingState.MANUAL_UPDATING ||
loadingState === LoadingState.UPDATING_BEFORE_COPY ||
loadingState === LoadingState.UPDATING_BEFORE_FILE_UPLOAD ||
loadingState === LoadingState.UPDATING_BEFORE_NEW
);
const getIsShowingProject = loadingState => (
@@ -203,13 +199,6 @@ const reducer = function (state, action) {
});
}
return state;
case DONE_UPDATING_BEFORE_FILE_UPLOAD:
if (state.loadingState === LoadingState.UPDATING_BEFORE_FILE_UPLOAD) {
return Object.assign({}, state, {
loadingState: LoadingState.LOADING_VM_FILE_UPLOAD
});
}
return state;
case DONE_UPDATING_BEFORE_NEW:
if (state.loadingState === LoadingState.UPDATING_BEFORE_NEW) {
return Object.assign({}, state, {
@@ -333,13 +322,6 @@ const reducer = function (state, action) {
});
}
return state;
case START_UPDATING_BEFORE_FILE_UPLOAD:
if (state.loadingState === LoadingState.SHOWING_WITH_ID) {
return Object.assign({}, state, {
loadingState: LoadingState.UPDATING_BEFORE_FILE_UPLOAD
});
}
return state;
case START_ERROR:
// fatal errors: there's no correct editor state for us to show
if ([
@@ -360,7 +342,6 @@ const reducer = function (state, action) {
LoadingState.MANUAL_UPDATING,
LoadingState.REMIXING,
LoadingState.UPDATING_BEFORE_COPY,
LoadingState.UPDATING_BEFORE_FILE_UPLOAD,
LoadingState.UPDATING_BEFORE_NEW
].includes(state.loadingState)) {
return Object.assign({}, state, {
@@ -471,10 +452,6 @@ const doneUpdatingProject = loadingState => {
return {
type: DONE_UPDATING_BEFORE_COPY
};
case LoadingState.UPDATING_BEFORE_FILE_UPLOAD:
return {
type: DONE_UPDATING_BEFORE_FILE_UPLOAD
};
case LoadingState.UPDATING_BEFORE_NEW:
return {
type: DONE_UPDATING_BEFORE_NEW
@@ -501,11 +478,8 @@ const requestNewProject = needSave => {
const requestProjectUpload = loadingState => {
switch (loadingState) {
case LoadingState.SHOWING_WITH_ID:
return {
type: START_UPDATING_BEFORE_FILE_UPLOAD
};
case LoadingState.NOT_LOADED:
case LoadingState.SHOWING_WITH_ID:
case LoadingState.SHOWING_WITHOUT_ID:
return {
type: START_LOADING_VM_FILE_UPLOAD
+1 -1
View File
@@ -30,7 +30,7 @@ describe('Working with the how-to library', () => {
});
test('Backpack can be expanded with backpack host param', async () => {
await loadUri(`${uri}?backpack_host=some-value`);
await loadUri(`${uri}?backpack_host=https://backpack.scratch.mit.edu`);
// Try activating the backpack from the costumes tab to make sure it isn't pushed off
await clickText('Costumes');
+5 -1
View File
@@ -193,7 +193,11 @@ describe('Working with costumes', () => {
.mouseMove(abbyElement)
.perform();
// wait for one of Abby's alternate costumes to appear
await findByXpath('//img[@src="https://cdn.assets.scratch.mit.edu/internalapi/asset/b6e23922f23b49ddc6f62f675e77417c.svg/get/"]');
const src1 = await abbyElement.findElement({css: 'img'}).getAttribute('src');
await driver.sleep(300);
const src2 = await abbyElement.findElement({css: 'img'}).getAttribute('src');
const sourcesMatch = (src1 === src2);
await expect(sourcesMatch).toBeFalsy(); // 'src' attribute should have changed by now
const logs = await getLogs();
await expect(logs).toEqual([]);
});
+1 -1
View File
@@ -41,7 +41,7 @@ describe('Menu bar settings', () => {
'//div[contains(@class, "menu-bar_menu-bar-item") and ' +
'contains(@class, "menu-bar_hoverable")][span[text()="File"]]'
);
await findByXpath('//*[li[span[text()="Load from your computer"]] and not(@data-tip="tooltip")]');
await findByXpath('//*[li[text()="Load from your computer"] and not(@data-tip="tooltip")]');
});
test('File->Save should be enabled', async () => {
+164
View File
@@ -0,0 +1,164 @@
import React from 'react';
import {mount} from 'enzyme';
import configureStore from 'redux-mock-store';
import MenuBarHOC from '../../../src/containers/menu-bar-hoc.jsx';
describe('Menu Bar HOC', () => {
const mockStore = configureStore();
let store;
beforeEach(() => {
store = mockStore({
scratchGui: {
projectChanged: true
}
});
});
test('Logged in user who IS owner and HAS changed project will NOT be prompted to save', () => {
const Component = () => (<div />);
const WrappedComponent = MenuBarHOC(Component);
const wrapper = mount(
<WrappedComponent
canCreateNew
canSave
projectChanged
// assume the user will click "cancel" on the confirm dialog
confirmWithMessage={() => (false)} // eslint-disable-line react/jsx-no-bind
store={store}
/>
);
const child = wrapper.find(Component);
expect(child.props().projectChanged).toBeUndefined();
expect(child.props().confirmReadyToReplaceProject('message')).toBe(true);
});
test('Logged in user who IS owner and has NOT changed project will NOT be prompted to save', () => {
const Component = () => (<div />);
const WrappedComponent = MenuBarHOC(Component);
const wrapper = mount(
<WrappedComponent
canCreateNew
canSave
confirmWithMessage={() => (false)} // eslint-disable-line react/jsx-no-bind
projectChanged={false}
store={store}
/>
);
const child = wrapper.find(Component);
expect(child.props().projectChanged).toBeUndefined();
expect(child.props().confirmReadyToReplaceProject('message')).toBe(true);
});
test('Logged in user who is NOT owner and HAS changed project will NOT be prompted to save', () => {
const Component = () => (<div />);
const WrappedComponent = MenuBarHOC(Component);
const wrapper = mount(
<WrappedComponent
canCreateNew
projectChanged
canSave={false}
confirmWithMessage={() => (false)} // eslint-disable-line react/jsx-no-bind
store={store}
/>
);
const child = wrapper.find(Component);
expect(child.props().projectChanged).toBeUndefined();
expect(child.props().confirmReadyToReplaceProject('message')).toBe(true);
});
test('Logged OUT user who HAS changed project WILL be prompted to save', () => {
const Component = () => (<div />);
const WrappedComponent = MenuBarHOC(Component);
const wrapper = mount(
<WrappedComponent
projectChanged
canCreateNew={false}
canSave={false}
confirmWithMessage={() => (false)} // eslint-disable-line react/jsx-no-bind
store={store}
/>
);
const child = wrapper.find(Component);
expect(child.props().projectChanged).toBeUndefined();
expect(child.props().confirmReadyToReplaceProject('message')).toBe(false);
});
test('Logged OUT user who has NOT changed project WILL NOT be prompted to save', () => {
const Component = () => (<div />);
const WrappedComponent = MenuBarHOC(Component);
const wrapper = mount(
<WrappedComponent
canCreateNew={false}
canSave={false}
confirmWithMessage={() => (false)} // eslint-disable-line react/jsx-no-bind
projectChanged={false}
store={store}
/>
);
const child = wrapper.find(Component);
expect(child.props().projectChanged).toBeUndefined();
expect(child.props().confirmReadyToReplaceProject('message')).toBe(true);
});
test('Logged in user who IS owner and HAS changed project SHOULD save before transition to project page', () => {
const Component = () => (<div />);
const WrappedComponent = MenuBarHOC(Component);
const wrapper = mount(
<WrappedComponent
canSave
projectChanged
store={store}
/>
);
const child = wrapper.find(Component);
expect(child.props().projectChanged).toBeUndefined();
expect(child.props().shouldSaveBeforeTransition()).toBe(true);
});
test('Logged in user who IS owner and has NOT changed project should NOT save before transition', () => {
const Component = () => (<div />);
const WrappedComponent = MenuBarHOC(Component);
const wrapper = mount(
<WrappedComponent
canSave
projectChanged={false}
store={store}
/>
);
const child = wrapper.find(Component);
expect(child.props().projectChanged).toBeUndefined();
expect(child.props().shouldSaveBeforeTransition()).toBe(false);
});
test('Logged in user who is NOT owner and HAS changed project should NOT save before transition', () => {
const Component = () => (<div />);
const WrappedComponent = MenuBarHOC(Component);
const wrapper = mount(
<WrappedComponent
projectChanged
canSave={false}
store={store}
/>
);
const child = wrapper.find(Component);
expect(child.props().projectChanged).toBeUndefined();
expect(child.props().shouldSaveBeforeTransition()).toBe(false);
});
test('Logged in user who is NOT owner and has NOT changed project should NOT save before transition', () => {
const Component = () => (<div />);
const WrappedComponent = MenuBarHOC(Component);
const wrapper = mount(
<WrappedComponent
canSave={false}
projectChanged={false}
store={store}
/>
);
const child = wrapper.find(Component);
expect(child.props().projectChanged).toBeUndefined();
expect(child.props().shouldSaveBeforeTransition()).toBe(false);
});
});
@@ -250,7 +250,7 @@ test('requestProjectUpload when showing project with id should load', () => {
};
const action = requestProjectUpload(initialState.loadingState);
const resultState = projectStateReducer(initialState, action);
expect(resultState.loadingState).toBe(LoadingState.UPDATING_BEFORE_FILE_UPLOAD);
expect(resultState.loadingState).toBe(LoadingState.LOADING_VM_FILE_UPLOAD);
});
test('requestProjectUpload when showing project without id should load', () => {
+23
View File
@@ -465,4 +465,27 @@ describe('projectSaverHOC', () => {
expect(setThumb).toHaveBeenCalledTimes(2);
expect(setThumb.mock.calls[1][0]).toBe(null);
});
test('uses onSetProjectSaver on mount/unmount', () => {
const Component = () => <div />;
const WrappedComponent = projectSaverHOC(Component);
const setSaver = jest.fn();
const mounted = mount(
<WrappedComponent
store={store}
vm={vm}
onSetProjectSaver={setSaver}
/>
);
// Set project saver should be called on mount
expect(setSaver).toHaveBeenCalledTimes(1);
// And it should not pass that function on to wrapped element
expect(mounted.find(Component).props().onSetProjectSaver).toBeUndefined();
// Unmounting should call it again with null
mounted.unmount();
expect(setSaver).toHaveBeenCalledTimes(2);
expect(setSaver.mock.calls[1][0]).toBe(null);
});
});
+2 -1
View File
@@ -41,7 +41,8 @@ const base = {
include: [
path.resolve(__dirname, 'src'),
/node_modules[\\/]scratch-[^\\/]+[\\/]src/,
/node_modules[\\/]pify/
/node_modules[\\/]pify/,
/node_modules[\\/]@vernier[\\/]godirect/
],
options: {
// Explicitly disable babelrc so we don't catch various config