diff --git a/bin/build-locales b/bin/build-locales
index 0c2c20d4d..d9af2135b 100755
--- a/bin/build-locales
+++ b/bin/build-locales
@@ -106,7 +106,9 @@ for ( var l in languages ) {
l + '.json'
);
fs.accessSync(langTranslations);
- generalLocales[l] = JSON.parse(fs.readFileSync(langTranslations, 'utf8'));
+ if (l !== 'en') {
+ generalLocales[l] = JSON.parse(fs.readFileSync(langTranslations, 'utf8'));
+ }
} catch (err) {
// just use english
generalLocales[l] = generalLocales['en'];
@@ -128,7 +130,7 @@ var localizedAssetUrls = {};
// - the route name
// - the transifex resource
// Add exceptions:
-// - txMapping: if the name of the transifex resource is different from the route name
+// - txMapping: if the name of the transifex resource is different from the route name
var txMapping = {
'projects': 'preview',
'scratch_1.4': 'scratch_14' // transifex doesn't allow dots in resource names
@@ -228,8 +230,11 @@ async.forEachLimit(views, 5, function (view, cb) {
}
}
try {
- viewLocales[isoCode] = merge({}, generalLocales[isoCode], JSON.parse(data));
- defaults(viewLocales[isoCode], viewLocales['en']);
+ // don't overwrite english view strings
+ if (isoCode !== 'en') {
+ viewLocales[isoCode] = merge({}, generalLocales[isoCode], JSON.parse(data));
+ defaults(viewLocales[isoCode], viewLocales['en']);
+ }
} catch (e) {
return cb(e);
}
diff --git a/package.json b/package.json
index 8036a319a..9bdb98d5e 100644
--- a/package.json
+++ b/package.json
@@ -101,7 +101,7 @@
"react-responsive": "3.0.0",
"react-slick": "0.16.0",
"react-string-replace": "0.4.1",
- "scratch-gui": "0.1.0-prerelease.20190501211108",
+ "scratch-gui": "0.1.0-prerelease.20190515193432",
"react-telephone-input": "4.3.4",
"redux": "3.5.2",
"redux-thunk": "2.0.1",
diff --git a/src/components/modal/social/container.jsx b/src/components/modal/social/container.jsx
new file mode 100644
index 000000000..876e88c19
--- /dev/null
+++ b/src/components/modal/social/container.jsx
@@ -0,0 +1,114 @@
+const bindAll = require('lodash.bindall');
+const PropTypes = require('prop-types');
+const React = require('react');
+const SocialModalPresentation = require('./presentation.jsx');
+const clipboardCopy = require('clipboard-copy');
+const social = require('../../../lib/social');
+
+class SocialModal extends React.Component {
+ constructor (props) {
+ super(props);
+ this.embedTextarea = {};
+ this.embedCopyTimeoutId = null;
+ this.linkCopyTimeoutId = null;
+ this.linkTextarea = {};
+ this.showCopyResultTimeout = 2000;
+ this.state = {
+ showEmbedResult: false,
+ showLinkResult: false
+ };
+ bindAll(this, [
+ 'handleCopyEmbed',
+ 'handleCopyProjectLink',
+ 'hideEmbedResult',
+ 'hideLinkResult',
+ 'setEmbedTextarea',
+ 'setLinkTextarea'
+ ]);
+ }
+ componentWillUnmount () {
+ this.clearEmbedCopyResultTimeout();
+ this.clearLinkCopyResultTimeout();
+ }
+ handleCopyEmbed () {
+ if (this.embedTextarea) {
+ this.embedTextarea.select();
+ clipboardCopy(this.embedTextarea.value);
+ if (this.state.showEmbedResult === false && this.embedCopyTimeoutId === null) {
+ this.setState({showEmbedResult: true}, () => {
+ this.embedCopyTimeoutId = setTimeout(
+ this.hideEmbedResult,
+ this.showCopyResultTimeout
+ );
+ });
+ }
+ }
+ }
+ handleCopyProjectLink () {
+ if (this.linkTextarea) {
+ this.linkTextarea.select();
+ clipboardCopy(this.linkTextarea.value);
+ if (this.state.showLinkResult === false && this.linkCopyTimeoutId === null) {
+ this.setState({showLinkResult: true}, () => {
+ this.linkCopyTimeoutId = setTimeout(
+ this.hideLinkResult,
+ this.showCopyResultTimeout
+ );
+ });
+ }
+ }
+ }
+ hideEmbedResult () {
+ this.setState({showEmbedResult: false});
+ this.embedCopyTimeoutId = null;
+ }
+ hideLinkResult () {
+ this.setState({showLinkResult: false});
+ this.linkCopyTimeoutId = null;
+ }
+ setEmbedTextarea (textarea) {
+ this.embedTextarea = textarea;
+ return textarea;
+ }
+ setLinkTextarea (textarea) {
+ this.linkTextarea = textarea;
+ return textarea;
+ }
+ clearEmbedCopyResultTimeout () {
+ if (this.embedCopyTimeoutId !== null) {
+ clearTimeout(this.embedCopyTimeoutId);
+ this.embedCopyTimeoutId = null;
+ }
+ }
+ clearLinkCopyResultTimeout () {
+ if (this.linkCopyTimeoutId !== null) {
+ clearTimeout(this.linkCopyTimeoutId);
+ this.linkCopyTimeoutId = null;
+ }
+ }
+ render () {
+ const projectId = this.props.projectId;
+ return (
+
+ );
+ }
+}
+
+SocialModal.propTypes = {
+ isOpen: PropTypes.bool,
+ onRequestClose: PropTypes.func,
+ projectId: PropTypes.oneOfType([PropTypes.number, PropTypes.string])
+};
+
+module.exports = SocialModal;
diff --git a/src/components/modal/social/modal.scss b/src/components/modal/social/modal.scss
new file mode 100644
index 000000000..be290d21e
--- /dev/null
+++ b/src/components/modal/social/modal.scss
@@ -0,0 +1,132 @@
+@import "../../../colors";
+@import "../../../frameless";
+
+.mod-social {
+ min-height: 15rem;
+ max-height: calc(100% - 8rem);
+ overflow: hidden;
+}
+
+.social-modal-header {
+ border-radius: 1rem 1rem 0 0;
+ box-shadow: inset 0 -1px 0 0 $ui-blue-dark;
+ background-color: $ui-blue;
+}
+
+.social-modal-content {
+ box-shadow: none;
+ width: 92%;
+ height: calc(100% - 0rem);
+ margin: 1rem auto 1.625rem;
+ font-size: .9375rem;
+}
+
+.social-row {
+ width: 100%;
+ margin-top: .5rem;
+ margin-bottom: .5rem;
+ align-items: start;
+}
+
+.social-spaced-row {
+ // width: 100%;
+ justify-content: space-between;
+ align-items: flex-end;
+}
+
+.social-row-right {
+ margin-left: auto;
+}
+
+.social-label-row {
+ width: 100%;
+ font-weight: bold;
+ margin-bottom: .5rem;
+ justify-content: space-between;
+ align-items: flex-end;
+}
+
+.social-label-title {
+ font-size: 1rem;
+ margin-right: 1.5rem;
+}
+
+.social-label-item {
+ margin-left: 1.5rem;
+ margin-right: .25rem;
+}
+
+.social-label-result {
+ color: $type-gray-75percent;
+ transition: opacity 100ms linear;
+}
+
+.link-section {
+ margin-top: .5rem;
+}
+
+.embed-section {
+ margin-top: 1rem;
+}
+
+.social-social-icon {
+ width: 2.75rem;
+ height: 2.75rem;
+ margin-right: .75rem;
+ background-repeat: no-repeat;
+ background-size: contain;
+}
+
+.social-twitter-icon {
+ background-image: url("/images/social/twitter.png");
+}
+
+.social-facebook-icon {
+ background-image: url("/images/social/facebook.png");
+}
+
+.social-google-classroom-icon {
+ background-image: url("/images/social/google-classroom.png");
+}
+
+.social-wechat-icon {
+ background-image: url("/images/social/wechat.png");
+}
+
+.social-form {
+ transition: all .2s ease;
+ border: 2px solid $box-shadow-light-gray;
+ border-radius: 8px;
+ background-color: $ui-blue-10percent;
+ color: $type-gray;
+ padding: .75rem .875rem;
+ line-height: 1.25rem;
+ font-size: .875rem;
+ box-sizing: border-box;
+ resize: none;
+ overflow: hidden;
+ width: 100%;
+
+ &:focus {
+ transition: all .2s ease;
+ outline: none;
+ border: 2px solid $ui-blue;
+ box-shadow: 0 0 0 4px $ui-blue-25percent;
+ }
+
+ &.social-textarea {
+ height: 6rem;
+ }
+
+ &.social-input {
+ height: 2.75rem;
+ }
+
+ textarea {
+ min-height: 4rem;
+ }
+}
+
+.social-hidden {
+ opacity: 0.0;
+}
diff --git a/src/components/modal/social/presentation.jsx b/src/components/modal/social/presentation.jsx
new file mode 100644
index 000000000..afd0b3981
--- /dev/null
+++ b/src/components/modal/social/presentation.jsx
@@ -0,0 +1,135 @@
+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 classNames = require('classnames');
+
+const Modal = require('../base/modal.jsx');
+const FlexRow = require('../../flex-row/flex-row.jsx');
+
+require('../../forms/button.scss');
+require('./modal.scss');
+
+const SocialModalPresentation = ({
+ embedHtml,
+ intl,
+ isOpen,
+ onCopyEmbed,
+ onCopyProjectLink,
+ onRequestClose,
+ projectUrl,
+ setEmbedTextarea,
+ setLinkTextarea,
+ showEmbedResult,
+ showLinkResult
+}) => {
+ const title = intl.formatMessage({id: 'general.copyLink'});
+
+ return (
+
+
+
+
+ {/* top row: link */}
+
+
+
+
+ {intl.formatMessage({id: 'social.linkLabel'})}
+
+
+
+ {intl.formatMessage({id: 'social.embedCopiedResultText'})}
+
+
+
+
+ setLinkTextarea(textarea)}
+ value={projectUrl}
+ />
+
+
+
+ {/* bottom row: embed */}
+
+
+
+
+ {intl.formatMessage({id: 'social.embedLabel'})}
+
+
+
+ {intl.formatMessage({id: 'social.embedCopiedResultText'})}
+
+
+
+
+
+
+
+
+
+ );
+};
+
+SocialModalPresentation.propTypes = {
+ embedHtml: PropTypes.string,
+ intl: intlShape,
+ isOpen: PropTypes.bool,
+ onCopyEmbed: PropTypes.func,
+ onCopyProjectLink: PropTypes.func,
+ onRequestClose: PropTypes.func,
+ projectUrl: PropTypes.string,
+ setEmbedTextarea: PropTypes.func,
+ setLinkTextarea: PropTypes.func,
+ showEmbedResult: PropTypes.bool,
+ showLinkResult: PropTypes.bool
+};
+
+module.exports = injectIntl(SocialModalPresentation);
diff --git a/src/l10n.json b/src/l10n.json
index cd3e528d4..c9920e4d3 100644
--- a/src/l10n.json
+++ b/src/l10n.json
@@ -256,5 +256,11 @@
"comments.status.suspended": "Suspended",
"comments.status.acctdel": "Account deleted",
"comments.status.deleted": "Deleted",
- "comments.status.reported": "Reported"
+ "comments.status.reported": "Reported",
+
+ "social.embedLabel": "Embed",
+ "social.copyEmbedLinkText": "Copy embed",
+ "social.linkLabel": "Link",
+ "social.copyLinkLinkText": "Copy link",
+ "social.embedCopiedResultText": "Copied"
}
diff --git a/src/lib/social.js b/src/lib/social.js
new file mode 100644
index 000000000..bbd1cbff4
--- /dev/null
+++ b/src/lib/social.js
@@ -0,0 +1,17 @@
+module.exports = {};
+
+module.exports.projectUrl = projectId => {
+ if (projectId) {
+ return `https://scratch.mit.edu/projects/${projectId}`;
+ }
+ return '';
+};
+
+module.exports.embedHtml = projectId => {
+ if (projectId) {
+ return `';
+ }
+ return '';
+};
diff --git a/src/routes.json b/src/routes.json
index cd90bd92b..43ea0737a 100644
--- a/src/routes.json
+++ b/src/routes.json
@@ -463,7 +463,7 @@
{
"name": "story-tutorial-redirect",
"pattern": "^/story/?$",
- "redirect": "/projects/editor/?tutorial=story"
+ "redirect": "/projects/editor/?tutorial=tell-a-story"
},
{
"name": "pong-tutorial-redirect",
diff --git a/src/views/conference/2019/index/index.jsx b/src/views/conference/2019/index/index.jsx
index 999c5a02e..753977585 100644
--- a/src/views/conference/2019/index/index.jsx
+++ b/src/views/conference/2019/index/index.jsx
@@ -53,7 +53,7 @@ const ConferenceSplash = () => (
src="/svgs/conference/flags/cl.svg"
/>
-
Conferencia Scratch al Sur
+ Scratch al Sur Conferencia Chile 2019
@@ -141,104 +141,7 @@ const ConferenceSplash = () => (
-
-
+
(
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/views/preview/comment/comment.scss b/src/views/preview/comment/comment.scss
index 34f913435..c78905e74 100644
--- a/src/views/preview/comment/comment.scss
+++ b/src/views/preview/comment/comment.scss
@@ -197,6 +197,19 @@
background: $reported-background;
}
}
+
+ &.comment-bubble-markedbyfilter {
+ $markedbyfilter-outline: $ui-yellow;
+ $markedbyfilter-background: rgb(255, 254, 210);
+
+ border-color: $markedbyfilter-outline;
+ background-color: $markedbyfilter-background;
+
+ &:before {
+ border-color: $markedbyfilter-outline transparent $markedbyfilter-outline $markedbyfilter-outline;
+ background: $markedbyfilter-background;
+ }
+ }
}
.comment-content {
diff --git a/src/views/preview/presentation.jsx b/src/views/preview/presentation.jsx
index e0234396a..0e764a868 100644
--- a/src/views/preview/presentation.jsx
+++ b/src/views/preview/presentation.jsx
@@ -88,7 +88,6 @@ const PreviewPresentation = ({
onAddToStudioClicked,
onAddToStudioClosed,
onCloseAdminPanel,
- onCopyProjectLink,
onDeleteComment,
onFavoriteClicked,
onGreenFlag,
@@ -108,6 +107,8 @@ const PreviewPresentation = ({
onSeeInside,
onSetProjectThumbnailer,
onShare,
+ onSocialClicked,
+ onSocialClosed,
onToggleComments,
onToggleStudio,
onUpdateProjectId,
@@ -126,6 +127,7 @@ const PreviewPresentation = ({
showAdminPanel,
showModInfo,
singleCommentId,
+ socialOpen,
userOwnsProject,
visibilityInfo
}) => {
@@ -368,13 +370,15 @@ const PreviewPresentation = ({
projectInfo={projectInfo}
reportOpen={reportOpen}
shareDate={shareDate}
+ socialOpen={socialOpen}
userOwnsProject={userOwnsProject}
onAddToStudioClicked={onAddToStudioClicked}
onAddToStudioClosed={onAddToStudioClosed}
- onCopyProjectLink={onCopyProjectLink}
onReportClicked={onReportClicked}
onReportClose={onReportClose}
onReportSubmit={onReportSubmit}
+ onSocialClicked={onSocialClicked}
+ onSocialClosed={onSocialClosed}
onToggleStudio={onToggleStudio}
/>
@@ -514,13 +518,15 @@ const PreviewPresentation = ({
projectInfo={projectInfo}
reportOpen={reportOpen}
shareDate={shareDate}
+ socialOpen={socialOpen}
userOwnsProject={userOwnsProject}
onAddToStudioClicked={onAddToStudioClicked}
onAddToStudioClosed={onAddToStudioClosed}
- onCopyProjectLink={onCopyProjectLink}
onReportClicked={onReportClicked}
onReportClose={onReportClose}
onReportSubmit={onReportSubmit}
+ onSocialClicked={onSocialClicked}
+ onSocialClosed={onSocialClosed}
onToggleStudio={onToggleStudio}
/>
@@ -693,7 +699,6 @@ PreviewPresentation.propTypes = {
onAddToStudioClicked: PropTypes.func,
onAddToStudioClosed: PropTypes.func,
onCloseAdminPanel: PropTypes.func,
- onCopyProjectLink: PropTypes.func,
onDeleteComment: PropTypes.func,
onFavoriteClicked: PropTypes.func,
onGreenFlag: PropTypes.func,
@@ -713,6 +718,8 @@ PreviewPresentation.propTypes = {
onSeeInside: PropTypes.func,
onSetProjectThumbnailer: PropTypes.func,
onShare: PropTypes.func,
+ onSocialClicked: PropTypes.func,
+ onSocialClosed: PropTypes.func,
onToggleComments: PropTypes.func,
onToggleStudio: PropTypes.func,
onUpdateProjectId: PropTypes.func,
@@ -731,6 +738,7 @@ PreviewPresentation.propTypes = {
showModInfo: PropTypes.bool,
showUsernameBlockAlert: PropTypes.bool,
singleCommentId: PropTypes.oneOfType([PropTypes.number, PropTypes.bool]),
+ socialOpen: PropTypes.bool,
userOwnsProject: PropTypes.bool,
visibilityInfo: PropTypes.shape({
censored: PropTypes.bool,
diff --git a/src/views/preview/project-view.jsx b/src/views/preview/project-view.jsx
index abcd8971e..903d01fd4 100644
--- a/src/views/preview/project-view.jsx
+++ b/src/views/preview/project-view.jsx
@@ -8,7 +8,6 @@ const PropTypes = require('prop-types');
const connect = require('react-redux').connect;
const injectIntl = require('react-intl').injectIntl;
const parser = require('scratch-parser');
-const copy = require('clipboard-copy');
const Page = require('../../components/page/www/page.jsx');
const storage = require('../../lib/storage.js').default;
@@ -55,8 +54,9 @@ class Preview extends React.Component {
'fetchCommunityData',
'handleAddComment',
'handleClickLogo',
- 'handleCopyProjectLink',
'handleDeleteComment',
+ 'handleSocialClick',
+ 'handleSocialClose',
'handleToggleStudio',
'handleFavoriteToggle',
'handleLoadMore',
@@ -109,6 +109,7 @@ class Preview extends React.Component {
clientFaved: false,
clientLoved: false,
extensions: [],
+ socialOpen: false,
favoriteCount: 0,
isProjectLoaded: false,
isRemixing: false,
@@ -381,6 +382,12 @@ class Preview extends React.Component {
handleAddToStudioClose () {
this.setState({addToStudioOpen: false});
}
+ handleSocialClick () {
+ this.setState({socialOpen: true});
+ }
+ handleSocialClose () {
+ this.setState({socialOpen: false});
+ }
handleReportSubmit (formData) {
const submit = data => this.props.reportProject(this.state.projectId, data, this.props.user.token);
if (this.getProjectThumbnail) {
@@ -579,11 +586,6 @@ class Preview extends React.Component {
this.props.user.token
);
}
- handleCopyProjectLink () {
- // Use the pathname so we do not have to update this if path changes
- // Also do not include hash or query params
- copy(`${window.location.origin}${window.location.pathname}`);
- }
initCounts (favorites, loves) {
this.setState({
favoriteCount: favorites,
@@ -678,13 +680,13 @@ class Preview extends React.Component {
showModInfo={this.props.isAdmin}
showUsernameBlockAlert={this.state.showUsernameBlockAlert}
singleCommentId={this.state.singleCommentId}
+ socialOpen={this.state.socialOpen}
userOwnsProject={this.props.userOwnsProject}
visibilityInfo={this.props.visibilityInfo}
onAddComment={this.handleAddComment}
onAddToStudioClicked={this.handleAddToStudioClick}
onAddToStudioClosed={this.handleAddToStudioClose}
onCloseAdminPanel={this.handleCloseAdminPanel}
- onCopyProjectLink={this.handleCopyProjectLink}
onDeleteComment={this.handleDeleteComment}
onFavoriteClicked={this.handleFavoriteToggle}
onGreenFlag={this.handleGreenFlag}
@@ -704,6 +706,8 @@ class Preview extends React.Component {
onSeeInside={this.handleSeeInside}
onSetProjectThumbnailer={this.handleSetProjectThumbnailer}
onShare={this.handleShare}
+ onSocialClicked={this.handleSocialClick}
+ onSocialClosed={this.handleSocialClose}
onToggleComments={this.handleToggleComments}
onToggleStudio={this.handleToggleStudio}
onUpdateProjectId={this.handleUpdateProjectId}
diff --git a/src/views/preview/subactions.jsx b/src/views/preview/subactions.jsx
index 2bf7f9626..5478d080a 100644
--- a/src/views/preview/subactions.jsx
+++ b/src/views/preview/subactions.jsx
@@ -6,7 +6,9 @@ const FlexRow = require('../../components/flex-row/flex-row.jsx');
const Button = require('../../components/forms/button.jsx');
const AddToStudioModal = require('./add-to-studio.jsx');
+const SocialModal = require('../../components/modal/social/container.jsx');
const ReportModal = require('../../components/modal/report/modal.jsx');
+const projectShape = require('./projectshape.jsx').projectShape;
require('./subactions.scss');
@@ -50,10 +52,18 @@ const Subactions = props => (
}
+ {props.socialOpen && props.projectInfo && props.projectInfo.id && (
+
+ )}
{(props.canReport) &&