diff --git a/src/components/extension-landing/extension-landing.jsx b/src/components/extension-landing/extension-landing.jsx index 44cb3fb01..c883d294f 100644 --- a/src/components/extension-landing/extension-landing.jsx +++ b/src/components/extension-landing/extension-landing.jsx @@ -1,7 +1,7 @@ const bindAll = require('lodash.bindall'); const React = require('react'); -const OS_ENUM = require('./os-enum.js'); +const detectOS = require('../../lib/detect-os.js').default; class ExtensionLanding extends React.Component { constructor (props) { @@ -10,16 +10,8 @@ class ExtensionLanding extends React.Component { 'onSetOS' ]); - // @todo use bowser for browser detection - let detectedOS = OS_ENUM.WINDOWS; - if (window.navigator && window.navigator.platform) { - if (window.navigator.platform === 'MacIntel') { - detectedOS = OS_ENUM.MACOS; - } - } - this.state = { - OS: detectedOS + OS: detectOS() }; } diff --git a/src/components/extension-landing/install-scratch-link.jsx b/src/components/extension-landing/install-scratch-link.jsx index b8186fbcf..507cdbeb7 100644 --- a/src/components/extension-landing/install-scratch-link.jsx +++ b/src/components/extension-landing/install-scratch-link.jsx @@ -2,13 +2,14 @@ const PropTypes = require('prop-types'); const FormattedMessage = require('react-intl').FormattedMessage; const React = require('react'); -const OS_ENUM = require('./os-enum.js'); +const OS_ENUM = require('../../lib/os-enum.js'); const FlexRow = require('../../components/flex-row/flex-row.jsx'); const Steps = require('../../components/steps/steps.jsx'); const Step = require('../../components/steps/step.jsx'); require('./extension-landing.scss'); +// Assumes this will only be called with an OS that needs Scratch Link const InstallScratchLink = ({ currentOS }) => ( @@ -37,20 +38,20 @@ const InstallScratchLink = ({ - + - + diff --git a/src/components/install-scratch/install-scratch.jsx b/src/components/install-scratch/install-scratch.jsx new file mode 100644 index 000000000..eff760cff --- /dev/null +++ b/src/components/install-scratch/install-scratch.jsx @@ -0,0 +1,146 @@ +const PropTypes = require('prop-types'); +const FormattedMessage = require('react-intl').FormattedMessage; +const React = require('react'); + +const OS_ENUM = require('../../lib/os-enum.js'); +const {CHROME_APP_RELEASED} = require('../../lib/feature-flags.js'); + +const {isDownloaded, isFromGooglePlay} = require('./install-util.js'); + +const FlexRow = require('../../components/flex-row/flex-row.jsx'); +const Steps = require('../../components/steps/steps.jsx'); +const Step = require('../../components/steps/step.jsx'); + +require('./install-scratch.scss'); + +const InstallScratch = ({ + currentOS +}) => ( +
+ +

+ {CHROME_APP_RELEASED ? ( + + ) : ( + + {isDownloaded(currentOS) && ( + + )} + {isFromGooglePlay(currentOS) && ( + + )} + + )} +

+ +
+ + + + {isDownloaded(currentOS) && ( + + )} + {isFromGooglePlay(currentOS) && ( + + )} + + +
+ {currentOS === OS_ENUM.WINDOWS && ( + + + + )} + {currentOS === OS_ENUM.MACOS && ( + + + + )} + {isFromGooglePlay(currentOS) && ( + + + + + )} + {isDownloaded(currentOS) && ( + + + + + + + + + )} +
+
+ +
+ {isDownloaded(currentOS) && ( + + + {currentOS === OS_ENUM.WINDOWS ? + : + + } + + +
+ +
+
+ )} +
+
+
+); + +InstallScratch.propTypes = { + currentOS: PropTypes.string +}; + +module.exports = InstallScratch; diff --git a/src/components/install-scratch/install-scratch.scss b/src/components/install-scratch/install-scratch.scss new file mode 100644 index 000000000..953b93d5a --- /dev/null +++ b/src/components/install-scratch/install-scratch.scss @@ -0,0 +1,95 @@ +@import "../../colors"; +@import "../../frameless"; + +#view { + padding: 0; +} + +.install-scratch { + padding: 2rem 0; + + .inner { + align-items: flex-start; + } + + .downloads-container { + text-align: center; + + .horizontal-divider { + display: block; + margin: 20px; + } + + .horizontal-divider:before, + .horizontal-divider:after { + display: inline-block; + position: relative; + background-color: $ui-dark-gray; + width: 50%; + height: 1px; + vertical-align: middle; + content: ""; + } + + .horizontal-divider:before { + right: .5em; + margin-left: -50%; + } + + .horizontal-divider:after { + left: .5em; + margin-right: -50%; + } + } + + .step-image { + height: 14rem; + } + + .title { + margin-bottom: 2rem; + font-size: 2rem; + } + + .legacy-link { + display: flex; + } + + .download-button { + display: inline-block; + margin: .5em 0; + border: 0; + border-radius: 8px; + background-color: $ui-blue; + cursor: pointer; + padding: 1rem 2rem; + color: $ui-white; + font-size: 1rem; + } + + .macos-badge img { + height: 50px; + } + + .ms-badge img { + height: 50px; + } + + .play-badge img { + height: 50px; + } + + .download-image { + width: 100%; + max-width: $cols6; + + img { + max-width: 100%; + max-height: 100%; + } + } + + .blue { + background-color: $ui-blue-10percent; + } +} diff --git a/src/components/install-scratch/install-util.js b/src/components/install-scratch/install-util.js new file mode 100644 index 000000000..20f667c21 --- /dev/null +++ b/src/components/install-scratch/install-util.js @@ -0,0 +1,13 @@ +const OS_ENUM = require('../../lib/os-enum.js'); + +module.exports = {}; + +module.exports.isDownloaded = os => { + if (os === OS_ENUM.WINDOWS || os === OS_ENUM.MACOS) return true; + return false; +}; + +module.exports.isFromGooglePlay = os => { + if (os === OS_ENUM.ANDROID || os === OS_ENUM.CHROMEOS) return true; + return false; +}; diff --git a/src/components/os-chooser/os-chooser.jsx b/src/components/os-chooser/os-chooser.jsx index 977c207ea..5d77d19e2 100644 --- a/src/components/os-chooser/os-chooser.jsx +++ b/src/components/os-chooser/os-chooser.jsx @@ -4,11 +4,12 @@ const FormattedMessage = require('react-intl').FormattedMessage; const PropTypes = require('prop-types'); const React = require('react'); +const {CHROME_APP_RELEASED} = require('../../lib/feature-flags.js'); const FlexRow = require('../../components/flex-row/flex-row.jsx'); const Button = require('../../components/forms/button.jsx'); -const OS_ENUM = require('../../components/extension-landing/os-enum.js'); +const OS_ENUM = require('../../lib/os-enum.js'); require('./os-chooser.scss'); @@ -34,6 +35,28 @@ const OSChooser = props => ( macOS + {CHROME_APP_RELEASED && ( + + + + + )} ); diff --git a/src/l10n.json b/src/l10n.json index 9b9032b7a..a72a5de67 100644 --- a/src/l10n.json +++ b/src/l10n.json @@ -133,10 +133,16 @@ "oschooser.choose": "Choose your OS:", + "installScratch.or": "or", + "installScratch.directDownload": "Direct download", + "installScratch.desktopHeaderTitle": "Install Scratch Desktop", + "installScratch.appHeaderTitle": "Install Scratch for {operatingsystem}", + "installScratch.downloadScratchDesktop": "Download Scratch Desktop", + "installScratch.downloadScratchAppGeneric": "Download Scratch for {operatingsystem}", + "installScratch.getScratchAppPlay": "Get Scratch on the Google Play Store", + "installScratchLink.installHeaderTitle": "Install Scratch Link", "installScratchLink.downloadAndInstall": "Download and install Scratch Link.", - "installScratchLink.or": "or", - "installScratchLink.directDownload": "Direct download", "installScratchLink.startScratchLink": "Start Scratch Link and make sure it is running. It should appear in your toolbar.", "parents.FaqAgeRangeA": "While Scratch is primarily designed for 8 to 16 year olds, it is also used by people of all ages, including younger children with their parents.", diff --git a/src/lib/detect-os.js b/src/lib/detect-os.js new file mode 100644 index 000000000..fa94c3229 --- /dev/null +++ b/src/lib/detect-os.js @@ -0,0 +1,18 @@ +import bowser from 'bowser'; +import OS_ENUM from './os-enum.js'; +import {CHROME_APP_RELEASED} from './feature-flags.js'; + +/** + * Helper function to the current Operating System. + * @returns {OS_ENUM} Returns the OS value, defaults to WINDOWS + */ +export default function () { + // matching OS strings from https://github.com/lancedikson/bowser/blob/master/src/constants.js + if (bowser.osname === 'macOS') return OS_ENUM.MACOS; + if (CHROME_APP_RELEASED) { + if (bowser.osname === 'Chrome OS') return OS_ENUM.CHROMEOS; + if (bowser.osname === 'Android') return OS_ENUM.ANDROID; + } + // if (bowser.osname === 'iOS') return OS_ENUM.IOS; // @todo + return OS_ENUM.WINDOWS; +} diff --git a/src/lib/feature-flags.js b/src/lib/feature-flags.js new file mode 100644 index 000000000..2965c1e9c --- /dev/null +++ b/src/lib/feature-flags.js @@ -0,0 +1,10 @@ +const isStaging = () => process.env.SCRATCH_ENV === 'staging'; + +const flagInUrl = flag => { + const url = (window.location && window.location.search) || ''; + return url.indexOf(`${flag}=true`) !== -1; +}; + +module.exports = { + CHROME_APP_RELEASED: isStaging() && flagInUrl('CHROME_APP_RELEASED') +}; diff --git a/src/components/extension-landing/os-enum.js b/src/lib/os-enum.js similarity index 51% rename from src/components/extension-landing/os-enum.js rename to src/lib/os-enum.js index b8cde494e..24d41c018 100644 --- a/src/components/extension-landing/os-enum.js +++ b/src/lib/os-enum.js @@ -1,6 +1,8 @@ const OS_ENUM = { WINDOWS: 'Windows', - MACOS: 'macOS' + MACOS: 'macOS', + CHROMEOS: 'ChromeOS', + ANDROID: 'Android' }; module.exports = OS_ENUM; diff --git a/src/views/download/download.jsx b/src/views/download/download.jsx index 2c935f04b..b0f77a4a8 100644 --- a/src/views/download/download.jsx +++ b/src/views/download/download.jsx @@ -5,13 +5,14 @@ const React = require('react'); const FlexRow = require('../../components/flex-row/flex-row.jsx'); const bindAll = require('lodash.bindall'); -const Steps = require('../../components/steps/steps.jsx'); -const Step = require('../../components/steps/step.jsx'); const Page = require('../../components/page/www/page.jsx'); const render = require('../../lib/render.jsx'); -const OS_ENUM = require('../../components/extension-landing/os-enum.js'); +const detectOS = require('../../lib/detect-os.js').default; +const {CHROME_APP_RELEASED} = require('../../lib/feature-flags.js'); const OSChooser = require('../../components/os-chooser/os-chooser.jsx'); +const InstallScratch = require('../../components/install-scratch/install-scratch.jsx'); +const {isDownloaded, isFromGooglePlay} = require('../../components/install-scratch/install-util.js'); require('./download.scss'); require('../../components/forms/button.scss'); @@ -22,15 +23,9 @@ class Download extends React.Component { bindAll(this, [ 'onSetOS' ]); - let detectedOS = OS_ENUM.WINDOWS; - if (window.navigator && window.navigator.platform) { - if (window.navigator.platform === 'MacIntel') { - detectedOS = OS_ENUM.MACOS; - } - } this.state = { - OS: detectedOS + OS: detectOS() }; } @@ -55,10 +50,16 @@ class Download extends React.Component { src="/images/download/icon.png" width="40" /> - + - + @@ -80,6 +81,24 @@ class Download extends React.Component { /> macOS 10.13+ + {CHROME_APP_RELEASED && ( + + + + ChromeOS + + + + Android 5.0+ + + + )} @@ -95,89 +114,150 @@ class Download extends React.Component { currentOS={this.state.OS} handleSetOS={this.onSetOS} /> -
- -

- -

- -
- - - - -
+ + {isDownloaded(this.state.OS) && ( +
+ +

+ +

+

+ +

+ + - - -
- - - {this.state.OS === OS_ENUM.WINDOWS ? - : - - } - - -
- +

-
- - -
+
+ + + +

+ + + + +

+
+ + +
+ )}

+ {isDownloaded(this.state.OS) && ( + +

+ +

+

+ +

+
+ )} + {isFromGooglePlay(this.state.OS) && ( + +

+ +

+

+ +

+
+ )}

- -

-

- -

-

- + {isFromGooglePlay(this.state.OS) ? + : + + }

-

- -

-

- -

-

- -

-

- -

+ {isDownloaded(this.state.OS) && (CHROME_APP_RELEASED ? ( + +

+ +

+

+ +

+
+ ) : ( + +

+ +

+

+ +

+
+ ))} + {isFromGooglePlay(this.state.OS) && ( + +

+ +

+

+ +

+
+ )} + {!CHROME_APP_RELEASED && ( + +

+ +

+

+ +

+
+ )}

@@ -186,64 +266,7 @@ class Download extends React.Component {

-
- -

- -

-

- -

- -
- - - -

- - - - -

-
-
- - - -

- - - - -

-
-
-
-
+
); diff --git a/src/views/download/download.scss b/src/views/download/download.scss index e53275950..3e2b4064c 100644 --- a/src/views/download/download.scss +++ b/src/views/download/download.scss @@ -27,6 +27,18 @@ font-size: 1rem; } + .macos-badge img { + height: 50px; + } + + .ms-badge img { + height: 50px; + } + + .play-badge img { + height: 50px; + } + .download-header { background-color: $ui-blue; padding: 4rem 0; @@ -80,6 +92,7 @@ .download-requirements { justify-content: space-between; + line-height: 2rem; } .download-requirements span { diff --git a/src/views/download/l10n.json b/src/views/download/l10n.json index e475c8a75..c1559b070 100644 --- a/src/views/download/l10n.json +++ b/src/views/download/l10n.json @@ -1,11 +1,10 @@ { "download.title": "Scratch Desktop", "download.intro": "You can install the Scratch Desktop editor to work on projects without an internet connection. This version will work on Windows and MacOS.", + "download.appTitle": "Download Scratch", + "download.appIntro": "You can install Scratch for free to work on projects without an internet connection.", "download.requirements": "Requirements", "download.imgAltDownloadIllustration" : "Scratch 3.0 Desktop screenshot", - "download.installHeaderTitle": "Install Scratch Desktop", - "download.downloadScratchDesktop": "Download Scratch Desktop", - "download.downloadButton": "Download", "download.troubleshootingTitle": "FAQ", "download.startScratchDesktop": "Start Scratch Desktop", "download.howDoIInstall": "How do I install Scratch Desktop?", @@ -14,17 +13,23 @@ "download.supportChromeOS" : "When will you have Scratch Desktop for Chromebooks?", "download.supportChromeOSAnswer": "Scratch Desktop for Chromebooks is not yet available. We are working on it and expect to release later in 2019.", "download.olderVersionsTitle" : "Older Versions", - "download.olderVersions": "Looking for the Scratch 2.0 Offline Editor or Scratch 1.4?", - "download.scratch1-4Desktop" : "Scratch 1.4 Desktop", - "download.scratch2Desktop" : "Scratch 2.0 Desktop", + "download.olderVersions": "Looking for earlier Scratch Offline Editors?", + "download.scratch1-4Desktop" : "Scratch 1.4", + "download.scratch2Desktop" : "Scratch 2.0 Offline Editor", "download.cannotAccessMacStore" : "What if I can't access the Mac App Store?", "download.cannotAccessWindowsStore" : "What if I can't access the Microsoft Store?", "download.macMoveToApplications" : "Open the .dmg file. Move Scratch Desktop into Applications.", "download.winMoveToApplications" : "Run the .exe file.", "download.canIUseScratchLink" : "Can I use Scratch Link to connect to extensions?", "download.canIUseScratchLinkAnswer" : "Yes. However, you will need an Internet connection to use Scratch Link.", + "download.canIUseExtensions" : "Can I connect to hardware extensions?", + "download.canIUseExtensionsAnswer" : "Yes. With the Scratch app you can connect to extensions, and you do not need Scratch Link.", "download.desktopAndBrowser": "Can I use Scratch Desktop and also have Scratch open in the browser?", + "download.appAndBrowser": "Can I use the Scratch app and also have Scratch open in the browser?", "download.yesAnswer" : "Yes.", "download.canIShare": "Can I share from Scratch Desktop?", - "download.canIShareAnswer": "This isn’t supported currently. For now, you can save a project from Scratch Desktop, upload it to your Scratch account, and share it there. In a later version we will add the ability to upload to your Scratch account directly in Scratch Desktop." + "download.canIShareAnswer": "This isn’t supported currently. For now, you can save a project from Scratch Desktop, upload it to your Scratch account, and share it there. In a later version we will add the ability to upload to your Scratch account directly in Scratch Desktop.", + "download.canIShareApp": "Can I share from Scratch for {operatingsystem}?", + "download.canIShareAnswerPlayStore": "Yes. Click the 3-dots menu on a project in the lobby and select Share from the options. In addition to sharing by email you can sign in to your Scratch account and share the project on the Scratch Community.", + "download.canIShareAnswerDownloaded": "This isn’t supported currently. For now, you can save a project from Scratch for {operatingsystem}, upload it to your Scratch account, and share it there. In a later version we will add the ability to upload to your Scratch account directly in Scratch for {operatingsystem}." } diff --git a/src/views/ev3/ev3.jsx b/src/views/ev3/ev3.jsx index a03b55a14..6666490e9 100644 --- a/src/views/ev3/ev3.jsx +++ b/src/views/ev3/ev3.jsx @@ -23,7 +23,7 @@ const ProjectCard = require('../../components/extension-landing/project-card.jsx const Steps = require('../../components/steps/steps.jsx'); const Step = require('../../components/steps/step.jsx'); -const OS_ENUM = require('../../components/extension-landing/os-enum.js'); +const OS_ENUM = require('../../lib/os-enum.js'); require('../../components/extension-landing/extension-landing.scss'); require('./ev3.scss'); diff --git a/src/views/microbit/microbit.jsx b/src/views/microbit/microbit.jsx index 1b36c4ba3..ac274423b 100644 --- a/src/views/microbit/microbit.jsx +++ b/src/views/microbit/microbit.jsx @@ -22,7 +22,7 @@ const Button = require('../../components/forms/button.jsx'); const Steps = require('../../components/steps/steps.jsx'); const Step = require('../../components/steps/step.jsx'); -const OS_ENUM = require('../../components/extension-landing/os-enum.js'); +const OS_ENUM = require('../../lib/os-enum.js'); require('../../components/extension-landing/extension-landing.scss'); require('./microbit.scss'); diff --git a/static/images/badges/google-play-badge.png b/static/images/badges/google-play-badge.png new file mode 100644 index 000000000..27392d217 Binary files /dev/null and b/static/images/badges/google-play-badge.png differ diff --git a/static/images/badges/mac-store-badge.svg b/static/images/badges/mac-store-badge.svg new file mode 100755 index 000000000..072b425a1 --- /dev/null +++ b/static/images/badges/mac-store-badge.svg @@ -0,0 +1,46 @@ + + Download_on_the_App_Store_Badge_US-UK_RGB_blk_4SVG_092917 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/badges/windows-store-badge.svg b/static/images/badges/windows-store-badge.svg new file mode 100755 index 000000000..21c139edd --- /dev/null +++ b/static/images/badges/windows-store-badge.svg @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/svgs/download/mac-badge.svg b/static/svgs/download/mac-badge.svg new file mode 100755 index 000000000..072b425a1 --- /dev/null +++ b/static/svgs/download/mac-badge.svg @@ -0,0 +1,46 @@ + + Download_on_the_App_Store_Badge_US-UK_RGB_blk_4SVG_092917 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/svgs/download/ms-badge.svg b/static/svgs/download/ms-badge.svg new file mode 100755 index 000000000..21c139edd --- /dev/null +++ b/static/svgs/download/ms-badge.svg @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +