mirror of
https://github.com/scratchfoundation/scratch-www.git
synced 2025-03-25 20:29:45 -04:00
Merge branch 'develop' into force-account-rename
This commit is contained in:
commit
132280477b
208 changed files with 3520 additions and 27889 deletions
.circleci
README.mdpackage-lock.jsonpackage.jsonsrc
_colors.scssinit.jsl10n.json
components
card
comment
dropdown-banner
dropdown
errorboundary
extension-landing
footer
formik-forms
forms
button.scssinplace-input.scssinput.scssphone-input.jsxphone-input.scssselect.scsstextarea.scssvalidation-message.scssvalidations.jsx
helpwidget
info-button
informationpage
intro
join-flow
birthdate-step.jsxcountry-step.jsxemail-step.jsxgender-step.jsxjoin-flow-steps.scssjoin-flow.jsxnext-step-button.jsxnext-step-button.scssregistration-error-step.jsxusername-step.jsxwelcome-step.jsx
languagechooser
login
modal
addtostudio
comments
mute
report
social
ttt
navigation
news
os-chooser
page/www
privacy-banner
registration
relative-time
social-message
steps
subnavigation
tabs
teacher-banner
tooltip
ttt-tile
lib
main.scssredux
routes.jsontemplate-config.jstemplate.ejsviews
about
annual-report
boost
camp
|
@ -128,6 +128,7 @@ workflows:
|
|||
branches:
|
||||
only:
|
||||
- develop
|
||||
- beta
|
||||
- /^hotfix\/.*/
|
||||
- /^release\/.*/
|
||||
- integration-tests:
|
||||
|
@ -141,6 +142,7 @@ workflows:
|
|||
branches:
|
||||
only:
|
||||
- develop
|
||||
- beta
|
||||
- /^hotfix\/.*/
|
||||
- /^release\/.*/
|
||||
- build-and-deploy-production:
|
||||
|
@ -190,5 +192,6 @@ workflows:
|
|||
ignore:
|
||||
- develop
|
||||
- master
|
||||
- beta
|
||||
- /^hotfix\/.*/
|
||||
- /^release\/.*/
|
||||
|
|
|
@ -165,9 +165,9 @@ the beginning of the command, before `npm start`:
|
|||
| `ASSET_HOST` | `https://assets.scratch.mit.edu` | Hostname for asset requests |
|
||||
| `BACKPACK_HOST` | `https://backpack.scratch.mit.edu` | Hostname for backpack requests |
|
||||
| `PROJECT_HOST` | `https://projects.scratch.mit.edu` | Hostname for project requests |
|
||||
| `SENTRY_DSN` | `''` | DSN for Sentry |
|
||||
| `FALLBACK` | `''` | Pass-through location for old site |
|
||||
| `GA_TRACKER` | `''` | Where to log Google Analytics data |
|
||||
| `GTM_ID` | `''` | Google Tag Manager ID |
|
||||
| `GTM_ENV_AUTH` | `''` | Google Tag Manager env and auth info |
|
||||
| `NODE_ENV` | `null` | If not `production`, app acts like development |
|
||||
| `PORT` | `8333` | Port for devserver (http://localhost:XXXX) |
|
||||
|
||||
|
|
27244
package-lock.json
generated
27244
package-lock.json
generated
File diff suppressed because it is too large
Load diff
24
package.json
24
package.json
|
@ -35,16 +35,15 @@
|
|||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+ssh://git@github.com/llk/scratch-www.git"
|
||||
"url": "git+ssh://git@github.com/scratchfoundation/scratch-www.git"
|
||||
},
|
||||
"author": "Massachusetts Institute of Technology",
|
||||
"license": "BSD-3-Clause",
|
||||
"bugs": {
|
||||
"url": "https://github.com/llk/scratch-www/issues"
|
||||
"url": "https://github.com/scratchfoundation/scratch-www/issues"
|
||||
},
|
||||
"homepage": "https://github.com/llk/scratch-www#readme",
|
||||
"homepage": "https://github.com/scratchfoundation/scratch-www#readme",
|
||||
"dependencies": {
|
||||
"@sentry/browser": "4.4.2",
|
||||
"bunyan": "1.8.15",
|
||||
"clipboard-copy": "2.0.1",
|
||||
"express": "4.16.1",
|
||||
|
@ -57,12 +56,14 @@
|
|||
"react-twitter-embed": "^3.0.3",
|
||||
"react-use": "^17.3.1",
|
||||
"scratch-parser": "5.1.1",
|
||||
"scratch-storage": "2.0.2"
|
||||
"scratch-storage": "2.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@formatjs/intl-locale": "2.4.34",
|
||||
"@formatjs/intl-pluralrules": "4.1.0",
|
||||
"@formatjs/intl-relativetimeformat": "8.1.8",
|
||||
"@formatjs/intl-datetimeformat": "6.4.3",
|
||||
"@formatjs/intl-locale": "3.0.11",
|
||||
"@formatjs/intl-numberformat": "8.3.3",
|
||||
"@formatjs/intl-pluralrules": "5.1.8",
|
||||
"@formatjs/intl-relativetimeformat": "11.1.8",
|
||||
"async": "3.2.2",
|
||||
"autoprefixer": "10.4.2",
|
||||
"babel-cli": "6.26.0",
|
||||
|
@ -119,7 +120,7 @@
|
|||
"query-string": "5.1.1",
|
||||
"react": "16.14.0",
|
||||
"react-dom": "16.14.0",
|
||||
"react-intl": "2.9.0",
|
||||
"react-intl": "5.25.1",
|
||||
"react-modal": "3.11.1",
|
||||
"react-onclickoutside": "6.7.1",
|
||||
"react-plotly.js": "2.4.0",
|
||||
|
@ -128,14 +129,15 @@
|
|||
"react-slick": "0.16.0",
|
||||
"react-string-replace": "0.4.1",
|
||||
"react-telephone-input": "4.3.4",
|
||||
"react-test-renderer": "16.14.0",
|
||||
"redux": "3.5.2",
|
||||
"redux-mock-store": "1.5.4",
|
||||
"redux-thunk": "2.0.1",
|
||||
"regenerator-runtime": "0.13.9",
|
||||
"sass": "1.49.7",
|
||||
"sass-loader": "10.2.1",
|
||||
"scratch-gui": "1.3.9",
|
||||
"scratch-l10n": "3.15.20230125032128",
|
||||
"scratch-gui": "2.0.55",
|
||||
"scratch-l10n": "3.15.20230711032212",
|
||||
"selenium-webdriver": "4.1.0",
|
||||
"slick-carousel": "1.6.0",
|
||||
"style-loader": "0.12.3",
|
||||
|
|
|
@ -13,6 +13,7 @@ $ui-orange-90percent: hsla(38, 100%, 55%, .9);
|
|||
$ui-dark-orange: hsla(30, 100%, 55%, 1); // ##FF8C1A Variables Primary
|
||||
|
||||
$ui-red: hsla(20, 100%, 55%, 1); /* #FF661A */
|
||||
$ui-red-dark: hsla(20, 100%, 40%, 1); /* #CC4400 */
|
||||
$ui-red-25percent: hsla(20, 100%, 55%, .25);
|
||||
$ui-green-35percent: rgba(126, 225, 195, .35);
|
||||
|
||||
|
@ -27,11 +28,15 @@ $motion-blue-3: hsla(215, 60%, 50%, 1);//#3373CC
|
|||
/* UI Secondary Colors */
|
||||
/* 3.0 colors */
|
||||
/* Using www naming convention for now, should be consistent with gui */
|
||||
$ui-aqua: hsla(163, 85%, 40%, 1); // #0FBD8C Extension Primary
|
||||
$ui-aqua-dark: hsla(163, 85%, 30%, 1); // #0B8E69 Extension Aqua 3
|
||||
$ui-aqua: hsla(144, 45%, 36%, 1);
|
||||
$ui-aqua-dark: darken($ui-aqua, 10%);
|
||||
$ui-purple: hsla(260, 100%, 70%, 1); // #9966FF Looks Primary
|
||||
$ui-purple-dark: hsla(260, 60%, 60%, 1); // #774DCB Looks Secondary
|
||||
$ui-purple-dark: hsla(260, 60%, 60%, 1); // #855CD6 Looks Secondary
|
||||
$ui-purple-darker: hsla(260, 46%, 54%, 1);
|
||||
$ui-purple-10percent: hsla(260, 60%, 60%, .1);
|
||||
$ui-purple-25percent: hsla(260, 60%, 60%, .25);
|
||||
$ui-magenta: hsla(300, 53%, 60%, 1); /* #CF63CF Sounds Primary */
|
||||
$ui-magenta-dark: hsla(300, 48%, 50%, 1); /* #BD42BD Sounds Tertiary */
|
||||
|
||||
$ui-yellow: hsla(45, 100%, 50%, 1); // #FFBF00 Events Primary
|
||||
$ui-coral: hsla(350, 100%, 70%, 1); // #FF6680 More Blocks Primary
|
||||
|
@ -59,11 +64,12 @@ $transparent-light-blue: rgba(229, 240, 254, 0);
|
|||
/* Typography Colors */
|
||||
$header-gray: hsla(225, 15%, 40%, 1); //#575E75
|
||||
$type-gray: hsla(225, 15%, 40%, 1); //#575E75
|
||||
$type-dark-gray: hsla(0, 0%, 23%, 1);
|
||||
$type-gray-75percent: hsla(225, 15%, 40%, .75);
|
||||
$type-gray-60percent: hsla(225, 15%, 40%, .6);
|
||||
$type-white: hsla(0, 100%, 100%, 1); //#FFF
|
||||
|
||||
$link-blue: $ui-blue;
|
||||
$link-purple: $ui-purple-dark;
|
||||
|
||||
/* Down Deep */
|
||||
$dd-darkblue: hsla(195, 72.4%, 17.1%, 1);
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
|
||||
&.has-error {
|
||||
.input {
|
||||
border: 1px solid $ui-orange;
|
||||
border: 1px solid $ui-red-dark;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
const classNames = require('classnames');
|
||||
const FormattedRelative = require('react-intl').FormattedRelative;
|
||||
const PropTypes = require('prop-types');
|
||||
const React = require('react');
|
||||
|
||||
|
@ -13,21 +12,12 @@ const CommentText = props => (
|
|||
className="mod-comment"
|
||||
text={props.comment}
|
||||
/>
|
||||
{typeof props.datetimeCreated === 'undefined' ? [] : [
|
||||
<p
|
||||
className="comment-text-timestamp"
|
||||
key="comment-text-timestamp"
|
||||
>
|
||||
<FormattedRelative value={new Date(props.datetimeCreated)} />
|
||||
</p>
|
||||
]}
|
||||
</div>
|
||||
);
|
||||
|
||||
CommentText.propTypes = {
|
||||
className: PropTypes.string,
|
||||
comment: PropTypes.string.isRequired,
|
||||
datetimeCreated: PropTypes.string
|
||||
comment: PropTypes.string.isRequired
|
||||
};
|
||||
|
||||
module.exports = CommentText;
|
||||
|
|
|
@ -35,9 +35,3 @@
|
|||
margin: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.comment-text-timestamp {
|
||||
margin: 1rem 0 0;
|
||||
color: $ui-dark-gray;
|
||||
font-size: .8rem;
|
||||
}
|
||||
|
|
|
@ -37,6 +37,6 @@ $navigation-height: 50px;
|
|||
}
|
||||
|
||||
&.warning {
|
||||
background-color: $ui-orange;
|
||||
background-color: $ui-red-dark;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
right: 0;
|
||||
border: 1px solid $active-gray;
|
||||
border-radius: 0 0 5px 5px;
|
||||
background-color: $ui-blue;
|
||||
background-color: $ui-purple-dark;
|
||||
padding: 10px;
|
||||
min-width: 9rem;
|
||||
max-width: 16.25rem;
|
||||
|
@ -16,9 +16,10 @@
|
|||
font-size: .8125rem;
|
||||
font-weight: normal;
|
||||
|
||||
&.staging {
|
||||
background-color: $ui-orange;
|
||||
}
|
||||
// Temporary removal of staging styling for testing purposes
|
||||
// &.staging {
|
||||
// background-color: $ui-orange;
|
||||
// }
|
||||
|
||||
&.open {
|
||||
display: block;
|
||||
|
@ -82,7 +83,7 @@
|
|||
border-left: 1px solid $active-gray;
|
||||
border-radius: 5px;
|
||||
|
||||
background-color: $ui-blue;
|
||||
background-color: $ui-purple-dark;
|
||||
width: $arrow-border-width;
|
||||
height: $arrow-border-width;
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
const PropTypes = require('prop-types');
|
||||
const React = require('react');
|
||||
const Sentry = require('@sentry/browser');
|
||||
|
||||
const CrashMessageComponent = require('../crashmessage/crashmessage.jsx');
|
||||
import log from '../../lib/log.js';
|
||||
|
@ -9,28 +8,37 @@ class ErrorBoundary extends React.Component {
|
|||
constructor (props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
hasError: false,
|
||||
errorId: null
|
||||
error: null,
|
||||
errorInfo: null
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle an error caught by this ErrorBoundary component.
|
||||
* @param {Error} error - the error that was caught.
|
||||
* @param {React.ErrorInfo} errorInfo - the React error info associated with the error.
|
||||
*/
|
||||
componentDidCatch (error, errorInfo) {
|
||||
// Display fallback UI
|
||||
Sentry.withScope(scope => {
|
||||
scope.setTag('project', 'scratch-www');
|
||||
if (this.props.componentName) {
|
||||
scope.setTag('component', this.props.componentName);
|
||||
}
|
||||
Object.keys(errorInfo).forEach(key => {
|
||||
scope.setExtra(key, errorInfo[key]);
|
||||
error = error || {
|
||||
stack: 'Unknown stack',
|
||||
message: 'Unknown error'
|
||||
};
|
||||
errorInfo = errorInfo || {
|
||||
componentStack: 'Unknown component stack'
|
||||
};
|
||||
|
||||
// only remember the first error: later errors might just be side effects of that first one
|
||||
if (!this.state.error) {
|
||||
// store error & errorInfo for debugging
|
||||
this.setState({
|
||||
error,
|
||||
errorInfo
|
||||
});
|
||||
Sentry.captureException(error);
|
||||
});
|
||||
this.setState({
|
||||
hasError: true,
|
||||
errorId: Sentry.lastEventId()
|
||||
});
|
||||
log.error(`Unhandled Error: ${error}, info: ${errorInfo}`);
|
||||
}
|
||||
|
||||
// report every error in the console
|
||||
const componentInfo = this.props.componentName ? ` in ${this.props.componentName}` : '';
|
||||
log.error(`Unhandled Error${componentInfo}: ${error.stack}\nComponent stack: ${errorInfo.componentStack}`);
|
||||
}
|
||||
|
||||
handleBack () {
|
||||
|
@ -38,10 +46,9 @@ class ErrorBoundary extends React.Component {
|
|||
}
|
||||
|
||||
render () {
|
||||
if (this.state.hasError) {
|
||||
if (this.state.error) {
|
||||
return (
|
||||
<CrashMessageComponent
|
||||
eventId={this.state.errorId}
|
||||
onBack={this.handleBack}
|
||||
/>
|
||||
);
|
||||
|
|
|
@ -39,7 +39,7 @@
|
|||
.headline-icon {
|
||||
height: 40px;
|
||||
width: 40px;
|
||||
margin: auto;
|
||||
margin: auto 0;
|
||||
}
|
||||
|
||||
.download {
|
||||
|
|
|
@ -1,15 +1,14 @@
|
|||
const injectIntl = require('react-intl').injectIntl;
|
||||
const intlShape = require('react-intl').intlShape;
|
||||
const FormattedMessage = require('react-intl').FormattedMessage;
|
||||
const React = require('react');
|
||||
|
||||
const FlexRow = require('../../../flex-row/flex-row.jsx');
|
||||
const FooterBox = require('../../container/footer.jsx');
|
||||
const LanguageChooser = require('../../../languagechooser/languagechooser.jsx');
|
||||
const {getLocale} = require('../../../../lib/locales.js');
|
||||
|
||||
require('../footer.scss');
|
||||
|
||||
const ConferenceFooter = props => (
|
||||
const ConferenceFooter = () => (
|
||||
<FooterBox>
|
||||
<FlexRow className="scratch-links">
|
||||
<div className="family">
|
||||
|
@ -86,12 +85,8 @@ const ConferenceFooter = props => (
|
|||
</div>
|
||||
</div>
|
||||
</FlexRow>
|
||||
<LanguageChooser locale={props.intl.locale} />
|
||||
<LanguageChooser locale={getLocale()} />
|
||||
</FooterBox>
|
||||
);
|
||||
|
||||
ConferenceFooter.propTypes = {
|
||||
intl: intlShape
|
||||
};
|
||||
|
||||
module.exports = injectIntl(ConferenceFooter);
|
||||
module.exports = ConferenceFooter;
|
||||
|
|
|
@ -1,15 +1,14 @@
|
|||
const FormattedMessage = require('react-intl').FormattedMessage;
|
||||
const injectIntl = require('react-intl').injectIntl;
|
||||
const intlShape = require('react-intl').intlShape;
|
||||
const React = require('react');
|
||||
|
||||
const FlexRow = require('../../../flex-row/flex-row.jsx');
|
||||
const FooterBox = require('../../container/footer.jsx');
|
||||
const LanguageChooser = require('../../../languagechooser/languagechooser.jsx');
|
||||
const {getLocale} = require('../../../../lib/locales.js');
|
||||
|
||||
require('../footer.scss');
|
||||
|
||||
const ConferenceFooter = props => (
|
||||
const ConferenceFooter = () => (
|
||||
<FooterBox>
|
||||
<div className="collaborators">
|
||||
<h4>Sponsors</h4>
|
||||
|
@ -213,12 +212,8 @@ const ConferenceFooter = props => (
|
|||
</div>
|
||||
</div>
|
||||
</FlexRow>
|
||||
<LanguageChooser locale={props.intl.locale} />
|
||||
<LanguageChooser locale={getLocale()} />
|
||||
</FooterBox>
|
||||
);
|
||||
|
||||
ConferenceFooter.propTypes = {
|
||||
intl: intlShape
|
||||
};
|
||||
|
||||
module.exports = injectIntl(ConferenceFooter);
|
||||
module.exports = ConferenceFooter;
|
||||
|
|
|
@ -1,15 +1,14 @@
|
|||
const injectIntl = require('react-intl').injectIntl;
|
||||
const intlShape = require('react-intl').intlShape;
|
||||
const FormattedMessage = require('react-intl').FormattedMessage;
|
||||
const React = require('react');
|
||||
|
||||
const FlexRow = require('../../../flex-row/flex-row.jsx');
|
||||
const FooterBox = require('../../container/footer.jsx');
|
||||
const LanguageChooser = require('../../../languagechooser/languagechooser.jsx');
|
||||
const {getLocale} = require('../../../../lib/locales');
|
||||
|
||||
require('../footer.scss');
|
||||
|
||||
const ConferenceFooter = props => (
|
||||
const ConferenceFooter = () => (
|
||||
<FooterBox>
|
||||
<FlexRow className="scratch-links">
|
||||
<div className="family">
|
||||
|
@ -107,12 +106,8 @@ const ConferenceFooter = props => (
|
|||
</div>
|
||||
</div>
|
||||
</FlexRow>
|
||||
<LanguageChooser locale={props.intl.locale} />
|
||||
<LanguageChooser locale={getLocale()} />
|
||||
</FooterBox>
|
||||
);
|
||||
|
||||
ConferenceFooter.propTypes = {
|
||||
intl: intlShape
|
||||
};
|
||||
|
||||
module.exports = injectIntl(ConferenceFooter);
|
||||
module.exports = ConferenceFooter;
|
||||
|
|
|
@ -1,13 +1,11 @@
|
|||
const FormattedMessage = require('react-intl').FormattedMessage;
|
||||
const injectIntl = require('react-intl').injectIntl;
|
||||
const intlShape = require('react-intl').intlShape;
|
||||
const React = require('react');
|
||||
const PropTypes = require('prop-types');
|
||||
|
||||
|
||||
const FlexRow = require('../../../flex-row/flex-row.jsx');
|
||||
const FooterBox = require('../../container/footer.jsx');
|
||||
const LanguageChooser = require('../../../languagechooser/languagechooser.jsx');
|
||||
const {getLocale} = require('../../../../lib/locales.js');
|
||||
|
||||
require('../footer.scss');
|
||||
|
||||
|
@ -146,7 +144,7 @@ const ConferenceFooter = props => (
|
|||
</div>
|
||||
</div>
|
||||
</FlexRow>
|
||||
<LanguageChooser locale={props.intl.locale} />
|
||||
<LanguageChooser locale={getLocale()} />
|
||||
<div className="organized-by-message">
|
||||
<FormattedMessage id={props.organizedByMsgId} />
|
||||
</div>
|
||||
|
@ -154,8 +152,7 @@ const ConferenceFooter = props => (
|
|||
);
|
||||
|
||||
ConferenceFooter.propTypes = {
|
||||
intl: intlShape,
|
||||
organizedByMsgId: PropTypes.string
|
||||
};
|
||||
|
||||
module.exports = injectIntl(ConferenceFooter);
|
||||
module.exports = ConferenceFooter;
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
const FormattedMessage = require('react-intl').FormattedMessage;
|
||||
const injectIntl = require('react-intl').injectIntl;
|
||||
const intlShape = require('react-intl').intlShape;
|
||||
const MediaQuery = require('react-responsive').default;
|
||||
const connect = require('react-redux').connect;
|
||||
const PropTypes = require('prop-types');
|
||||
|
@ -10,6 +9,8 @@ const FooterBox = require('../container/footer.jsx');
|
|||
const LanguageChooser = require('../../languagechooser/languagechooser.jsx');
|
||||
|
||||
const frameless = require('../../../lib/frameless');
|
||||
const intlShape = require('../../../lib/intl-shape');
|
||||
const {getLocale} = require('../../../lib/locales.js');
|
||||
const getScratchWikiLink = require('../../../lib/scratch-wiki');
|
||||
|
||||
require('./footer.scss');
|
||||
|
@ -25,7 +26,7 @@ const Footer = props => (
|
|||
</a>
|
||||
</dd>
|
||||
<dd>
|
||||
<a href="https://www.scratchfoundation.org/opportunities">
|
||||
<a href="https://www.scratchfoundation.org/careers">
|
||||
<FormattedMessage id="general.jobs" />
|
||||
</a>
|
||||
</dd>
|
||||
|
@ -91,7 +92,7 @@ const Footer = props => (
|
|||
</a>
|
||||
</dd>
|
||||
<dd>
|
||||
<a href="https://www.scratchfoundation.org/opportunities">
|
||||
<a href="https://www.scratchfoundation.org/careers">
|
||||
<FormattedMessage id="general.jobs" />
|
||||
</a>
|
||||
</dd>
|
||||
|
@ -168,6 +169,11 @@ const Footer = props => (
|
|||
<FormattedMessage id="general.privacyPolicy" />
|
||||
</a>
|
||||
</dd>
|
||||
<dd>
|
||||
<a href="/cookies">
|
||||
<FormattedMessage id="general.cookies" />
|
||||
</a>
|
||||
</dd>
|
||||
<dd>
|
||||
<a href="/DMCA">
|
||||
<FormattedMessage id="general.dmca" />
|
||||
|
@ -213,12 +219,12 @@ const Footer = props => (
|
|||
</dl>
|
||||
</div>
|
||||
</MediaQuery>
|
||||
<LanguageChooser locale={props.intl.locale} />
|
||||
<LanguageChooser locale={getLocale()} />
|
||||
</FooterBox>
|
||||
);
|
||||
|
||||
Footer.propTypes = {
|
||||
intl: intlShape.isRequired,
|
||||
intl: intlShape.isRequired, // eslint-disable-line react/no-unused-prop-types
|
||||
scratchWikiLink: PropTypes.string
|
||||
};
|
||||
|
||||
|
|
|
@ -12,17 +12,17 @@
|
|||
font-size: .875rem;
|
||||
|
||||
&:focus {
|
||||
box-shadow: 0 0 0 .25rem $ui-blue-25percent;
|
||||
box-shadow: 0 0 0 .25rem $ui-purple-25percent;
|
||||
outline: none;
|
||||
border: 1px solid $ui-blue;
|
||||
border: 1px solid $ui-purple-dark;
|
||||
transition: all .5s ease, font-size 0s;
|
||||
}
|
||||
|
||||
&.fail {
|
||||
border: 1px solid $ui-orange;
|
||||
border: 1px solid $ui-red-dark;
|
||||
|
||||
&:focus {
|
||||
box-shadow: 0 0 0 .25rem $ui-orange-25percent;
|
||||
box-shadow: 0 0 0 .25rem $ui-red-25percent;
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
.select {
|
||||
.fail {
|
||||
border: 1px solid $ui-orange;
|
||||
border: 1px solid $ui-purple-dark;
|
||||
|
||||
&:focus {
|
||||
box-shadow: 0 0 0 .25rem $ui-orange-25percent;
|
||||
|
|
|
@ -9,7 +9,7 @@ $pass-bg: $ui-aqua;
|
|||
margin: .5em 0;
|
||||
border: 0;
|
||||
border-radius: .5rem;
|
||||
background-color: $ui-blue;
|
||||
background-color: $ui-purple-dark;
|
||||
cursor: pointer;
|
||||
padding: 1em 1.25em;
|
||||
color: $type-white;
|
||||
|
@ -24,7 +24,7 @@ $pass-bg: $ui-aqua;
|
|||
/* DATA BUTTON STATES */
|
||||
&.white {
|
||||
background-color: $base-bg;
|
||||
color: $ui-blue;
|
||||
color: $ui-purple-dark;
|
||||
}
|
||||
|
||||
&.pass {
|
||||
|
|
|
@ -13,12 +13,12 @@
|
|||
&:focus {
|
||||
transition: all .5s ease;
|
||||
outline: none;
|
||||
border: 2px solid $ui-blue;
|
||||
box-shadow: 0 0 0 4px $ui-blue-25percent;
|
||||
border: 2px solid $ui-purple-dark;
|
||||
box-shadow: 0 0 0 4px $ui-purple-25percent;
|
||||
}
|
||||
|
||||
&.fail {
|
||||
border: 1px solid $ui-orange;
|
||||
border: 1px solid $ui-red-dark;
|
||||
}
|
||||
|
||||
&.pass {
|
||||
|
@ -51,12 +51,12 @@
|
|||
&:focus {
|
||||
transition: all .2s ease;
|
||||
outline: none;
|
||||
border: 2px solid $ui-blue;
|
||||
box-shadow: 0 0 0 4px $ui-blue-25percent;
|
||||
border: 2px solid $ui-purple-dark;
|
||||
box-shadow: 0 0 0 4px $ui-purple-25percent;
|
||||
}
|
||||
|
||||
&.fail {
|
||||
border: 1px solid $ui-orange;
|
||||
border: 1px solid $ui-red-dark;
|
||||
}
|
||||
|
||||
&::placeholder {
|
||||
|
|
|
@ -23,11 +23,11 @@ $base-bg: $ui-light-gray;
|
|||
&:focus {
|
||||
transition: all .5s ease;
|
||||
outline: none;
|
||||
border: 1px solid $ui-blue;
|
||||
border: 1px solid $ui-purple-dark;
|
||||
}
|
||||
|
||||
&.fail {
|
||||
border: 1px solid $ui-orange;
|
||||
border: 1px solid $ui-red-dark;
|
||||
}
|
||||
|
||||
&.pass {
|
||||
|
|
|
@ -8,10 +8,10 @@ const ReactPhoneInput = require('react-telephone-input/lib/withStyles').default;
|
|||
const Row = require('formsy-react-components').Row;
|
||||
const Help = require('formsy-react-components/release/components/help').default;
|
||||
const ErrorMessages = require('formsy-react-components/release/components/error-messages').default;
|
||||
const intl = require('react-intl');
|
||||
|
||||
const defaultValidationHOC = require('./validations.jsx').defaultValidationHOC;
|
||||
const inputHOC = require('./input-hoc.jsx');
|
||||
const intl = require('../../lib/intl.jsx');
|
||||
const validationHOCFactory = require('./validations.jsx').validationHOCFactory;
|
||||
|
||||
require('./row.scss');
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
|
||||
&:focus {
|
||||
outline: none;
|
||||
border-color: $ui-purple-dark;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
|
||||
&:focus {
|
||||
outline: none;
|
||||
border: 1px solid $ui-blue;
|
||||
border: 1px solid $ui-purple-dark;
|
||||
}
|
||||
|
||||
&:-moz-focusring {
|
||||
|
|
|
@ -16,11 +16,11 @@
|
|||
&:focus {
|
||||
transition: all 1s ease;
|
||||
outline: none;
|
||||
border: 1px solid $ui-blue;
|
||||
border: 1px solid $ui-purple-dark;
|
||||
}
|
||||
|
||||
&.fail {
|
||||
border: 1px solid $ui-orange;
|
||||
border: 1px solid $ui-red-dark;
|
||||
}
|
||||
|
||||
&::placeholder {
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
margin-left: $arrow-border-width;
|
||||
border: 1px solid $active-gray;
|
||||
border-radius: 5px;
|
||||
background-color: $ui-orange;
|
||||
background-color: $ui-red-dark;
|
||||
padding: 1rem;
|
||||
max-width: 18.75rem;
|
||||
min-height: 1rem;
|
||||
|
@ -32,7 +32,7 @@
|
|||
border-left: 1px solid $active-gray;
|
||||
border-radius: 5px;
|
||||
|
||||
background-color: $ui-orange;
|
||||
background-color: $ui-red-dark;
|
||||
width: $arrow-border-width;
|
||||
height: $arrow-border-width;
|
||||
|
||||
|
@ -73,18 +73,18 @@
|
|||
}
|
||||
|
||||
.validation-error {
|
||||
background-color: $ui-orange;
|
||||
background-color: $ui-red-dark;
|
||||
|
||||
&:before {
|
||||
background-color: $ui-orange;
|
||||
background-color: $ui-red-dark;
|
||||
}
|
||||
}
|
||||
|
||||
.validation-info {
|
||||
background-color: $ui-blue;
|
||||
background-color: $ui-blue-dark;
|
||||
box-shadow: 0 0 4px 2px rgba(0, 0, 0, .15);
|
||||
|
||||
&:before {
|
||||
background-color: $ui-blue;
|
||||
background-color: $ui-blue-dark;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
const defaults = require('lodash.defaultsdeep');
|
||||
const intl = require('../../lib/intl.jsx');
|
||||
const intl = require('react-intl');
|
||||
const omit = require('lodash.omit');
|
||||
const PropTypes = require('prop-types');
|
||||
const React = require('react');
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
const FormattedMessage = require('react-intl').FormattedMessage;
|
||||
const injectIntl = require('react-intl').injectIntl;
|
||||
const intlShape = require('react-intl').intlShape;
|
||||
const bindAll = require('lodash.bindall');
|
||||
const connect = require('react-redux').connect;
|
||||
const PropTypes = require('prop-types');
|
||||
const React = require('react');
|
||||
|
||||
const intlShape = require('../../lib/intl-shape');
|
||||
const Button = require('../forms/button.jsx');
|
||||
const Spinner = require('../spinner/spinner.jsx');
|
||||
|
||||
require('./helpwidget.scss');
|
||||
|
||||
// map Scratch locale to supported Freshdesk locale
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
box-shadow: 0 0 4px 2px rgba(0, 0, 0, .15);
|
||||
padding: .75rem;
|
||||
overflow: visible;
|
||||
background-color: $ui-blue;
|
||||
background-color: $ui-blue-dark;
|
||||
color: $type-white;
|
||||
line-height: 1.25rem;
|
||||
text-align: left;
|
||||
|
@ -48,7 +48,7 @@
|
|||
border-left: 1px solid $active-gray;
|
||||
border-radius: 5px;
|
||||
|
||||
background-color: $ui-blue;
|
||||
background-color: $ui-blue-dark;
|
||||
width: $arrow-border-width;
|
||||
height: $arrow-border-width;
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
.title-banner {
|
||||
&.masthead {
|
||||
background-color: $ui-blue-dark;
|
||||
background-color: $ui-purple-dark;
|
||||
padding-bottom: .5rem;
|
||||
|
||||
h1 {
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
.intro-container {
|
||||
min-height: 24rem;
|
||||
justify-content: space-between;
|
||||
background-color: $ui-blue;
|
||||
background-color: $ui-purple-dark;
|
||||
background-size: 624px 325px;
|
||||
background-repeat: no-repeat;
|
||||
background-position: right;
|
||||
|
@ -80,7 +80,7 @@
|
|||
.intro-button {
|
||||
border-radius: 4px;
|
||||
background-color: $ui-white;
|
||||
color: $ui-blue;
|
||||
color: $ui-purple-dark;
|
||||
padding: .625rem .75rem;
|
||||
font-size: 1rem;
|
||||
margin-right: .75rem;
|
||||
|
|
|
@ -3,9 +3,10 @@ const classNames = require('classnames');
|
|||
const React = require('react');
|
||||
const PropTypes = require('prop-types');
|
||||
import {Formik} from 'formik';
|
||||
const {injectIntl, intlShape} = require('react-intl');
|
||||
const {injectIntl} = require('react-intl');
|
||||
const FormattedMessage = require('react-intl').FormattedMessage;
|
||||
|
||||
const intlShape = require('../../lib/intl-shape');
|
||||
const FormikSelect = require('../../components/formik-forms/formik-select.jsx');
|
||||
const JoinFlowStep = require('./join-flow-step.jsx');
|
||||
const InfoButton = require('../info-button/info-button.jsx');
|
||||
|
|
|
@ -3,9 +3,10 @@ const classNames = require('classnames');
|
|||
const React = require('react');
|
||||
const PropTypes = require('prop-types');
|
||||
import {Formik} from 'formik';
|
||||
const {injectIntl, intlShape} = require('react-intl');
|
||||
const {injectIntl} = require('react-intl');
|
||||
|
||||
const countryData = require('../../lib/country-data');
|
||||
const intlShape = require('../../lib/intl-shape');
|
||||
const FormikSelect = require('../../components/formik-forms/formik-select.jsx');
|
||||
const JoinFlowStep = require('./join-flow-step.jsx');
|
||||
const FormikCheckbox = require('../../components/formik-forms/formik-checkbox.jsx');
|
||||
|
|
|
@ -3,14 +3,16 @@ const classNames = require('classnames');
|
|||
const React = require('react');
|
||||
const PropTypes = require('prop-types');
|
||||
import {Formik} from 'formik';
|
||||
const {injectIntl, intlShape} = require('react-intl');
|
||||
const {injectIntl} = require('react-intl');
|
||||
const FormattedMessage = require('react-intl').FormattedMessage;
|
||||
|
||||
const intlShape = require('../../lib/intl-shape');
|
||||
const validate = require('../../lib/validate');
|
||||
const JoinFlowStep = require('./join-flow-step.jsx');
|
||||
const FormikInput = require('../../components/formik-forms/formik-input.jsx');
|
||||
const InfoButton = require('../info-button/info-button.jsx');
|
||||
const Captcha = require('../../components/captcha/captcha.jsx');
|
||||
|
||||
require('./join-flow-steps.scss');
|
||||
|
||||
class EmailStep extends React.Component {
|
||||
|
|
|
@ -3,9 +3,10 @@ const classNames = require('classnames');
|
|||
const React = require('react');
|
||||
const PropTypes = require('prop-types');
|
||||
import {Formik} from 'formik';
|
||||
const {injectIntl, intlShape} = require('react-intl');
|
||||
const {injectIntl} = require('react-intl');
|
||||
const FormattedMessage = require('react-intl').FormattedMessage;
|
||||
|
||||
const intlShape = require('../../lib/intl-shape');
|
||||
const FormikRadioButton = require('../../components/formik-forms/formik-radio-button.jsx');
|
||||
const JoinFlowStep = require('./join-flow-step.jsx');
|
||||
const InfoButton = require('../info-button/info-button.jsx');
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
margin-bottom: .5rem;
|
||||
|
||||
&:focus {
|
||||
box-shadow: 0 0 0 .25rem $ui-blue-25percent;
|
||||
box-shadow: 0 0 0 .25rem $ui-purple-25percent;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,10 +3,10 @@ const connect = require('react-redux').connect;
|
|||
const defaults = require('lodash.defaultsdeep');
|
||||
const PropTypes = require('prop-types');
|
||||
const React = require('react');
|
||||
const injectIntl = require('react-intl').injectIntl;
|
||||
|
||||
const api = require('../../lib/api');
|
||||
const injectIntl = require('../../lib/intl.jsx').injectIntl;
|
||||
const intlShape = require('../../lib/intl.jsx').intlShape;
|
||||
const intlShape = require('../../lib/intl-shape');
|
||||
const sessionActions = require('../../redux/session.js');
|
||||
const validate = require('../../lib/validate');
|
||||
|
||||
|
@ -221,15 +221,11 @@ class JoinFlow extends React.Component {
|
|||
resetState () {
|
||||
this.setState(this.initialState);
|
||||
}
|
||||
sendAnalytics (path) {
|
||||
const gaID = window.GA_ID;
|
||||
if (!window.ga) {
|
||||
return;
|
||||
}
|
||||
window.ga('send', {
|
||||
hitType: 'pageview',
|
||||
page: path,
|
||||
tid: gaID
|
||||
sendAnalytics (joinFlowStep) {
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
window.dataLayer.push({
|
||||
event: 'join_flow',
|
||||
joinFlowStep
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ const React = require('react');
|
|||
const PropTypes = require('prop-types');
|
||||
const injectIntl = require('react-intl').injectIntl;
|
||||
|
||||
const intl = require('../../lib/intl.jsx');
|
||||
const intlShape = require('../../lib/intl-shape');
|
||||
const Spinner = require('../../components/spinner/spinner.jsx');
|
||||
const ModalTitle = require('../modal/base/modal-title.jsx');
|
||||
|
||||
|
@ -29,7 +29,7 @@ const NextStepButton = props => (
|
|||
|
||||
NextStepButton.propTypes = {
|
||||
content: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
|
||||
intl: intl.intlShape,
|
||||
intl: intlShape,
|
||||
waiting: PropTypes.bool
|
||||
};
|
||||
|
||||
|
|
|
@ -8,11 +8,11 @@
|
|||
border-bottom-left-radius: 1rem;
|
||||
border-bottom-right-radius: 1rem;
|
||||
height: 5.1875rem;
|
||||
background-color: $ui-orange;
|
||||
background-color: $ui-purple-dark;
|
||||
|
||||
&:hover {
|
||||
transition: background-color .25s ease;
|
||||
background-color: $ui-orange-90percent;
|
||||
background-color: $ui-purple-darker;
|
||||
}
|
||||
|
||||
/* match the small window setting for modal as a whole */
|
||||
|
|
|
@ -2,8 +2,9 @@ const bindAll = require('lodash.bindall');
|
|||
const React = require('react');
|
||||
const PropTypes = require('prop-types');
|
||||
const FormattedMessage = require('react-intl').FormattedMessage;
|
||||
const {injectIntl, intlShape} = require('react-intl');
|
||||
const {injectIntl} = require('react-intl');
|
||||
|
||||
const intlShape = require('../../lib/intl-shape');
|
||||
const JoinFlowStep = require('./join-flow-step.jsx');
|
||||
|
||||
require('./join-flow-steps.scss');
|
||||
|
|
|
@ -3,8 +3,9 @@ const classNames = require('classnames');
|
|||
const React = require('react');
|
||||
const PropTypes = require('prop-types');
|
||||
import {Formik} from 'formik';
|
||||
const {injectIntl, intlShape} = require('react-intl');
|
||||
const {injectIntl} = require('react-intl');
|
||||
|
||||
const intlShape = require('../../lib/intl-shape');
|
||||
const validate = require('../../lib/validate');
|
||||
const FormikInput = require('../../components/formik-forms/formik-input.jsx');
|
||||
const FormikCheckbox = require('../../components/formik-forms/formik-checkbox.jsx');
|
||||
|
|
|
@ -3,8 +3,9 @@ const React = require('react');
|
|||
const PropTypes = require('prop-types');
|
||||
import {Formik} from 'formik';
|
||||
const FormattedMessage = require('react-intl').FormattedMessage;
|
||||
const {injectIntl, intlShape} = require('react-intl');
|
||||
const {injectIntl} = require('react-intl');
|
||||
|
||||
const intlShape = require('../../lib/intl-shape');
|
||||
const JoinFlowStep = require('./join-flow-step.jsx');
|
||||
|
||||
require('./join-flow-steps.scss');
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
const bindAll = require('lodash.bindall');
|
||||
const classNames = require('classnames');
|
||||
const injectIntl = require('react-intl').injectIntl;
|
||||
const intlShape = require('react-intl').intlShape;
|
||||
const PropTypes = require('prop-types');
|
||||
const React = require('react');
|
||||
|
||||
const intlShape = require('../../lib/intl-shape');
|
||||
const jar = require('../../lib/jar.js');
|
||||
const languages = require('scratch-l10n').default;
|
||||
const Form = require('../forms/form.jsx');
|
||||
|
|
|
@ -3,8 +3,8 @@ const connect = require('react-redux').connect;
|
|||
const FormattedMessage = require('react-intl').FormattedMessage;
|
||||
const PropTypes = require('prop-types');
|
||||
const injectIntl = require('react-intl').injectIntl;
|
||||
const intlShape = require('react-intl').intlShape;
|
||||
|
||||
const intlShape = require('../../lib/intl-shape');
|
||||
const navigationActions = require('../../redux/navigation.js');
|
||||
const Modal = require('../modal/base/modal.jsx');
|
||||
|
||||
|
|
|
@ -160,15 +160,15 @@ are not obscured by gradient overlay */
|
|||
}
|
||||
|
||||
.studio-status-icon-unselected {
|
||||
background-color: $ui-blue;
|
||||
background-color: $ui-purple-dark;
|
||||
}
|
||||
|
||||
.submit-button {
|
||||
background-color: $ui-blue;
|
||||
background-color: $ui-purple-dark;
|
||||
}
|
||||
|
||||
.submit-button-waiting {
|
||||
background-color: $ui-blue;
|
||||
background-color: $ui-purple-dark;
|
||||
}
|
||||
|
||||
.studio-status-icon-plus-img,
|
||||
|
|
|
@ -2,9 +2,9 @@ const PropTypes = require('prop-types');
|
|||
const React = require('react');
|
||||
const FormattedMessage = require('react-intl').FormattedMessage;
|
||||
const injectIntl = require('react-intl').injectIntl;
|
||||
const intlShape = require('react-intl').intlShape;
|
||||
const Modal = require('../base/modal.jsx');
|
||||
|
||||
const intlShape = require('../../../lib/intl-shape');
|
||||
const Form = require('../../forms/form.jsx');
|
||||
const Button = require('../../forms/button.jsx');
|
||||
const Spinner = require('../../spinner/spinner.jsx');
|
||||
|
|
|
@ -2,9 +2,9 @@ const PropTypes = require('prop-types');
|
|||
const React = require('react');
|
||||
const FormattedMessage = require('react-intl').FormattedMessage;
|
||||
const injectIntl = require('react-intl').injectIntl;
|
||||
const intlShape = require('react-intl').intlShape;
|
||||
const Modal = require('../base/modal.jsx');
|
||||
|
||||
const intlShape = require('../../../lib/intl-shape');
|
||||
const Button = require('../../forms/button.jsx');
|
||||
const FlexRow = require('../../flex-row/flex-row.jsx');
|
||||
|
||||
|
|
|
@ -17,8 +17,8 @@ $medium-and-small: "screen and (max-width : #{$mobileIntermediate}-1)";
|
|||
|
||||
.report-modal-header {
|
||||
border-radius: 1rem 1rem 0 0;
|
||||
box-shadow: inset 0 -1px 0 0 $ui-coral-dark;
|
||||
background-color: $ui-coral;
|
||||
box-shadow: inset 0 -1px 0 0 $ui-red-dark;
|
||||
background-color: $ui-red-dark;
|
||||
padding-top: .75rem;
|
||||
width: 100%;
|
||||
height: 3rem;
|
||||
|
|
|
@ -2,9 +2,9 @@ const PropTypes = require('prop-types');
|
|||
const React = require('react');
|
||||
const FormattedMessage = require('react-intl').FormattedMessage;
|
||||
const injectIntl = require('react-intl').injectIntl;
|
||||
const intlShape = require('react-intl').intlShape;
|
||||
const Modal = require('../base/modal.jsx');
|
||||
|
||||
const intlShape = require('../../../lib/intl-shape');
|
||||
const Button = require('../../forms/button.jsx');
|
||||
const FlexRow = require('../../flex-row/flex-row.jsx');
|
||||
|
||||
|
|
|
@ -3,7 +3,6 @@ const PropTypes = require('prop-types');
|
|||
const React = require('react');
|
||||
const FormattedMessage = require('react-intl').FormattedMessage;
|
||||
const injectIntl = require('react-intl').injectIntl;
|
||||
const intlShape = require('react-intl').intlShape;
|
||||
const Modal = require('../base/modal.jsx');
|
||||
const ModalInnerContent = require('../base/modal-inner-content.jsx');
|
||||
const Button = require('../../forms/button.jsx');
|
||||
|
@ -12,9 +11,11 @@ const FlexRow = require('../../flex-row/flex-row.jsx');
|
|||
const MuteStep = require('./mute-step.jsx');
|
||||
const FeedbackForm = require('./feedback-form.jsx');
|
||||
const classNames = require('classnames');
|
||||
require('./modal.scss');
|
||||
|
||||
const api = require('../../../lib/api');
|
||||
const intlShape = require('../../../lib/intl-shape');
|
||||
|
||||
require('./modal.scss');
|
||||
|
||||
const steps = {
|
||||
COMMENT_ISSUE: 0,
|
||||
|
|
|
@ -4,9 +4,9 @@ const React = require('react');
|
|||
const connect = require('react-redux').connect;
|
||||
const FormattedMessage = require('react-intl').FormattedMessage;
|
||||
const injectIntl = require('react-intl').injectIntl;
|
||||
const intlShape = require('react-intl').intlShape;
|
||||
const Modal = require('../base/modal.jsx');
|
||||
|
||||
const intlShape = require('../../../lib/intl-shape');
|
||||
const ModalTitle = require('../base/modal-title.jsx');
|
||||
const ModalInnerContent = require('../base/modal-inner-content.jsx');
|
||||
const Select = require('../../forms/select.jsx');
|
||||
|
|
|
@ -110,8 +110,8 @@
|
|||
&:focus {
|
||||
transition: all .2s ease;
|
||||
outline: none;
|
||||
border: 2px solid $ui-blue;
|
||||
box-shadow: 0 0 0 4px $ui-blue-25percent;
|
||||
border: 2px solid $ui-purple-dark;
|
||||
box-shadow: 0 0 0 4px $ui-purple-25percent;
|
||||
}
|
||||
|
||||
&.social-textarea {
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
const PropTypes = require('prop-types');
|
||||
const React = require('react');
|
||||
const injectIntl = require('react-intl').injectIntl;
|
||||
const intlShape = require('react-intl').intlShape;
|
||||
const classNames = require('classnames');
|
||||
|
||||
const intlShape = require('../../../lib/intl-shape');
|
||||
const Modal = require('../base/modal.jsx');
|
||||
const ModalTitle = require('../base/modal-title.jsx');
|
||||
const ModalInnerContent = require('../base/modal-inner-content.jsx');
|
||||
|
|
|
@ -20,7 +20,8 @@ const TTTModal = props => (
|
|||
'cardsUrl',
|
||||
'guideUrl',
|
||||
'thumbImage',
|
||||
'modalImage'
|
||||
'modalImage',
|
||||
'modalImageDescription'
|
||||
]
|
||||
)}
|
||||
>
|
||||
|
@ -29,7 +30,7 @@ const TTTModal = props => (
|
|||
<a href={props.tutorialUrl}>
|
||||
<div className="ttt-img-container">
|
||||
<img
|
||||
alt=""
|
||||
alt={props.modalImageDescription}
|
||||
className="mod-ttt-img"
|
||||
src={props.modalImage}
|
||||
/>
|
||||
|
@ -91,6 +92,7 @@ TTTModal.propTypes = {
|
|||
description: PropTypes.string.isRequired,
|
||||
guideUrl: PropTypes.string.isRequired,
|
||||
modalImage: PropTypes.string.isRequired,
|
||||
modalImageDescription: PropTypes.string,
|
||||
thumbImage: PropTypes.string.isRequired,
|
||||
title: PropTypes.string.isRequired,
|
||||
tutorialUrl: PropTypes.string.isRequired
|
||||
|
|
|
@ -9,11 +9,12 @@
|
|||
border-bottom: 1px solid $active-gray;
|
||||
|
||||
box-shadow: 0 0 3px $box-shadow-gray;
|
||||
background-color: $ui-blue;
|
||||
background-color: $ui-purple-dark;
|
||||
|
||||
&.staging {
|
||||
background-color: $ui-orange;
|
||||
}
|
||||
// Temporary removal of staging styling for testing purposes
|
||||
// &.staging {
|
||||
// background-color: $ui-orange;
|
||||
// }
|
||||
|
||||
|
||||
width: 100%;
|
||||
|
|
|
@ -3,10 +3,10 @@ const classNames = require('classnames');
|
|||
const connect = require('react-redux').connect;
|
||||
const FormattedMessage = require('react-intl').FormattedMessage;
|
||||
const injectIntl = require('react-intl').injectIntl;
|
||||
const intlShape = require('react-intl').intlShape;
|
||||
const PropTypes = require('prop-types');
|
||||
const React = require('react');
|
||||
|
||||
const intlShape = require('../../../lib/intl-shape');
|
||||
const messageCountActions = require('../../../redux/message-count.js');
|
||||
const navigationActions = require('../../../redux/navigation.js');
|
||||
const sessionActions = require('../../../redux/session.js');
|
||||
|
|
|
@ -2,17 +2,18 @@
|
|||
@import "../../../frameless";
|
||||
|
||||
#navigation {
|
||||
&.staging {
|
||||
.messages {
|
||||
.message-count {
|
||||
display: none;
|
||||
// Temporary removal of staging styling for testing purposes
|
||||
// &.staging {
|
||||
// .messages {
|
||||
// .message-count {
|
||||
// display: none;
|
||||
|
||||
&.show {
|
||||
background-color: $ui-blue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// &.show {
|
||||
// background-color: $ui-blue;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
.logo {
|
||||
margin-right: 10px;
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
h4 {
|
||||
display: block;
|
||||
|
||||
color: $link-blue;
|
||||
color: $link-purple;
|
||||
font-size: .85rem;
|
||||
}
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
}
|
||||
|
||||
&.active {
|
||||
background-color: $ui-blue;
|
||||
background-color: $ui-purple-dark;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ const Navigation = require('../../navigation/www/navigation.jsx');
|
|||
const Footer = require('../../footer/www/footer.jsx');
|
||||
const DonorRecognition = require('./donor-recognition.jsx');
|
||||
const ErrorBoundary = require('../../errorboundary/errorboundary.jsx');
|
||||
const PrivacyBanner = require('../../privacy-banner/privacy-banner.jsx');
|
||||
|
||||
const today = new Date();
|
||||
const semi = today.getDate() === 1 && today.getMonth() === 3;
|
||||
|
@ -25,6 +26,7 @@ const Page = ({
|
|||
>
|
||||
<Navigation />
|
||||
</nav>
|
||||
<PrivacyBanner />
|
||||
<main id="view">
|
||||
{children}
|
||||
</main>
|
||||
|
|
107
src/components/privacy-banner/privacy-banner.jsx
Normal file
107
src/components/privacy-banner/privacy-banner.jsx
Normal file
|
@ -0,0 +1,107 @@
|
|||
const bindAll = require('lodash.bindall');
|
||||
const FormattedMessage = require('react-intl').FormattedMessage;
|
||||
const injectIntl = require('react-intl').injectIntl;
|
||||
const connect = require('react-redux').connect;
|
||||
const PropTypes = require('prop-types');
|
||||
const React = require('react');
|
||||
|
||||
const TitleBanner = require('../title-banner/title-banner.jsx');
|
||||
const Button = require('../forms/button.jsx');
|
||||
const jar = require('../../lib/jar.js');
|
||||
|
||||
require('./privacy-banner.scss');
|
||||
|
||||
const PRIVACY_UPDATE_START_TIME = 1684987200000; // May 25 2023 0000 ET
|
||||
const PRIVACY_UPDATE_END_TIME = 1686887999000; // Jun 15 2023 1159 ET
|
||||
|
||||
class PrivacyBanner extends React.Component {
|
||||
constructor (props) {
|
||||
super(props);
|
||||
bindAll(this, [
|
||||
'shouldShowBanner',
|
||||
'handleCloseBanner'
|
||||
]);
|
||||
}
|
||||
|
||||
shouldShowBanner () {
|
||||
const seen = jar.get('scratchpolicyseen');
|
||||
return (
|
||||
Date.now() >= PRIVACY_UPDATE_START_TIME &&
|
||||
Date.now() < PRIVACY_UPDATE_END_TIME &&
|
||||
typeof seen === 'undefined' &&
|
||||
typeof this.props.user !== 'undefined'
|
||||
);
|
||||
}
|
||||
|
||||
handleCloseBanner () {
|
||||
const opts = {
|
||||
expires: new Date(new Date().setDate(new Date().getDate() + 21)) // expires after 3 weeks
|
||||
};
|
||||
this.setState({dismissedPrivacyBanner: true});
|
||||
jar.set('scratchpolicyseen', true, opts);
|
||||
}
|
||||
render () {
|
||||
const showBanner = this.shouldShowBanner();
|
||||
const privacyPolicyLink = chunks => <a href="/privacy_policy">{chunks}</a>;
|
||||
if (showBanner) {
|
||||
return (
|
||||
<aside className="privacy-aside">
|
||||
<TitleBanner className="privacy-banner">
|
||||
<div className="privacy-banner-container">
|
||||
<img
|
||||
aria-hidden="true"
|
||||
alt=""
|
||||
className="lightbulb-icon"
|
||||
src="/images/ideas/bulb-icon.svg"
|
||||
/>
|
||||
<div className="privacy-banner-centered">
|
||||
<p className="privacy-banner-text">
|
||||
<FormattedMessage
|
||||
id="privacyBanner.update"
|
||||
values={{
|
||||
a: privacyPolicyLink
|
||||
}}
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
<Button
|
||||
isCloseType
|
||||
className="privacy-close-button"
|
||||
key="closeButton"
|
||||
name="closeButton"
|
||||
type="button"
|
||||
onClick={this.handleCloseBanner}
|
||||
>
|
||||
<div className="action-button-text">
|
||||
<FormattedMessage id="general.close" />
|
||||
</div>
|
||||
</Button>
|
||||
</div>
|
||||
</TitleBanner>
|
||||
</aside>
|
||||
);
|
||||
}
|
||||
|
||||
// if we're not showing the banner, return null to not render anything
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
user: state.session && state.session.session && state.session.session.user
|
||||
});
|
||||
|
||||
PrivacyBanner.propTypes = {
|
||||
// onRequestClose: PropTypes.func
|
||||
user: PropTypes.shape({
|
||||
classroomId: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
|
||||
thumbnailUrl: PropTypes.string,
|
||||
username: PropTypes.string
|
||||
})
|
||||
};
|
||||
|
||||
const ConnectedPrivacyBanner = connect(
|
||||
mapStateToProps
|
||||
)(PrivacyBanner);
|
||||
|
||||
module.exports = injectIntl(ConnectedPrivacyBanner);
|
75
src/components/privacy-banner/privacy-banner.scss
Normal file
75
src/components/privacy-banner/privacy-banner.scss
Normal file
|
@ -0,0 +1,75 @@
|
|||
@import "../../colors";
|
||||
@import "../../frameless";
|
||||
|
||||
.privacy-aside {
|
||||
position: sticky;
|
||||
}
|
||||
|
||||
.privacy-banner {
|
||||
display: flex;
|
||||
z-index: 8;
|
||||
background-color: $ui-purple-darker;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: -50px;
|
||||
margin-top: 50px;
|
||||
|
||||
.privacy-banner-container {
|
||||
display: flex;
|
||||
margin: 0.375rem auto;
|
||||
align-items: center;
|
||||
|
||||
.lightbulb-icon {
|
||||
margin: 0.6875rem;
|
||||
width: 1.75rem;
|
||||
height: 1.75rem;
|
||||
}
|
||||
|
||||
.privacy-banner-centered {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.privacy-banner-text {
|
||||
text-align: left;
|
||||
color: $ui-white;
|
||||
font-size: 1rem;
|
||||
font-weight: bold;
|
||||
margin-right: 1rem;
|
||||
max-width: 70vw;
|
||||
}
|
||||
}
|
||||
|
||||
.privacy-close-button {
|
||||
right: 1rem;
|
||||
top: auto;
|
||||
}
|
||||
|
||||
a {
|
||||
color: $ui-white;
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: $mobileIntermediate) {
|
||||
.privacy-banner .privacy-banner-container .privacy-banner-centered {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.privacy-banner .privacy-banner-container .lightbulb-icon {
|
||||
padding-bottom: 2rem;
|
||||
}
|
||||
|
||||
.privacy-banner .privacy-banner-container {
|
||||
margin-left: 0;
|
||||
margin-bottom: 1rem
|
||||
}
|
||||
|
||||
.privacy-banner .donate-close-button {
|
||||
top: 1rem;
|
||||
}
|
||||
}
|
|
@ -1,11 +1,11 @@
|
|||
/* eslint-disable react/no-multi-comp */
|
||||
const bindAll = require('lodash.bindall');
|
||||
const injectIntl = require('react-intl').injectIntl;
|
||||
const intlShape = require('react-intl').intlShape;
|
||||
const PropTypes = require('prop-types');
|
||||
const React = require('react');
|
||||
const intl = require('react-intl');
|
||||
|
||||
const intl = require('../../lib/intl.jsx');
|
||||
const intlShape = require('../../lib/intl-shape');
|
||||
|
||||
const Card = require('../../components/card/card.jsx');
|
||||
const Checkbox = require('../../components/forms/checkbox.jsx');
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
/* eslint-disable react/no-multi-comp */
|
||||
const bindAll = require('lodash.bindall');
|
||||
const injectIntl = require('react-intl').injectIntl;
|
||||
const intlShape = require('react-intl').intlShape;
|
||||
const {injectIntl, FormattedMessage} = require('react-intl');
|
||||
const omit = require('lodash.omit');
|
||||
const PropTypes = require('prop-types');
|
||||
const React = require('react');
|
||||
|
||||
const api = require('../../lib/api');
|
||||
const countryData = require('../../lib/country-data');
|
||||
const intl = require('../../lib/intl.jsx');
|
||||
const intlShape = require('../../lib/intl-shape');
|
||||
|
||||
const Avatar = require('../../components/avatar/avatar.jsx');
|
||||
const Button = require('../../components/forms/button.jsx');
|
||||
|
@ -174,7 +173,7 @@ class UsernameStep extends React.Component {
|
|||
{this.props.title ? (
|
||||
this.props.title
|
||||
) : (
|
||||
<intl.FormattedMessage id="registration.usernameStepTitle" />
|
||||
<FormattedMessage id="registration.usernameStepTitle" />
|
||||
)}
|
||||
</h2>
|
||||
<p className="description">
|
||||
|
@ -182,9 +181,9 @@ class UsernameStep extends React.Component {
|
|||
this.props.description
|
||||
) : (
|
||||
<span>
|
||||
<intl.FormattedMessage id="registration.usernameStepDescription" />
|
||||
<FormattedMessage id="registration.usernameStepDescription" />
|
||||
<b>
|
||||
<intl.FormattedMessage id="registration.usernameStepRealName" />
|
||||
<FormattedMessage id="registration.usernameStepRealName" />
|
||||
</b>
|
||||
</span>
|
||||
)}
|
||||
|
@ -281,7 +280,7 @@ class UsernameStep extends React.Component {
|
|||
/>
|
||||
<GeneralError name="all" />
|
||||
<NextStepButton
|
||||
text={<intl.FormattedMessage id="registration.nextStep" />}
|
||||
text={<FormattedMessage id="registration.nextStep" />}
|
||||
waiting={this.props.waiting || this.state.waiting}
|
||||
/>
|
||||
</Form>
|
||||
|
@ -298,7 +297,7 @@ class UsernameStep extends React.Component {
|
|||
UsernameStep.propTypes = {
|
||||
activeStep: PropTypes.number,
|
||||
description: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
|
||||
intl: intl.intlShape,
|
||||
intl: intlShape,
|
||||
onNextStep: PropTypes.func,
|
||||
showPassword: PropTypes.bool,
|
||||
title: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
|
||||
|
@ -339,7 +338,7 @@ class ChoosePasswordStep extends React.Component {
|
|||
{this.props.intl.formatMessage({id: 'registration.choosePasswordStepTitle'})}
|
||||
</h2>
|
||||
<p className="description">
|
||||
<intl.FormattedMessage id="registration.choosePasswordStepDescription" />
|
||||
<FormattedMessage id="registration.choosePasswordStepDescription" />
|
||||
<Tooltip
|
||||
tipContent={
|
||||
this.props.intl.formatMessage({id: 'registration.choosePasswordStepTooltip'})
|
||||
|
@ -384,7 +383,7 @@ class ChoosePasswordStep extends React.Component {
|
|||
onChange={this.handleChangeShowPassword}
|
||||
/>
|
||||
<NextStepButton
|
||||
text={<intl.FormattedMessage id="registration.nextStep" />}
|
||||
text={<FormattedMessage id="registration.nextStep" />}
|
||||
waiting={this.props.waiting || this.state.waiting}
|
||||
/>
|
||||
</Form>
|
||||
|
@ -501,12 +500,12 @@ class DemographicsStep extends React.Component {
|
|||
return (
|
||||
<Slide className="registration-step demographics-step">
|
||||
<h2>
|
||||
<intl.FormattedMessage id="registration.personalStepTitle" />
|
||||
<FormattedMessage id="registration.personalStepTitle" />
|
||||
</h2>
|
||||
<p className="description">
|
||||
{this.props.description ?
|
||||
this.props.description :
|
||||
<intl.FormattedMessage id="registration.personalStepDescription" />
|
||||
<FormattedMessage id="registration.personalStepDescription" />
|
||||
}
|
||||
<Tooltip
|
||||
tipContent={
|
||||
|
@ -589,7 +588,7 @@ class DemographicsStep extends React.Component {
|
|||
valueLabel="I'm a robot!"
|
||||
/>
|
||||
<NextStepButton
|
||||
text={<intl.FormattedMessage id="registration.nextStep" />}
|
||||
text={<FormattedMessage id="registration.nextStep" />}
|
||||
waiting={this.props.waiting}
|
||||
/>
|
||||
</Form>
|
||||
|
@ -629,10 +628,10 @@ const IntlDemographicsStep = injectIntl(DemographicsStep);
|
|||
const NameStep = props => (
|
||||
<Slide className="registration-step name-step">
|
||||
<h2>
|
||||
<intl.FormattedHTMLMessage id="teacherRegistration.nameStepTitle" />
|
||||
<FormattedMessage id="teacherRegistration.nameStepTitleNew" />
|
||||
</h2>
|
||||
<p className="description">
|
||||
<intl.FormattedMessage id="teacherRegistration.nameStepDescription" />
|
||||
<FormattedMessage id="teacherRegistration.nameStepDescription" />
|
||||
<Tooltip
|
||||
tipContent={
|
||||
props.intl.formatMessage({id: 'registration.nameStepTooltip'})
|
||||
|
@ -675,7 +674,7 @@ const NameStep = props => (
|
|||
}}
|
||||
/>
|
||||
<NextStepButton
|
||||
text={<intl.FormattedMessage id="registration.nextStep" />}
|
||||
text={<FormattedMessage id="registration.nextStep" />}
|
||||
waiting={props.waiting}
|
||||
/>
|
||||
</Form>
|
||||
|
@ -748,10 +747,10 @@ class OrganizationStep extends React.Component {
|
|||
return (
|
||||
<Slide className="registration-step organization-step">
|
||||
<h2>
|
||||
<intl.FormattedMessage id="teacherRegistration.organization" />
|
||||
<FormattedMessage id="teacherRegistration.organization" />
|
||||
</h2>
|
||||
<p className="description">
|
||||
<intl.FormattedMessage id="teacherRegistration.privacyDescription" />
|
||||
<FormattedMessage id="teacherRegistration.privacyDescription" />
|
||||
<Tooltip
|
||||
tipContent={
|
||||
this.props.intl.formatMessage({id: 'registration.nameStepTooltip'})
|
||||
|
@ -792,9 +791,9 @@ class OrganizationStep extends React.Component {
|
|||
}}
|
||||
/>
|
||||
<div className="organization-type">
|
||||
<b className="row-label"><intl.FormattedMessage id="teacherRegistration.orgType" /></b>
|
||||
<b className="row-label"><FormattedMessage id="teacherRegistration.orgType" /></b>
|
||||
<p className="help-text">
|
||||
<intl.FormattedMessage id="teacherRegistration.checkAll" />
|
||||
<FormattedMessage id="teacherRegistration.checkAll" />
|
||||
</p>
|
||||
<CheckboxGroup
|
||||
required
|
||||
|
@ -833,9 +832,9 @@ class OrganizationStep extends React.Component {
|
|||
/>
|
||||
</div>
|
||||
<div className="url-input">
|
||||
<b className="row-label"><intl.FormattedMessage id="general.website" /></b>
|
||||
<b className="row-label"><FormattedMessage id="general.website" /></b>
|
||||
<p className="help-text">
|
||||
<intl.FormattedMessage id="teacherRegistration.notRequired" />
|
||||
<FormattedMessage id="teacherRegistration.notRequired" />
|
||||
</p>
|
||||
<Input
|
||||
name="organization.url"
|
||||
|
@ -853,7 +852,7 @@ class OrganizationStep extends React.Component {
|
|||
/>
|
||||
</div>
|
||||
<NextStepButton
|
||||
text={<intl.FormattedMessage id="registration.nextStep" />}
|
||||
text={<FormattedMessage id="registration.nextStep" />}
|
||||
waiting={this.props.waiting}
|
||||
/>
|
||||
</Form>
|
||||
|
@ -906,10 +905,10 @@ class AddressStep extends React.Component {
|
|||
return (
|
||||
<Slide className="registration-step address-step">
|
||||
<h2>
|
||||
<intl.FormattedMessage id="teacherRegistration.addressStepTitle" />
|
||||
<FormattedMessage id="teacherRegistration.addressStepTitle" />
|
||||
</h2>
|
||||
<p className="description">
|
||||
<intl.FormattedMessage id="teacherRegistration.privacyDescription" />
|
||||
<FormattedMessage id="teacherRegistration.privacyDescription" />
|
||||
<Tooltip
|
||||
tipContent={
|
||||
this.props.intl.formatMessage({id: 'registration.nameStepTooltip'})
|
||||
|
@ -988,10 +987,10 @@ class AddressStep extends React.Component {
|
|||
/> : []
|
||||
}
|
||||
<b className="row-label">
|
||||
<intl.FormattedMessage id="teacherRegistration.zipCode" />
|
||||
<FormattedMessage id="teacherRegistration.zipCode" />
|
||||
</b>
|
||||
{this.state.countryChoice === 'us' ? [] : <p className="help-text">
|
||||
<intl.FormattedMessage id="teacherRegistration.notRequired" />
|
||||
<FormattedMessage id="teacherRegistration.notRequired" />
|
||||
</p>}
|
||||
<Input
|
||||
name="address.zip"
|
||||
|
@ -1008,7 +1007,7 @@ class AddressStep extends React.Component {
|
|||
/>
|
||||
<GeneralError name="all" />
|
||||
<NextStepButton
|
||||
text={<intl.FormattedMessage id="registration.nextStep" />}
|
||||
text={<FormattedMessage id="registration.nextStep" />}
|
||||
waiting={this.props.waiting || this.state.waiting}
|
||||
/>
|
||||
</Form>
|
||||
|
@ -1062,10 +1061,10 @@ class UseScratchStep extends React.Component {
|
|||
return (
|
||||
<Slide className="registration-step usescratch-step">
|
||||
<h2>
|
||||
<intl.FormattedMessage id="teacherRegistration.useScratchStepTitle" />
|
||||
<FormattedMessage id="teacherRegistration.useScratchStepTitle" />
|
||||
</h2>
|
||||
<p className="description">
|
||||
<intl.FormattedMessage id="teacherRegistration.useScratchStepDescription" />
|
||||
<FormattedMessage id="teacherRegistration.useScratchStepDescription" />
|
||||
<Tooltip
|
||||
tipContent={
|
||||
this.props.intl.formatMessage({id: 'registration.nameStepTooltip'})
|
||||
|
@ -1098,7 +1097,7 @@ class UseScratchStep extends React.Component {
|
|||
maxCharacters={this.props.maxCharacters}
|
||||
/>
|
||||
<NextStepButton
|
||||
text={<intl.FormattedMessage id="registration.nextStep" />}
|
||||
text={<FormattedMessage id="registration.nextStep" />}
|
||||
waiting={this.props.waiting}
|
||||
/>
|
||||
</Form>
|
||||
|
@ -1192,10 +1191,10 @@ class EmailStep extends React.Component {
|
|||
return (
|
||||
<Slide className="registration-step email-step">
|
||||
<h2>
|
||||
<intl.FormattedMessage id="teacherRegistration.emailStepTitle" />
|
||||
<FormattedMessage id="teacherRegistration.emailStepTitle" />
|
||||
</h2>
|
||||
<p className="description">
|
||||
<intl.FormattedMessage id="teacherRegistration.emailStepDescription" />
|
||||
<FormattedMessage id="teacherRegistration.emailStepDescription" />
|
||||
<Tooltip
|
||||
tipContent={
|
||||
this.props.intl.formatMessage({id: 'registration.nameStepTooltip'})
|
||||
|
@ -1237,7 +1236,7 @@ class EmailStep extends React.Component {
|
|||
/>
|
||||
<GeneralError name="all" />
|
||||
<NextStepButton
|
||||
text={<intl.FormattedMessage id="registration.nextStep" />}
|
||||
text={<FormattedMessage id="registration.nextStep" />}
|
||||
waiting={this.props.waiting}
|
||||
/>
|
||||
</Form>
|
||||
|
@ -1272,6 +1271,7 @@ EmailStep.defaultProps = {
|
|||
|
||||
const IntlEmailStep = injectIntl(EmailStep);
|
||||
|
||||
const EducatorResourcesLink = chunks => <a href="/educators#resources">{chunks}</a>;
|
||||
|
||||
/*
|
||||
* TEACHER APPROVAL STEP
|
||||
|
@ -1279,17 +1279,17 @@ const IntlEmailStep = injectIntl(EmailStep);
|
|||
const TeacherApprovalStep = props => (
|
||||
<Slide className="registration-step last-step">
|
||||
<h2>
|
||||
<intl.FormattedMessage id="registration.lastStepTitle" />
|
||||
<FormattedMessage id="registration.lastStepTitle" />
|
||||
</h2>
|
||||
<p className="description">
|
||||
<intl.FormattedMessage id="registration.lastStepDescription" />
|
||||
<FormattedMessage id="registration.lastStepDescription" />
|
||||
</p>
|
||||
{props.confirmed || !props.email ?
|
||||
[] : (
|
||||
<Card className="confirm">
|
||||
<h4><intl.FormattedMessage id="registration.confirmYourEmail" /></h4>
|
||||
<h4><FormattedMessage id="registration.confirmYourEmail" /></h4>
|
||||
<p>
|
||||
<intl.FormattedMessage id="registration.confirmYourEmailDescription" /><br />
|
||||
<FormattedMessage id="registration.confirmYourEmailDescription" /><br />
|
||||
<strong>{props.email}</strong>
|
||||
</p>
|
||||
</Card>
|
||||
|
@ -1297,16 +1297,19 @@ const TeacherApprovalStep = props => (
|
|||
}
|
||||
{props.invited ?
|
||||
<Card className="wait">
|
||||
<h4><intl.FormattedMessage id="registration.waitForApproval" /></h4>
|
||||
<h4><FormattedMessage id="registration.waitForApproval" /></h4>
|
||||
<p>
|
||||
<intl.FormattedMessage id="registration.waitForApprovalDescription" />
|
||||
<FormattedMessage id="registration.waitForApprovalDescription" />
|
||||
</p>
|
||||
</Card> : []
|
||||
}
|
||||
<Card className="resources">
|
||||
<h4><intl.FormattedMessage id="registration.checkOutResources" /></h4>
|
||||
<h4><FormattedMessage id="registration.checkOutResources" /></h4>
|
||||
<p>
|
||||
<intl.FormattedHTMLMessage id="registration.checkOutResourcesDescription" />
|
||||
<FormattedMessage
|
||||
id="registration.checkOutResourcesDescriptionHTML"
|
||||
values={{a: EducatorResourcesLink}}
|
||||
/>
|
||||
</p>
|
||||
</Card>
|
||||
</Slide>
|
||||
|
|
39
src/components/relative-time/relative-time.jsx
Normal file
39
src/components/relative-time/relative-time.jsx
Normal file
|
@ -0,0 +1,39 @@
|
|||
const React = require('react');
|
||||
const {useEffect, useState} = React;
|
||||
const PropTypes = require('prop-types');
|
||||
const {FormattedRelativeTime} = require('react-intl');
|
||||
const {selectUnit} = require('../../lib/select-unit');
|
||||
|
||||
const RelativeTime = ({value}) => {
|
||||
const [selectedUnit, setSelectedUnit] = useState(selectUnit(value));
|
||||
|
||||
useEffect(() => {
|
||||
// It is unlikely that users will leave this running for days. Don't
|
||||
// auto-update beyond hours.
|
||||
if (!['second', 'minute', 'hour'].includes(selectedUnit.unit)) return;
|
||||
|
||||
const timerId = setInterval(() => {
|
||||
const nextSelectedUnit = selectUnit(value);
|
||||
if (selectedUnit.value !== nextSelectedUnit.value ||
|
||||
selectUnit.unit !== nextSelectedUnit.unit) {
|
||||
setSelectedUnit(nextSelectedUnit);
|
||||
}
|
||||
}, 10000);
|
||||
|
||||
return () => clearTimeout(timerId);
|
||||
}, [value, selectedUnit]);
|
||||
|
||||
return (
|
||||
<FormattedRelativeTime
|
||||
value={selectedUnit.value}
|
||||
unit={selectedUnit.unit}
|
||||
numeric="auto"
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
RelativeTime.propTypes = {
|
||||
value: PropTypes.instanceOf(Date)
|
||||
};
|
||||
|
||||
module.exports = RelativeTime;
|
|
@ -1,9 +1,9 @@
|
|||
const classNames = require('classnames');
|
||||
const FormattedRelative = require('react-intl').FormattedRelative;
|
||||
const PropTypes = require('prop-types');
|
||||
const React = require('react');
|
||||
|
||||
const FlexRow = require('../flex-row/flex-row.jsx');
|
||||
const RelativeTime = require('../relative-time/relative-time.jsx');
|
||||
|
||||
require('./social-message.scss');
|
||||
|
||||
|
@ -24,7 +24,7 @@ const SocialMessage = props => (
|
|||
</div>
|
||||
</div>
|
||||
<span className="social-message-date">
|
||||
<FormattedRelative value={new Date(props.datetime)} />
|
||||
<RelativeTime value={new Date(props.datetime)} />
|
||||
</span>
|
||||
</FlexRow>
|
||||
</props.as>
|
||||
|
|
|
@ -41,7 +41,7 @@ a.social-messages-profile-link {
|
|||
color: $type-gray;
|
||||
|
||||
&:hover {
|
||||
color: $link-blue;
|
||||
color: $link-purple;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
.step-number {
|
||||
display: inline-flex;
|
||||
border-radius: 2rem;
|
||||
background-color: $ui-blue;
|
||||
background-color: $ui-purple-dark;
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
color: $ui-white;
|
||||
|
|
|
@ -20,6 +20,7 @@ const SubNavigation = props => (
|
|||
'sub-nav-align-right': props.align === 'right'
|
||||
}
|
||||
)}
|
||||
role={props.role}
|
||||
>
|
||||
{props.children}
|
||||
</div>
|
||||
|
@ -27,6 +28,7 @@ const SubNavigation = props => (
|
|||
|
||||
SubNavigation.propTypes = {
|
||||
align: PropTypes.string,
|
||||
role: PropTypes.string,
|
||||
children: PropTypes.node,
|
||||
className: PropTypes.string
|
||||
};
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
const classNames = require('classnames');
|
||||
const PropTypes = require('prop-types');
|
||||
const {useRef} = require('react');
|
||||
const React = require('react');
|
||||
|
||||
const SubNavigation = require('../../components/subnavigation/subnavigation.jsx');
|
||||
|
@ -10,17 +10,94 @@ require('./tabs.scss');
|
|||
* Container for a custom, horizontal list of navigation elements
|
||||
* that can be displayed within a view or component.
|
||||
*/
|
||||
const Tabs = props => (
|
||||
<div className="tab-background">
|
||||
<SubNavigation className={classNames('tabs', props.className)}>
|
||||
{props.children}
|
||||
</SubNavigation>
|
||||
</div>
|
||||
);
|
||||
const Tabs = ({items, activeTabName}) => {
|
||||
const tabElementRefs = useRef({});
|
||||
|
||||
const itemsRendered = items.map(({name, onTrigger, getContent}) => {
|
||||
const isActive = name === activeTabName;
|
||||
|
||||
let tabRef;
|
||||
if (tabElementRefs.current[name]) {
|
||||
tabRef = tabElementRefs.current[name];
|
||||
} else {
|
||||
tabRef = React.createRef();
|
||||
tabElementRefs.current[name] = tabRef;
|
||||
}
|
||||
|
||||
return (
|
||||
<button
|
||||
role="tab"
|
||||
aria-selected={`${isActive ? 'true' : 'false'}`}
|
||||
className={`${isActive ? 'active' : ''}`}
|
||||
onClick={onTrigger}
|
||||
tabIndex={isActive ? 0 : -1}
|
||||
key={name}
|
||||
ref={tabRef}
|
||||
>
|
||||
{getContent(isActive)}
|
||||
</button>
|
||||
);
|
||||
});
|
||||
|
||||
const handleKeyDown = event => {
|
||||
if (!['ArrowLeft', 'ArrowRight', 'Home', 'End', 'Enter', ' '].includes(event.key)) {
|
||||
return;
|
||||
}
|
||||
event.preventDefault();
|
||||
const focusedIndex = Object.values(tabElementRefs.current)
|
||||
.findIndex(tabElementRef =>
|
||||
document.activeElement === tabElementRef.current
|
||||
);
|
||||
if (event.key === 'ArrowLeft') {
|
||||
let nextIndex;
|
||||
if (focusedIndex === 0) {
|
||||
nextIndex = Object.values(tabElementRefs.current).length - 1;
|
||||
} else {
|
||||
nextIndex = focusedIndex - 1;
|
||||
}
|
||||
Object.values(tabElementRefs.current)[nextIndex].current.focus();
|
||||
} else if (event.key === 'ArrowRight') {
|
||||
let nextIndex;
|
||||
if (focusedIndex === Object.values(tabElementRefs.current).length - 1) {
|
||||
nextIndex = 0;
|
||||
} else {
|
||||
nextIndex = focusedIndex + 1;
|
||||
}
|
||||
Object.values(tabElementRefs.current)[nextIndex].current.focus();
|
||||
} else if (event.key === 'Home') {
|
||||
Object.values(tabElementRefs.current)[0].current.focus();
|
||||
} else if (event.key === 'End') {
|
||||
const lastTab = Object.values(tabElementRefs.current).length - 1;
|
||||
Object.values(tabElementRefs.current)[lastTab].current.focus();
|
||||
} else if (event.key === 'Enter' || event.key === ' ') {
|
||||
items[focusedIndex].onTrigger();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className="tab-background"
|
||||
onKeyDown={handleKeyDown}// eslint-disable-line
|
||||
>
|
||||
<SubNavigation
|
||||
role="tablist"
|
||||
className="tabs"
|
||||
>
|
||||
{itemsRendered}
|
||||
</SubNavigation>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
Tabs.propTypes = {
|
||||
children: PropTypes.node,
|
||||
className: PropTypes.string
|
||||
items: PropTypes.arrayOf(
|
||||
PropTypes.shape({
|
||||
name: PropTypes.string.isRequired,
|
||||
onTrigger: PropTypes.func.isRequired,
|
||||
getContent: PropTypes.func.isRequired
|
||||
})
|
||||
).isRequired,
|
||||
activeTabName: PropTypes.string.isRequired
|
||||
};
|
||||
|
||||
module.exports = Tabs;
|
||||
|
|
|
@ -14,13 +14,14 @@
|
|||
justify-content: center;
|
||||
}
|
||||
|
||||
.tabs li {
|
||||
.tabs button {
|
||||
margin: 0;
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
width: $cols2;
|
||||
text-align: center;
|
||||
color: $header-gray;
|
||||
background-color: transparent;
|
||||
|
||||
&.active {
|
||||
border-bottom: 3px solid $ui-aqua;
|
||||
|
|
|
@ -33,6 +33,6 @@
|
|||
margin-left: 10px;
|
||||
background-color: $ui-white;
|
||||
padding: 13px 20px;
|
||||
color: $ui-blue;
|
||||
color: $ui-purple-dark;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
margin-top: $arrow-border-width * 0.5;
|
||||
border: 1px solid $active-gray;
|
||||
border-radius: 5px;
|
||||
background-color: $ui-blue;
|
||||
background-color: $ui-blue-dark;
|
||||
padding: 1rem;
|
||||
width: 13.75rem;
|
||||
text-align: left;
|
||||
|
@ -43,7 +43,7 @@
|
|||
border-left: 1px solid $active-gray;
|
||||
border-radius: 5px;
|
||||
|
||||
background-color: $ui-blue;
|
||||
background-color: $ui-blue-dark;
|
||||
width: $arrow-border-width;
|
||||
height: $arrow-border-width;
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ const TTTTile = props => (
|
|||
<div className="ttt-tile-tutorial">
|
||||
<div className="ttt-tile-image">
|
||||
<img
|
||||
alt=""
|
||||
alt={props.thumbImageDescription}
|
||||
className="ttt-tile-image-img"
|
||||
src={props.thumbImage}
|
||||
/>
|
||||
|
@ -33,6 +33,7 @@ TTTTile.propTypes = {
|
|||
description: PropTypes.string,
|
||||
onClick: PropTypes.func,
|
||||
thumbImage: PropTypes.string.isRequired,
|
||||
thumbImageDescription: PropTypes.string,
|
||||
title: PropTypes.string.isRequired
|
||||
};
|
||||
|
||||
|
|
|
@ -82,22 +82,6 @@
|
|||
font-size: .875rem;
|
||||
}
|
||||
|
||||
.ttt-tile-guides {
|
||||
margin: auto;
|
||||
border-top: 1px dashed $ui-border;
|
||||
border-radius: 0 0 1rem 1rem;
|
||||
cursor: pointer;
|
||||
padding: 1.25rem 0;
|
||||
color: $link-blue;
|
||||
|
||||
font-size: .75rem;
|
||||
font-weight: 500;
|
||||
|
||||
&:hover {
|
||||
background-color: $ui-blue-10percent;
|
||||
}
|
||||
}
|
||||
|
||||
.ttt-tile-open-modal {
|
||||
display: inline-block;
|
||||
padding: 0 .25rem;
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
import 'regenerator-runtime/runtime'; // Needed for async/await
|
||||
const jar = require('./lib/jar');
|
||||
import intlPolyfill from './lib/intl-polyfill';
|
||||
|
||||
/**
|
||||
* -----------------------------------------------------------------------------
|
||||
* L10N
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
(async () => {
|
||||
(() => {
|
||||
/*
|
||||
* Bind locale code from cookie if available. Uses navigator language API as a fallback.
|
||||
*
|
||||
|
@ -37,7 +36,6 @@ import intlPolyfill from './lib/intl-polyfill';
|
|||
|
||||
window._locale = updateLocale();
|
||||
document.documentElement.lang = window._locale;
|
||||
await intlPolyfill(window._locale);
|
||||
})();
|
||||
|
||||
/**
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
"general.contactUs": "Contact Us",
|
||||
"general.getHelp": "Get Help",
|
||||
"general.contact": "Contact",
|
||||
"general.cookies": "Cookies",
|
||||
"general.done": "Done",
|
||||
"general.downloadPDF": "Download PDF",
|
||||
"general.emailUs": "Email Us",
|
||||
|
@ -74,6 +75,8 @@
|
|||
"general.download": "Download",
|
||||
"general.password": "Password",
|
||||
"general.press": "Press",
|
||||
"general.projectsSelected": "Projects Tab Selected",
|
||||
"general.projectsNotS": "Projects",
|
||||
"general.privacyPolicy": "Privacy Policy",
|
||||
"general.projects": "Projects",
|
||||
"general.profile": "Profile",
|
||||
|
@ -91,6 +94,8 @@
|
|||
"general.startOver": "Start over",
|
||||
"general.statistics": "Statistics",
|
||||
"general.studios": "Studios",
|
||||
"general.studiosSelected": "Studios Tab Selected",
|
||||
"general.studiosNotS": "Studios",
|
||||
"general.support": "Resources",
|
||||
"general.ideas": "Ideas",
|
||||
"general.tipsWindow": "Tips Window",
|
||||
|
@ -111,13 +116,21 @@
|
|||
"general.seeAllComments": "See all comments",
|
||||
|
||||
"general.all": "All",
|
||||
"general.allSelected": "All Selected",
|
||||
"general.animations": "Animations",
|
||||
"general.animationsSelected": "Animations Selected",
|
||||
"general.art": "Art",
|
||||
"general.artSelected": "Art Selected",
|
||||
"general.games": "Games",
|
||||
"general.gamesSelected": "Games Selected",
|
||||
"general.music": "Music",
|
||||
"general.musicSelected": "Music Selected",
|
||||
"general.results": "Results",
|
||||
"general.resultsSelected": "Results Selected",
|
||||
"general.stories": "Stories",
|
||||
"general.storiesSelected": "Stories Selected",
|
||||
"general.tutorials": "Tutorials",
|
||||
"general.tutorialsSelected": "Tutorials Selected",
|
||||
|
||||
"general.teacherAccounts": "Teacher Accounts",
|
||||
|
||||
|
@ -166,6 +179,7 @@
|
|||
"registration.cantCreateAccount": "Scratch could not create your account.",
|
||||
"registration.checkOutResources": "Get Started with Resources",
|
||||
"registration.checkOutResourcesDescription": "Explore materials for educators and facilitators written by the Scratch Team, including <a href='/educators#resources'>tips, tutorials, and guides</a>.",
|
||||
"registration.checkOutResourcesDescriptionHTML": "Explore materials for educators and facilitators written by the Scratch Team, including <a>tips, tutorials, and guides</a>.",
|
||||
"registration.choosePasswordStepDescription": "Type in a new password for your account. You will use this password the next time you log into Scratch.",
|
||||
"registration.choosePasswordStepTitle": "Create a password",
|
||||
"registration.choosePasswordStepTooltip": "Don't use your name or anything that's easy for someone else to guess.",
|
||||
|
@ -439,7 +453,8 @@
|
|||
|
||||
"bluetooth.enableLocationServicesTitle": "Make sure you have location services enabled on Chromebooks or Android tablets",
|
||||
"bluetooth.enableLocationServicesText": "Bluetooth can be used to provide location data to the app. In addition to granting the Scratch App permission to access location, location must be enabled in your general device settings. Search for 'Location' in your settings, and make sure it is on. On Chromebooks search for 'Location' in the Google Play Store Android preferences.",
|
||||
|
||||
"privacyBanner.update": "The Scratch privacy policy has been updated, effective May 25, 2023. You can see the new policy <a>here</a>.",
|
||||
|
||||
"renameAccount.accountBlocked": "Account Blocked",
|
||||
"renameAccount.toRecover": "To recover a access to your account, change your username.",
|
||||
"renameAccount.yourScratchAccount": "Your scratch account has been temporarily blocked because your username appears to contain personal information.",
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
// this file should only be `required` in the format-time
|
||||
// when Intl.RelativeTimeFormat is not available (Safari < 14), but
|
||||
// we're not currently able to do the code splitting in www, and it
|
||||
// is always included. To reduce the amount of data that's loaded limit
|
||||
// the number of languages loaded to just the top few that are still using
|
||||
// safari <14. These seven account for most uses.
|
||||
// This file polyfills the required Intl objects and locale data.
|
||||
// The react-intl library uses PluralRules, RelativeTimeFormat, NumberFormat,
|
||||
// and DateTimeFormat. Even if browsers support these objects, it is possible
|
||||
// that the browser does not support the specific locale.
|
||||
// There are a small number of Scratch locales that do not have polyfill locale
|
||||
// data available. See /src/lib/locales.js for how they are handled.
|
||||
// relativetimeformat depends on locale which also needs to be polyfilled in
|
||||
// safari <14
|
||||
// The plural rules is required for safari 12.
|
||||
|
@ -11,6 +11,8 @@ import 'regenerator-runtime/runtime'; // Needed for async/await
|
|||
import {shouldPolyfill as shouldPolyfillLocale} from '@formatjs/intl-locale/should-polyfill';
|
||||
import {shouldPolyfill as shouldPolyfillRelativeTimeFormat} from '@formatjs/intl-relativetimeformat/should-polyfill';
|
||||
import {shouldPolyfill as shouldPolyfillPluralRules} from '@formatjs/intl-pluralrules/should-polyfill';
|
||||
import {shouldPolyfill as shouldPolyfillNumberFormat} from '@formatjs/intl-numberformat/should-polyfill';
|
||||
import {shouldPolyfill as shouldPolyfillDateTimeFormat} from '@formatjs/intl-datetimeformat/should-polyfill';
|
||||
/**
|
||||
* polyfill all the parts needed from intl
|
||||
* @param {string} locale currently selected locale
|
||||
|
@ -19,293 +21,458 @@ import {shouldPolyfill as shouldPolyfillPluralRules} from '@formatjs/intl-plural
|
|||
const intlPolyfill = async function (locale) {
|
||||
if (!(shouldPolyfillLocale() ||
|
||||
shouldPolyfillPluralRules(locale) ||
|
||||
shouldPolyfillRelativeTimeFormat(locale))) {
|
||||
shouldPolyfillRelativeTimeFormat(locale) ||
|
||||
shouldPolyfillNumberFormat(locale) ||
|
||||
shouldPolyfillDateTimeFormat(locale))) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (shouldPolyfillRelativeTimeFormat(locale)) {
|
||||
await import('@formatjs/intl-relativetimeformat/polyfill');
|
||||
if (shouldPolyfillLocale()) {
|
||||
await import('@formatjs/intl-locale/polyfill-force');
|
||||
}
|
||||
|
||||
if (shouldPolyfillPluralRules(locale)) {
|
||||
await import('@formatjs/intl-pluralrules/polyfill');
|
||||
await import('@formatjs/intl-pluralrules/polyfill-force');
|
||||
}
|
||||
|
||||
if (shouldPolyfillLocale(locale)) {
|
||||
await import('@formatjs/intl-locale/polyfill');
|
||||
if (shouldPolyfillRelativeTimeFormat(locale)) {
|
||||
await import('@formatjs/intl-relativetimeformat/polyfill-force');
|
||||
}
|
||||
|
||||
if (shouldPolyfillNumberFormat(locale)) {
|
||||
await import('@formatjs/intl-numberformat/polyfill-force');
|
||||
}
|
||||
|
||||
if (shouldPolyfillDateTimeFormat(locale)) {
|
||||
await import('@formatjs/intl-datetimeformat/polyfill-force');
|
||||
}
|
||||
|
||||
switch (locale.toLowerCase().split('-')[0]) {
|
||||
case 'af':
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/af');
|
||||
await import('@formatjs/intl-pluralrules/locale-data/af');
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/af');
|
||||
await import('@formatjs/intl-numberformat/locale-data/af');
|
||||
await import('@formatjs/intl-datetimeformat/locale-data/af');
|
||||
break;
|
||||
case 'ar':
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/ar');
|
||||
await import('@formatjs/intl-pluralrules/locale-data/ar');
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/ar');
|
||||
await import('@formatjs/intl-numberformat/locale-data/ar');
|
||||
await import('@formatjs/intl-datetimeformat/locale-data/ar');
|
||||
break;
|
||||
case 'am':
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/am');
|
||||
await import('@formatjs/intl-pluralrules/locale-data/am');
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/am');
|
||||
await import('@formatjs/intl-numberformat/locale-data/am');
|
||||
await import('@formatjs/intl-datetimeformat/locale-data/am');
|
||||
break;
|
||||
case 'an':
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/en');
|
||||
await import('@formatjs/intl-pluralrules/locale-data/an');
|
||||
case 'ast':
|
||||
await import('@formatjs/intl-pluralrules/locale-data/ast');
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/ast');
|
||||
await import('@formatjs/intl-numberformat/locale-data/ast');
|
||||
await import('@formatjs/intl-datetimeformat/locale-data/ast');
|
||||
break;
|
||||
case 'az':
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/az');
|
||||
await import('@formatjs/intl-pluralrules/locale-data/az');
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/az');
|
||||
await import('@formatjs/intl-numberformat/locale-data/az');
|
||||
await import('@formatjs/intl-datetimeformat/locale-data/az');
|
||||
break;
|
||||
case 'id':
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/id');
|
||||
await import('@formatjs/intl-pluralrules/locale-data/id');
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/id');
|
||||
await import('@formatjs/intl-numberformat/locale-data/id');
|
||||
await import('@formatjs/intl-datetimeformat/locale-data/id');
|
||||
break;
|
||||
case 'bn':
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/bn');
|
||||
await import('@formatjs/intl-pluralrules/locale-data/bn');
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/bn');
|
||||
await import('@formatjs/intl-numberformat/locale-data/bn');
|
||||
await import('@formatjs/intl-datetimeformat/locale-data/bn');
|
||||
break;
|
||||
case 'be':
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/be');
|
||||
await import('@formatjs/intl-pluralrules/locale-data/be');
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/be');
|
||||
await import('@formatjs/intl-numberformat/locale-data/be');
|
||||
await import('@formatjs/intl-datetimeformat/locale-data/be');
|
||||
break;
|
||||
case 'bg':
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/bg');
|
||||
await import('@formatjs/intl-pluralrules/locale-data/bg');
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/bg');
|
||||
await import('@formatjs/intl-numberformat/locale-data/bg');
|
||||
await import('@formatjs/intl-datetimeformat/locale-data/bg');
|
||||
break;
|
||||
case 'ca':
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/ca');
|
||||
await import('@formatjs/intl-pluralrules/locale-data/ca');
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/ca');
|
||||
await import('@formatjs/intl-numberformat/locale-data/ca');
|
||||
await import('@formatjs/intl-datetimeformat/locale-data/ca');
|
||||
break;
|
||||
case 'cs':
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/cs');
|
||||
await import('@formatjs/intl-pluralrules/locale-data/cs');
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/cs');
|
||||
await import('@formatjs/intl-numberformat/locale-data/cs');
|
||||
await import('@formatjs/intl-datetimeformat/locale-data/cs');
|
||||
break;
|
||||
case 'cy':
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/cy');
|
||||
await import('@formatjs/intl-pluralrules/locale-data/cy');
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/cy');
|
||||
await import('@formatjs/intl-numberformat/locale-data/cy');
|
||||
await import('@formatjs/intl-datetimeformat/locale-data/cy');
|
||||
break;
|
||||
case 'da':
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/da');
|
||||
await import('@formatjs/intl-pluralrules/locale-data/da');
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/da');
|
||||
await import('@formatjs/intl-numberformat/locale-data/da');
|
||||
await import('@formatjs/intl-datetimeformat/locale-data/da');
|
||||
break;
|
||||
case 'de':
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/de');
|
||||
await import('@formatjs/intl-pluralrules/locale-data/de');
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/de');
|
||||
await import('@formatjs/intl-numberformat/locale-data/de');
|
||||
await import('@formatjs/intl-datetimeformat/locale-data/de');
|
||||
break;
|
||||
case 'et':
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/et');
|
||||
await import('@formatjs/intl-pluralrules/locale-data/et');
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/et');
|
||||
await import('@formatjs/intl-numberformat/locale-data/et');
|
||||
await import('@formatjs/intl-datetimeformat/locale-data/et');
|
||||
break;
|
||||
case 'el':
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/el');
|
||||
await import('@formatjs/intl-pluralrules/locale-data/el');
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/el');
|
||||
await import('@formatjs/intl-numberformat/locale-data/el');
|
||||
await import('@formatjs/intl-datetimeformat/locale-data/el');
|
||||
break;
|
||||
case 'en':
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/en');
|
||||
await import('@formatjs/intl-pluralrules/locale-data/en');
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/en');
|
||||
await import('@formatjs/intl-numberformat/locale-data/en');
|
||||
await import('@formatjs/intl-datetimeformat/locale-data/en');
|
||||
break;
|
||||
case 'es':
|
||||
case 'rap':
|
||||
case 'qu':
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/es');
|
||||
await import('@formatjs/intl-pluralrules/locale-data/es');
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/es');
|
||||
await import('@formatjs/intl-numberformat/locale-data/es');
|
||||
await import('@formatjs/intl-datetimeformat/locale-data/es');
|
||||
break;
|
||||
case 'eo':
|
||||
await import('@formatjs/intl-pluralrules/locale-data/eo');
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/eo');
|
||||
await import('@formatjs/intl-numberformat/locale-data/eo');
|
||||
await import('@formatjs/intl-datetimeformat/locale-data/eo');
|
||||
break;
|
||||
case 'eu':
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/eu');
|
||||
await import('@formatjs/intl-pluralrules/locale-data/eu');
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/eu');
|
||||
await import('@formatjs/intl-numberformat/locale-data/eu');
|
||||
await import('@formatjs/intl-datetimeformat/locale-data/eu');
|
||||
break;
|
||||
case 'fa':
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/fa');
|
||||
await import('@formatjs/intl-pluralrules/locale-data/fa');
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/fa');
|
||||
await import('@formatjs/intl-numberformat/locale-data/fa');
|
||||
await import('@formatjs/intl-datetimeformat/locale-data/fa');
|
||||
break;
|
||||
case 'fil':
|
||||
await import('@formatjs/intl-pluralrules/locale-data/fil');
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/fil');
|
||||
await import('@formatjs/intl-numberformat/locale-data/fil');
|
||||
await import('@formatjs/intl-datetimeformat/locale-data/fil');
|
||||
break;
|
||||
case 'fr':
|
||||
case 'ht':
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/fr');
|
||||
await import('@formatjs/intl-pluralrules/locale-data/fr');
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/fr');
|
||||
await import('@formatjs/intl-numberformat/locale-data/fr');
|
||||
await import('@formatjs/intl-datetimeformat/locale-data/fr');
|
||||
break;
|
||||
case 'fy':
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/fy');
|
||||
await import('@formatjs/intl-pluralrules/locale-data/fy');
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/fy');
|
||||
await import('@formatjs/intl-numberformat/locale-data/fy');
|
||||
await import('@formatjs/intl-datetimeformat/locale-data/fy');
|
||||
break;
|
||||
case 'ga':
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/ga');
|
||||
await import('@formatjs/intl-pluralrules/locale-data/ga');
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/ga');
|
||||
await import('@formatjs/intl-numberformat/locale-data/ga');
|
||||
await import('@formatjs/intl-datetimeformat/locale-data/ga');
|
||||
break;
|
||||
case 'gd':
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/gd');
|
||||
await import('@formatjs/intl-pluralrules/locale-data/gd');
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/gd');
|
||||
await import('@formatjs/intl-numberformat/locale-data/gd');
|
||||
await import('@formatjs/intl-datetimeformat/locale-data/gd');
|
||||
break;
|
||||
case 'gl':
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/gl');
|
||||
await import('@formatjs/intl-pluralrules/locale-data/gl');
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/gl');
|
||||
await import('@formatjs/intl-numberformat/locale-data/gl');
|
||||
await import('@formatjs/intl-datetimeformat/locale-data/gl');
|
||||
break;
|
||||
case 'ko':
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/ko');
|
||||
await import('@formatjs/intl-pluralrules/locale-data/ko');
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/ko');
|
||||
await import('@formatjs/intl-numberformat/locale-data/ko');
|
||||
await import('@formatjs/intl-datetimeformat/locale-data/ko');
|
||||
break;
|
||||
case 'ha':
|
||||
await import('@formatjs/intl-pluralrules/locale-data/ha');
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/ha');
|
||||
await import('@formatjs/intl-numberformat/locale-data/ha');
|
||||
await import('@formatjs/intl-datetimeformat/locale-data/ha');
|
||||
break;
|
||||
case 'hy':
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/hy');
|
||||
await import('@formatjs/intl-pluralrules/locale-data/hy');
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/hy');
|
||||
await import('@formatjs/intl-numberformat/locale-data/hy');
|
||||
await import('@formatjs/intl-datetimeformat/locale-data/hy');
|
||||
break;
|
||||
case 'he':
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/he');
|
||||
await import('@formatjs/intl-pluralrules/locale-data/he');
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/he');
|
||||
await import('@formatjs/intl-numberformat/locale-data/he');
|
||||
await import('@formatjs/intl-datetimeformat/locale-data/he');
|
||||
break;
|
||||
case 'hr':
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/hr');
|
||||
await import('@formatjs/intl-pluralrules/locale-data/hr');
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/hr');
|
||||
await import('@formatjs/intl-numberformat/locale-data/hr');
|
||||
await import('@formatjs/intl-datetimeformat/locale-data/hr');
|
||||
break;
|
||||
case 'xh':
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/xh');
|
||||
await import('@formatjs/intl-pluralrules/locale-data/xh');
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/xh');
|
||||
await import('@formatjs/intl-numberformat/locale-data/xh');
|
||||
await import('@formatjs/intl-datetimeformat/locale-data/xh');
|
||||
break;
|
||||
case 'zu':
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/zu');
|
||||
await import('@formatjs/intl-pluralrules/locale-data/zu');
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/zu');
|
||||
await import('@formatjs/intl-numberformat/locale-data/zu');
|
||||
await import('@formatjs/intl-datetimeformat/locale-data/zu');
|
||||
break;
|
||||
case 'is':
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/is');
|
||||
await import('@formatjs/intl-pluralrules/locale-data/is');
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/is');
|
||||
await import('@formatjs/intl-numberformat/locale-data/is');
|
||||
await import('@formatjs/intl-datetimeformat/locale-data/is');
|
||||
break;
|
||||
case 'it':
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/it');
|
||||
await import('@formatjs/intl-pluralrules/locale-data/it');
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/it');
|
||||
await import('@formatjs/intl-numberformat/locale-data/it');
|
||||
await import('@formatjs/intl-datetimeformat/locale-data/it');
|
||||
break;
|
||||
case 'ka':
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/ka');
|
||||
await import('@formatjs/intl-pluralrules/locale-data/ka');
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/ka');
|
||||
await import('@formatjs/intl-numberformat/locale-data/ka');
|
||||
await import('@formatjs/intl-datetimeformat/locale-data/ka');
|
||||
break;
|
||||
case 'kk':
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/kk');
|
||||
await import('@formatjs/intl-pluralrules/locale-data/kk');
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/kk');
|
||||
await import('@formatjs/intl-numberformat/locale-data/kk');
|
||||
await import('@formatjs/intl-datetimeformat/locale-data/kk');
|
||||
break;
|
||||
case 'qu':
|
||||
await import('@formatjs/intl-pluralrules/locale-data/en');
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/qu');
|
||||
await import('@formatjs/intl-numberformat/locale-data/qu');
|
||||
await import('@formatjs/intl-datetimeformat/locale-data/qu');
|
||||
break;
|
||||
case 'sw':
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/sw');
|
||||
await import('@formatjs/intl-pluralrules/locale-data/sw');
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/sw');
|
||||
await import('@formatjs/intl-numberformat/locale-data/sw');
|
||||
await import('@formatjs/intl-datetimeformat/locale-data/sw');
|
||||
break;
|
||||
case 'ku':
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/ku');
|
||||
await import('@formatjs/intl-pluralrules/locale-data/ku');
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/ku');
|
||||
await import('@formatjs/intl-numberformat/locale-data/ku');
|
||||
await import('@formatjs/intl-datetimeformat/locale-data/ku');
|
||||
break;
|
||||
case 'ckb':
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/ckb');
|
||||
await import('@formatjs/intl-pluralrules/locale-data/ckb');
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/ckb');
|
||||
await import('@formatjs/intl-numberformat/locale-data/ckb');
|
||||
await import('@formatjs/intl-datetimeformat/locale-data/ckb');
|
||||
break;
|
||||
case 'lv':
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/lv');
|
||||
await import('@formatjs/intl-pluralrules/locale-data/lv');
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/lv');
|
||||
await import('@formatjs/intl-numberformat/locale-data/lv');
|
||||
await import('@formatjs/intl-datetimeformat/locale-data/lv');
|
||||
break;
|
||||
case 'lt':
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/lt');
|
||||
await import('@formatjs/intl-pluralrules/locale-data/lt');
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/lt');
|
||||
await import('@formatjs/intl-numberformat/locale-data/lt');
|
||||
await import('@formatjs/intl-datetimeformat/locale-data/lt');
|
||||
break;
|
||||
case 'hu':
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/hu');
|
||||
await import('@formatjs/intl-pluralrules/locale-data/hu');
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/hu');
|
||||
await import('@formatjs/intl-numberformat/locale-data/hu');
|
||||
await import('@formatjs/intl-datetimeformat/locale-data/hu');
|
||||
break;
|
||||
case 'mi':
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/mi');
|
||||
await import('@formatjs/intl-pluralrules/locale-data/en');
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/mi');
|
||||
await import('@formatjs/intl-numberformat/locale-data/mi');
|
||||
await import('@formatjs/intl-datetimeformat/locale-data/mi');
|
||||
break;
|
||||
case 'mn':
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/mn');
|
||||
await import('@formatjs/intl-pluralrules/locale-data/mn');
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/mn');
|
||||
await import('@formatjs/intl-numberformat/locale-data/mn');
|
||||
await import('@formatjs/intl-datetimeformat/locale-data/mn');
|
||||
break;
|
||||
case 'nl':
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/nl');
|
||||
await import('@formatjs/intl-pluralrules/locale-data/nl');
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/nl');
|
||||
await import('@formatjs/intl-numberformat/locale-data/nl');
|
||||
await import('@formatjs/intl-datetimeformat/locale-data/nl');
|
||||
break;
|
||||
case 'ja':
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/ja');
|
||||
await import('@formatjs/intl-pluralrules/locale-data/ja');
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/ja');
|
||||
await import('@formatjs/intl-numberformat/locale-data/ja');
|
||||
await import('@formatjs/intl-datetimeformat/locale-data/ja');
|
||||
break;
|
||||
case 'nb':
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/nb');
|
||||
await import('@formatjs/intl-pluralrules/locale-data/nb');
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/nb');
|
||||
await import('@formatjs/intl-numberformat/locale-data/nb');
|
||||
await import('@formatjs/intl-datetimeformat/locale-data/nb');
|
||||
break;
|
||||
case 'nn':
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/nn');
|
||||
await import('@formatjs/intl-pluralrules/locale-data/nn');
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/nn');
|
||||
await import('@formatjs/intl-numberformat/locale-data/nn');
|
||||
await import('@formatjs/intl-datetimeformat/locale-data/nn');
|
||||
break;
|
||||
case 'or':
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/or');
|
||||
await import('@formatjs/intl-pluralrules/locale-data/or');
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/or');
|
||||
await import('@formatjs/intl-numberformat/locale-data/or');
|
||||
await import('@formatjs/intl-datetimeformat/locale-data/or');
|
||||
break;
|
||||
case 'uz':
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/uz');
|
||||
await import('@formatjs/intl-pluralrules/locale-data/uz');
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/uz');
|
||||
await import('@formatjs/intl-numberformat/locale-data/uz');
|
||||
await import('@formatjs/intl-datetimeformat/locale-data/uz');
|
||||
break;
|
||||
case 'th':
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/th');
|
||||
await import('@formatjs/intl-pluralrules/locale-data/th');
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/th');
|
||||
await import('@formatjs/intl-numberformat/locale-data/th');
|
||||
await import('@formatjs/intl-datetimeformat/locale-data/th');
|
||||
break;
|
||||
case 'km':
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/km');
|
||||
await import('@formatjs/intl-pluralrules/locale-data/km');
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/km');
|
||||
await import('@formatjs/intl-numberformat/locale-data/km');
|
||||
await import('@formatjs/intl-datetimeformat/locale-data/km');
|
||||
break;
|
||||
case 'pl':
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/pl');
|
||||
await import('@formatjs/intl-pluralrules/locale-data/pl');
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/pl');
|
||||
await import('@formatjs/intl-numberformat/locale-data/pl');
|
||||
await import('@formatjs/intl-datetimeformat/locale-data/pl');
|
||||
break;
|
||||
case 'pt':
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/pt');
|
||||
await import('@formatjs/intl-pluralrules/locale-data/pt');
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/pt');
|
||||
await import('@formatjs/intl-numberformat/locale-data/pt');
|
||||
await import('@formatjs/intl-datetimeformat/locale-data/pt');
|
||||
break;
|
||||
case 'ro':
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/ro');
|
||||
await import('@formatjs/intl-pluralrules/locale-data/ro');
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/ro');
|
||||
await import('@formatjs/intl-numberformat/locale-data/ro');
|
||||
await import('@formatjs/intl-datetimeformat/locale-data/ro');
|
||||
break;
|
||||
case 'ru':
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/ru');
|
||||
await import('@formatjs/intl-pluralrules/locale-data/ru');
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/ru');
|
||||
await import('@formatjs/intl-numberformat/locale-data/ru');
|
||||
await import('@formatjs/intl-datetimeformat/locale-data/ru');
|
||||
break;
|
||||
case 'nso':
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/en');
|
||||
await import('@formatjs/intl-pluralrules/locale-data/nso');
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/en');
|
||||
await import('@formatjs/intl-numberformat/locale-data/en');
|
||||
await import('@formatjs/intl-datetimeformat/locale-data/en');
|
||||
break;
|
||||
case 'tn':
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/en');
|
||||
await import('@formatjs/intl-pluralrules/locale-data/tn');
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/en');
|
||||
await import('@formatjs/intl-numberformat/locale-data/en');
|
||||
await import('@formatjs/intl-datetimeformat/locale-data/en');
|
||||
break;
|
||||
case 'sk':
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/sk');
|
||||
await import('@formatjs/intl-pluralrules/locale-data/sk');
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/sk');
|
||||
await import('@formatjs/intl-numberformat/locale-data/sk');
|
||||
await import('@formatjs/intl-datetimeformat/locale-data/sk');
|
||||
break;
|
||||
case 'sl':
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/sl');
|
||||
await import('@formatjs/intl-pluralrules/locale-data/sl');
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/sl');
|
||||
await import('@formatjs/intl-numberformat/locale-data/sl');
|
||||
await import('@formatjs/intl-datetimeformat/locale-data/sl');
|
||||
break;
|
||||
case 'sr':
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/sr');
|
||||
await import('@formatjs/intl-pluralrules/locale-data/sr');
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/sr');
|
||||
await import('@formatjs/intl-numberformat/locale-data/sr');
|
||||
await import('@formatjs/intl-datetimeformat/locale-data/sr');
|
||||
break;
|
||||
case 'fi':
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/fi');
|
||||
await import('@formatjs/intl-pluralrules/locale-data/fi');
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/fi');
|
||||
await import('@formatjs/intl-numberformat/locale-data/fi');
|
||||
await import('@formatjs/intl-datetimeformat/locale-data/fi');
|
||||
break;
|
||||
case 'sv':
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/sv');
|
||||
await import('@formatjs/intl-pluralrules/locale-data/sv');
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/sv');
|
||||
await import('@formatjs/intl-numberformat/locale-data/sv');
|
||||
await import('@formatjs/intl-datetimeformat/locale-data/sv');
|
||||
break;
|
||||
case 'vi':
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/vi');
|
||||
await import('@formatjs/intl-pluralrules/locale-data/vi');
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/vi');
|
||||
await import('@formatjs/intl-numberformat/locale-data/vi');
|
||||
await import('@formatjs/intl-datetimeformat/locale-data/vi');
|
||||
break;
|
||||
case 'tr':
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/tr');
|
||||
await import('@formatjs/intl-pluralrules/locale-data/tr');
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/tr');
|
||||
await import('@formatjs/intl-numberformat/locale-data/tr');
|
||||
await import('@formatjs/intl-datetimeformat/locale-data/tr');
|
||||
break;
|
||||
case 'uk':
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/uk');
|
||||
await import('@formatjs/intl-pluralrules/locale-data/uk');
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/uk');
|
||||
await import('@formatjs/intl-numberformat/locale-data/uk');
|
||||
await import('@formatjs/intl-datetimeformat/locale-data/uk');
|
||||
break;
|
||||
case 'zh':
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/zh');
|
||||
await import('@formatjs/intl-pluralrules/locale-data/zh');
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/zh');
|
||||
await import('@formatjs/intl-numberformat/locale-data/zh');
|
||||
await import('@formatjs/intl-datetimeformat/locale-data/zh');
|
||||
break;
|
||||
default:
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/en');
|
||||
await import('@formatjs/intl-pluralrules/locale-data/en');
|
||||
await import('@formatjs/intl-relativetimeformat/locale-data/en');
|
||||
await import('@formatjs/intl-numberformat/locale-data/en');
|
||||
await import('@formatjs/intl-datetimeformat/locale-data/en');
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
|
10
src/lib/intl-shape.js
Normal file
10
src/lib/intl-shape.js
Normal file
|
@ -0,0 +1,10 @@
|
|||
const PropTypes = require('prop-types');
|
||||
|
||||
// intlShape was removed in react-intl@3 and replaced with a TypeScript interface.
|
||||
// These are some of the commonly used properties from the intl object.
|
||||
const intlShape = PropTypes.shape({
|
||||
locale: PropTypes.string.isRequired,
|
||||
formatMessage: PropTypes.func.isRequired
|
||||
});
|
||||
|
||||
module.exports = intlShape;
|
|
@ -1,7 +0,0 @@
|
|||
const ReactIntl = require('react-intl');
|
||||
|
||||
// Add locale data to react intl for all supported languages
|
||||
const localeData = require('scratch-l10n').localeData;
|
||||
ReactIntl.addLocaleData(localeData);
|
||||
|
||||
module.exports = ReactIntl;
|
44
src/lib/locales.js
Normal file
44
src/lib/locales.js
Normal file
|
@ -0,0 +1,44 @@
|
|||
/**
|
||||
* Scratch has some locales that are not recognized by Intl. Use an appropriate alternative for these locales.
|
||||
* @param {string} locale Scratch's locale
|
||||
* @returns {string} the locale to use in IntlProvider
|
||||
*/
|
||||
const scratchLocaleToIntlLocale = locale => {
|
||||
switch (locale) {
|
||||
case 'ab':
|
||||
return 'ru';
|
||||
case 'an':
|
||||
case 'rap':
|
||||
return 'es';
|
||||
case 'ht':
|
||||
case 'oc':
|
||||
return 'fr';
|
||||
default:
|
||||
return locale;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the locale for the current window.
|
||||
* @returns {string} locale
|
||||
*/
|
||||
const getLocale = () => {
|
||||
// Get locale from global namespace (see "init.js")
|
||||
let locale = window._locale || 'en';
|
||||
if (typeof window._messages !== 'undefined') {
|
||||
if (typeof window._messages[locale] === 'undefined') {
|
||||
// Fall back on the split
|
||||
locale = locale.split('-')[0];
|
||||
}
|
||||
if (typeof window._messages[locale] === 'undefined') {
|
||||
// Language appears to not be supported – fall back to 'en'
|
||||
locale = 'en';
|
||||
}
|
||||
}
|
||||
return locale;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
getLocale,
|
||||
scratchLocaleToIntlLocale
|
||||
};
|
|
@ -2,11 +2,13 @@
|
|||
const React = require('react'); // eslint-disable-line
|
||||
const ReactDOM = require('react-dom');
|
||||
const StoreProvider = require('react-redux').Provider;
|
||||
const IntlProvider = require('react-intl').IntlProvider;
|
||||
|
||||
const IntlProvider = require('./intl.jsx').IntlProvider;
|
||||
const {getLocale, scratchLocaleToIntlLocale} = require('./locales.js');
|
||||
const permissionsActions = require('../redux/permissions.js');
|
||||
const sessionActions = require('../redux/session.js');
|
||||
const configureStore = require('./configure-store.js');
|
||||
import intlPolyfill from '../lib/intl-polyfill';
|
||||
|
||||
require('../main.scss');
|
||||
|
||||
|
@ -20,38 +22,35 @@ require('../main.scss');
|
|||
*/
|
||||
const render = (jsx, element, reducers, initialState, enhancer) => {
|
||||
// Get locale and messages from global namespace (see "init.js")
|
||||
let locale = window._locale || 'en';
|
||||
const locale = getLocale();
|
||||
let messages = {};
|
||||
if (typeof window._messages !== 'undefined') {
|
||||
if (typeof window._messages[locale] === 'undefined') {
|
||||
// Fall back on the split
|
||||
locale = locale.split('-')[0];
|
||||
}
|
||||
if (typeof window._messages[locale] === 'undefined') {
|
||||
// Language appears to not be supported – fall back to 'en'
|
||||
locale = 'en';
|
||||
}
|
||||
messages = window._messages[locale];
|
||||
}
|
||||
|
||||
const intlLocale = scratchLocaleToIntlLocale(locale);
|
||||
// react-intl needs Intl before rendering
|
||||
intlPolyfill(intlLocale).then(() => {
|
||||
const store = configureStore(reducers, initialState, enhancer);
|
||||
|
||||
const store = configureStore(reducers, initialState, enhancer);
|
||||
|
||||
// Render view component
|
||||
ReactDOM.render(
|
||||
<StoreProvider store={store}>
|
||||
<IntlProvider
|
||||
locale={locale}
|
||||
messages={messages}
|
||||
>
|
||||
{jsx}
|
||||
</IntlProvider>
|
||||
</StoreProvider>,
|
||||
element
|
||||
);
|
||||
|
||||
// Get initial session & permissions
|
||||
store.dispatch(permissionsActions.getPermissions());
|
||||
store.dispatch(sessionActions.refreshSession());
|
||||
// Render view component
|
||||
ReactDOM.render(
|
||||
<StoreProvider store={store}>
|
||||
<IntlProvider
|
||||
locale={intlLocale}
|
||||
messages={messages}
|
||||
textComponent="span"
|
||||
>
|
||||
{jsx}
|
||||
</IntlProvider>
|
||||
</StoreProvider>,
|
||||
element
|
||||
);
|
||||
|
||||
// Get initial session & permissions
|
||||
store.dispatch(permissionsActions.getPermissions());
|
||||
store.dispatch(sessionActions.refreshSession());
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = render;
|
||||
|
|
82
src/lib/select-unit.js
Normal file
82
src/lib/select-unit.js
Normal file
|
@ -0,0 +1,82 @@
|
|||
const MILLISECONDS_PER_SECOND = 1000;
|
||||
const SECONDS_PER_MINUTE = 60;
|
||||
const MINUTES_PER_HOUR = 60;
|
||||
const HOURS_PER_DAY = 24;
|
||||
|
||||
/**
|
||||
* Determines the best unit (seconds, minutes, hours, days, months, or years)
|
||||
* and value to represent the time difference between two dates.
|
||||
* @param {Date} time - date to calculate relative units
|
||||
* @param {Date} [relativeTo] - optional relative date, defaults to now
|
||||
* @returns {object} calculated best unit and value
|
||||
*/
|
||||
const selectUnit = (time, relativeTo) => {
|
||||
// Default to now
|
||||
if (!relativeTo) relativeTo = new Date();
|
||||
|
||||
const seconds = (time - relativeTo) / MILLISECONDS_PER_SECOND;
|
||||
if (Math.abs(seconds) < SECONDS_PER_MINUTE) {
|
||||
return {
|
||||
value: Math.trunc(seconds),
|
||||
unit: 'second'
|
||||
};
|
||||
}
|
||||
|
||||
const minutes = seconds / SECONDS_PER_MINUTE;
|
||||
if (Math.abs(minutes) < MINUTES_PER_HOUR) {
|
||||
return {
|
||||
value: Math.trunc(minutes),
|
||||
unit: 'minute'
|
||||
};
|
||||
}
|
||||
|
||||
const hours = minutes / MINUTES_PER_HOUR;
|
||||
if (Math.abs(hours) < HOURS_PER_DAY) {
|
||||
return {
|
||||
value: Math.trunc(hours),
|
||||
unit: 'hour'
|
||||
};
|
||||
}
|
||||
|
||||
const days = hours / HOURS_PER_DAY;
|
||||
|
||||
let years = time.getFullYear() - relativeTo.getFullYear();
|
||||
let months = time.getMonth() - relativeTo.getMonth() + (12 * years);
|
||||
|
||||
// Handle calendar months different but less than a complete month elapsed
|
||||
if (months > 0 && time.getDate() < relativeTo.getDate()) months--;
|
||||
if (months < 0 && time.getDate() > relativeTo.getDate()) months++;
|
||||
|
||||
if (Math.abs(months) < 1) {
|
||||
return {
|
||||
value: Math.trunc(days),
|
||||
unit: 'day'
|
||||
};
|
||||
}
|
||||
|
||||
// Handle calendar years different but less than a complete year elapsed
|
||||
if (years > 0 && (time.getMonth() < relativeTo.getMonth() ||
|
||||
(time.getMonth() === relativeTo.getMonth() && time.getDate() < relativeTo.getDate()))) {
|
||||
years--;
|
||||
}
|
||||
if (years < 0 && (time.getMonth() > relativeTo.getMonth() ||
|
||||
(time.getMonth() === relativeTo.getMonth() && time.getDate() > relativeTo.getDate()))) {
|
||||
years++;
|
||||
}
|
||||
|
||||
if (Math.abs(years) < 1) {
|
||||
return {
|
||||
value: months,
|
||||
unit: 'month'
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
value: years,
|
||||
unit: 'year'
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
selectUnit
|
||||
};
|
|
@ -1,18 +0,0 @@
|
|||
const initSentry = () => {
|
||||
// initialize Sentry instance, making sure it hasn't been initialized already
|
||||
if (!window.Sentry && `${process.env.SENTRY_DSN}` !== '') {
|
||||
const Sentry = require('@sentry/browser');
|
||||
|
||||
Sentry.init({
|
||||
dsn: `${process.env.SENTRY_DSN}`,
|
||||
// Do not collect global onerror, only collect specifically from React error boundaries.
|
||||
// TryCatch plugin also includes errors from setTimeouts (i.e. the VM)
|
||||
integrations: integrations => integrations.filter(i =>
|
||||
!(i.name === 'GlobalHandlers' || i.name === 'TryCatch'))
|
||||
});
|
||||
|
||||
window.Sentry = Sentry; // Allow GUI access to Sentry via window
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = initSentry;
|
|
@ -6,7 +6,7 @@ html,
|
|||
body {
|
||||
display: block;
|
||||
margin: 0;
|
||||
background-color: $ui-blue-dark;
|
||||
background-color: $ui-purple-dark;
|
||||
padding: 0;
|
||||
color: $type-gray;
|
||||
font-family: "Helvetica Neue", "Helvetica", Arial, sans-serif;
|
||||
|
@ -91,19 +91,19 @@ strong {
|
|||
/* Links */
|
||||
a {
|
||||
cursor: pointer;
|
||||
color: $ui-blue;
|
||||
color: $ui-purple-dark;
|
||||
font-weight: bold;
|
||||
|
||||
&:link,
|
||||
&:visited,
|
||||
&:active {
|
||||
text-decoration: none;
|
||||
color: $link-blue;
|
||||
color: $link-purple;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
color: $ui-blue-dark;
|
||||
color: $ui-purple-darker;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -137,6 +137,10 @@ p {
|
|||
font-weight: normal;
|
||||
}
|
||||
|
||||
input {
|
||||
outline-color: $ui-purple-dark;
|
||||
}
|
||||
|
||||
::selection {
|
||||
background-color: $ui-blue-25percent;
|
||||
}
|
||||
|
|
|
@ -13,6 +13,8 @@ const Types = keyMirror({
|
|||
});
|
||||
|
||||
const banGoodListPaths = [
|
||||
'/ip_ban_appeal',
|
||||
'/vpn_required',
|
||||
'/accounts/banned-response',
|
||||
'/accounts/bad-username',
|
||||
'/community_guidelines',
|
||||
|
@ -67,6 +69,19 @@ module.exports.setStatus = status => ({
|
|||
const handleSessionResponse = (dispatch, body) => {
|
||||
if (typeof body === 'undefined') return dispatch(module.exports.setSessionError('No session content'));
|
||||
if (
|
||||
body.vpn_required &&
|
||||
banGoodListPaths.every(goodPath => window.location.pathname.indexOf(goodPath) === -1)
|
||||
) {
|
||||
window.location = '/vpn_required/';
|
||||
return;
|
||||
} else if (
|
||||
body.banned &&
|
||||
body.redirectURL &&
|
||||
banGoodListPaths.every(goodPath => window.location.pathname.indexOf(goodPath) === -1)
|
||||
) {
|
||||
window.location = body.redirectURL;
|
||||
return;
|
||||
} else if (
|
||||
body.user &&
|
||||
body.user.banned &&
|
||||
banGoodListPaths.every(goodPath => window.location.pathname.indexOf(goodPath) === -1)
|
||||
|
@ -145,7 +160,6 @@ module.exports.selectToken = state => get(state, ['session', 'session', 'user',
|
|||
module.exports.selectIsAdmin = state => get(state, ['session', 'session', 'permissions', 'admin'], false);
|
||||
module.exports.selectIsSocial = state => get(state, ['session', 'session', 'permissions', 'social'], false);
|
||||
module.exports.selectIsEducator = state => get(state, ['session', 'session', 'permissions', 'educator'], false);
|
||||
module.exports.selectBannedUser = state => get(state, ['session', 'session', 'user'], false);
|
||||
module.exports.selectProjectCommentsGloballyEnabled = state =>
|
||||
get(state, ['session', 'session', 'flags', 'project_comments_enabled'], false);
|
||||
module.exports.selectStudioCommentsGloballyEnabled = state =>
|
||||
|
|
|
@ -138,6 +138,13 @@
|
|||
"title": "Contact Us",
|
||||
"viewportWidth": "device-width"
|
||||
},
|
||||
{
|
||||
"name": "cookies",
|
||||
"pattern": "^/cookies/?$",
|
||||
"routeAlias": "/cookies/?",
|
||||
"view": "cookies/cookies",
|
||||
"title": "Cookie Policy"
|
||||
},
|
||||
{
|
||||
"name": "credits",
|
||||
"pattern": "^/credits/?$",
|
||||
|
@ -211,7 +218,7 @@
|
|||
{
|
||||
"name": "jobs-redirect",
|
||||
"pattern": "^/jobs/?(\\?.*)?$",
|
||||
"redirect": "https://www.scratchfoundation.org/opportunities/"
|
||||
"redirect": "https://www.scratchfoundation.org/careers"
|
||||
},
|
||||
{
|
||||
"name": "join",
|
||||
|
|
|
@ -28,6 +28,15 @@ module.exports = {
|
|||
og_image_height: 860,
|
||||
|
||||
// Analytics & Monitoring
|
||||
ga_tracker: process.env.GA_TRACKER || '',
|
||||
gtm_id: process.env.GTM_ID || ''
|
||||
// ----------------------
|
||||
|
||||
// Google Tag Manager ID
|
||||
// Looks like 'GTM-XXXXXXX'
|
||||
gtm_id: process.env.GTM_ID || '',
|
||||
|
||||
// Google Tag Manager env & auth info for alterative GTM environments
|
||||
// Looks like '>m_auth=0123456789abcdefghijklm>m_preview=env-00>m_cookies_win=x'
|
||||
// Taken from the middle of: GTM -> Admin -> Environments -> (environment) -> Get Snippet
|
||||
// Blank for production
|
||||
gtm_env_auth: process.env.GTM_ENV_AUTH || ''
|
||||
};
|
||||
|
|
|
@ -8,8 +8,9 @@
|
|||
<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
|
||||
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
|
||||
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
|
||||
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
|
||||
})(window,document,'script','dataLayer','<%- htmlWebpackPlugin.options.gtm_id %>');</script>
|
||||
'https://www.googletagmanager.com/gtm.js?id='+i+dl+'<%= htmlWebpackPlugin.options.gtm_env_auth %>';
|
||||
f.parentNode.insertBefore(j,f);})(window,document,'script','dataLayer','<%- htmlWebpackPlugin.options.gtm_id %>');
|
||||
</script>
|
||||
<!-- End Google Tag Manager -->
|
||||
<% } %>
|
||||
|
||||
|
@ -46,28 +47,12 @@
|
|||
|
||||
<!-- Polyfills -->
|
||||
<script src="/js/polyfill.min.js"></script>
|
||||
|
||||
<!-- Analytics (GA) -->
|
||||
<script>
|
||||
/* eslint-disable */
|
||||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
||||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
||||
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
||||
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
|
||||
|
||||
ga('create', '<%- htmlWebpackPlugin.options.ga_tracker %>', {
|
||||
'sampleRate': 10
|
||||
});
|
||||
ga('send', 'pageview');
|
||||
window.GA_ID = '<%- htmlWebpackPlugin.options.ga_tracker %>';
|
||||
/* eslint-enable */
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<% if (htmlWebpackPlugin.options.gtm_id) { %>
|
||||
<!-- Google Tag Manager (noscript) -->
|
||||
<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=<%- htmlWebpackPlugin.options.gtm_id %>" height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
|
||||
<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=<%- htmlWebpackPlugin.options.gtm_id %><%= htmlWebpackPlugin.options.gtm_env_auth %>" height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
|
||||
<!-- End Google Tag Manager (noscript) -->
|
||||
<% } %>
|
||||
<noscript>
|
||||
|
|
|
@ -5,10 +5,12 @@ const render = require('../../lib/render.jsx');
|
|||
const Button = require('../../components/forms/button.jsx');
|
||||
const Page = require('../../components/page/www/page.jsx');
|
||||
const Video = require('../../components/video/video.jsx');
|
||||
const injectIntl = require('react-intl').injectIntl;
|
||||
|
||||
require('./about.scss');
|
||||
|
||||
const About = () => (
|
||||
const tedLink = chunks => <a href="https://www.ted.com/talks/mitch_resnick_let_s_teach_kids_to_code">{chunks}</a>;
|
||||
const About = injectIntl(({intl}) => (
|
||||
<div className="inner about">
|
||||
<h1><FormattedMessage id="general.aboutScratch" /></h1>
|
||||
|
||||
|
@ -56,14 +58,22 @@ const About = () => (
|
|||
|
||||
<li>
|
||||
<h2><FormattedMessage id="about.literacy" /></h2>
|
||||
<iframe
|
||||
allowFullScreen
|
||||
mozallowfullscreen={'true'}
|
||||
scrolling="no"
|
||||
src="https://embed-ssl.ted.com/talks/mitch_resnick_let_s_teach_kids_to_code.html"
|
||||
webkitallowfullscreen={'true'}
|
||||
/>
|
||||
<p><FormattedMessage id="about.literacyDescription" /></p>
|
||||
<a href="https://www.ted.com/talks/mitch_resnick_let_s_teach_kids_to_code">
|
||||
<img
|
||||
alt={intl.formatMessage(
|
||||
{id: 'about.literacyImageDescription'}
|
||||
)}
|
||||
src="/images/about/ted-thumbnail.jpg"
|
||||
/>
|
||||
</a>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="about.literacyDescription"
|
||||
values={{
|
||||
a: tedLink
|
||||
}}
|
||||
/>
|
||||
</p>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
@ -176,7 +186,7 @@ const About = () => (
|
|||
),
|
||||
annualReportLink: (
|
||||
<a
|
||||
href="/annual-report"
|
||||
href="https://www.scratchfoundation.org/annualreport"
|
||||
>
|
||||
<FormattedMessage id="about.annualReportLinkText" />
|
||||
</a>
|
||||
|
@ -198,7 +208,7 @@ const About = () => (
|
|||
<a href="/educators"><FormattedMessage id="about.learnMoreEducators" /></a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/annual-report"><FormattedMessage id="about.learnMoreAnnualReport" /></a>
|
||||
<a href="https://www.scratchfoundation.org/annualreport"><FormattedMessage id="about.learnMoreAnnualReport" /></a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
|
@ -241,6 +251,6 @@ const About = () => (
|
|||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
));
|
||||
|
||||
render(<Page><About /></Page>, document.getElementById('app'));
|
||||
|
|
|
@ -79,7 +79,7 @@
|
|||
|
||||
.about-button {
|
||||
margin-right: .75rem;
|
||||
background-color: $ui-blue;
|
||||
background-color: $ui-purple-dark;
|
||||
color: $ui-white;
|
||||
font-size: 1rem;
|
||||
|
||||
|
|
|
@ -19,7 +19,8 @@
|
|||
"about.learnMoreEducators": "Information for Educators",
|
||||
"about.learnMoreAnnualReport": "Annual Report",
|
||||
"about.literacy": "Learn to Code, Code to Learn",
|
||||
"about.literacyDescription": "The ability to code computer programs is an important part of literacy in today’s society. When people learn to code in Scratch, they learn important strategies for solving problems, designing projects, and communicating ideas.",
|
||||
"about.literacyImageDescription": "An image of Mitch Resnick giving a TED talk titled \"Let's Teach Kids to Code.\" A play button is in the center of the image.",
|
||||
"about.literacyDescription": "In this <a>TED talk</a>, Scratch founder Mitch Resnick describes why the ability to code computer programs is an important part of literacy in today’s society. When people learn to code in Scratch, they learn important strategies for solving problems, designing projects, and communicating ideas.",
|
||||
"about.schools": "Scratch in Schools",
|
||||
"about.schoolsDescription": "Students are learning with Scratch at all levels (from elementary school to college) and across disciplines (such as math, computer science, language arts, social studies). Educator resources are available on the {scratchForEducatorsLink} page.",
|
||||
"about.scratchForEducatorsLinkText": "Scratch For Educators",
|
||||
|
|
|
@ -4,8 +4,8 @@ const React = require('react');
|
|||
const MediaQuery = require('react-responsive').default;
|
||||
const FormattedMessage = require('react-intl').FormattedMessage;
|
||||
const injectIntl = require('react-intl').injectIntl;
|
||||
const intlShape = require('react-intl').intlShape;
|
||||
|
||||
const intlShape = require('../../../lib/intl-shape');
|
||||
const render = require('../../../lib/render.jsx');
|
||||
const frameless = require('../../../lib/frameless');
|
||||
|
||||
|
|
|
@ -4,8 +4,8 @@ const React = require('react');
|
|||
const MediaQuery = require('react-responsive').default;
|
||||
const FormattedMessage = require('react-intl').FormattedMessage;
|
||||
const injectIntl = require('react-intl').injectIntl;
|
||||
const intlShape = require('react-intl').intlShape;
|
||||
|
||||
const intlShape = require('../../../lib/intl-shape');
|
||||
const render = require('../../../lib/render.jsx');
|
||||
const frameless = require('../../../lib/frameless');
|
||||
|
||||
|
@ -2129,6 +2129,7 @@ class AnnualReport extends React.Component {
|
|||
comment={this.props.intl.formatMessage(
|
||||
{id: 'annualReport.2020.communityQuote2Text'}
|
||||
)}
|
||||
datetimeCreated="2020-01-01"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -4,8 +4,8 @@ const React = require('react');
|
|||
const MediaQuery = require('react-responsive').default;
|
||||
const FormattedMessage = require('react-intl').FormattedMessage;
|
||||
const injectIntl = require('react-intl').injectIntl;
|
||||
const intlShape = require('react-intl').intlShape;
|
||||
|
||||
const intlShape = require('../../../lib/intl-shape');
|
||||
const render = require('../../../lib/render.jsx');
|
||||
const frameless = require('../../../lib/frameless');
|
||||
|
||||
|
|
|
@ -75,7 +75,7 @@ p a {
|
|||
|
||||
a, a:link, a:visited, a:active{
|
||||
cursor: pointer;
|
||||
color: $motion-blue-3;
|
||||
color: $ui-purple-dark;
|
||||
}
|
||||
|
||||
.bold {
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
const injectIntl = require('react-intl').injectIntl;
|
||||
const intlShape = require('react-intl').intlShape;
|
||||
const FormattedMessage = require('react-intl').FormattedMessage;
|
||||
const React = require('react');
|
||||
|
||||
const intlShape = require('../../lib/intl-shape');
|
||||
|
||||
const Page = require('../../components/page/www/page.jsx');
|
||||
const render = require('../../lib/render.jsx');
|
||||
|
||||
|
@ -34,7 +35,7 @@ class Boost extends ExtensionLanding {
|
|||
renderCopy={
|
||||
<FlexRow className="extension-copy">
|
||||
<h1><img
|
||||
alt=""
|
||||
alt="Boost"
|
||||
className="headline-icon"
|
||||
src="/images/boost/boost.svg"
|
||||
/>LEGO BOOST</h1>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
.boost {
|
||||
.extension-header {
|
||||
background-color: $ui-orange;
|
||||
background-color: $ui-magenta-dark;
|
||||
background-image: url("/images/boost/boost-pattern.svg");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
const React = require('react');
|
||||
const injectIntl = require('react-intl').injectIntl;
|
||||
const FormattedMessage = require('react-intl').FormattedMessage;
|
||||
const FormattedHTMLMessage = require('react-intl').FormattedHTMLMessage;
|
||||
const render = require('../../lib/render.jsx');
|
||||
const TitleBanner = require('../../components/title-banner/title-banner.jsx');
|
||||
const FlexRow = require('../../components/flex-row/flex-row.jsx');
|
||||
|
@ -10,6 +9,10 @@ const Page = require('../../components/page/www/page.jsx');
|
|||
|
||||
require('./camp.scss');
|
||||
|
||||
const MainStudio = chunks => <a href="https://scratch.mit.edu/studios/4160302/">{chunks}</a>;
|
||||
const FinalStudio = chunks => <a href="https://scratch.mit.edu/studios/4160301/">{chunks}</a>;
|
||||
const CounselorsStudio = chunks => <a href="https://scratch.mit.edu/studios/4160300/">{chunks}</a>;
|
||||
|
||||
const Camp = injectIntl(() => (
|
||||
<div>
|
||||
<TitleBanner className="masthead mod-blue-bg">
|
||||
|
@ -40,7 +43,10 @@ const Camp = injectIntl(() => (
|
|||
<center>
|
||||
<h2><FormattedMessage id="camp.welcome" /></h2>
|
||||
<p id="intro">
|
||||
<FormattedHTMLMessage id="camp.welcomeIntro" />
|
||||
<FormattedMessage
|
||||
id="camp.welcomeIntroHTML"
|
||||
values={{br: <br />}}
|
||||
/>
|
||||
</p>
|
||||
</center>
|
||||
<center>
|
||||
|
@ -63,7 +69,10 @@ const Camp = injectIntl(() => (
|
|||
<div className="sidebar column">
|
||||
<h3><FormattedMessage id="camp.particpateTitle" /></h3>
|
||||
<p>
|
||||
<FormattedHTMLMessage id="camp.part1Particpate" />
|
||||
<FormattedMessage
|
||||
id="camp.part1ParticpateHTML"
|
||||
values={{a: MainStudio}}
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
</FlexRow>
|
||||
|
@ -86,7 +95,10 @@ const Camp = injectIntl(() => (
|
|||
<div className="sidebar column">
|
||||
<h3><FormattedMessage id="camp.particpateTitle" /></h3>
|
||||
<p>
|
||||
<FormattedHTMLMessage id="camp.part2Particpate" />
|
||||
<FormattedMessage
|
||||
id="camp.part2ParticpateHTML"
|
||||
values={{a: MainStudio}}
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
</FlexRow>
|
||||
|
@ -109,7 +121,10 @@ const Camp = injectIntl(() => (
|
|||
<div className="sidebar column">
|
||||
<h3><FormattedMessage id="camp.particpateTitle" /></h3>
|
||||
<p>
|
||||
<FormattedHTMLMessage id="camp.part3Particpate" />
|
||||
<FormattedMessage
|
||||
id="camp.part3ParticpateHTML"
|
||||
values={{a: FinalStudio}}
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
</FlexRow>
|
||||
|
@ -132,7 +147,10 @@ const Camp = injectIntl(() => (
|
|||
src="/images/camp/dolphin.svg"
|
||||
/>
|
||||
<p>
|
||||
<FormattedHTMLMessage id="camp.infoCounselors" />
|
||||
<FormattedMessage
|
||||
id="camp.infoCounselorsHTML"
|
||||
values={{a: CounselorsStudio}}
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue