Merge pull request #2980 from LLK/release/05-16-2019

[Master]Release/05 16 2019
This commit is contained in:
picklesrus 2019-05-16 15:22:38 -04:00 committed by GitHub
commit bda2c70725
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 517 additions and 128 deletions

View file

@ -106,7 +106,9 @@ for ( var l in languages ) {
l + '.json' l + '.json'
); );
fs.accessSync(langTranslations); fs.accessSync(langTranslations);
generalLocales[l] = JSON.parse(fs.readFileSync(langTranslations, 'utf8')); if (l !== 'en') {
generalLocales[l] = JSON.parse(fs.readFileSync(langTranslations, 'utf8'));
}
} catch (err) { } catch (err) {
// just use english // just use english
generalLocales[l] = generalLocales['en']; generalLocales[l] = generalLocales['en'];
@ -128,7 +130,7 @@ var localizedAssetUrls = {};
// - the route name // - the route name
// - the transifex resource // - the transifex resource
// Add exceptions: // 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 = { var txMapping = {
'projects': 'preview', 'projects': 'preview',
'scratch_1.4': 'scratch_14' // transifex doesn't allow dots in resource names '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 { try {
viewLocales[isoCode] = merge({}, generalLocales[isoCode], JSON.parse(data)); // don't overwrite english view strings
defaults(viewLocales[isoCode], viewLocales['en']); if (isoCode !== 'en') {
viewLocales[isoCode] = merge({}, generalLocales[isoCode], JSON.parse(data));
defaults(viewLocales[isoCode], viewLocales['en']);
}
} catch (e) { } catch (e) {
return cb(e); return cb(e);
} }

View file

@ -19,6 +19,10 @@
"locale": "ja-hr", "locale": "ja-hr",
"parentLocale": "ja" "parentLocale": "ja"
}, },
"rap": {
"locale": "rap",
"parentLocale": "es"
},
"la": { "la": {
"locale": "la", "locale": "la",
"parentLocale": "it" "parentLocale": "it"

View file

@ -101,7 +101,7 @@
"react-responsive": "3.0.0", "react-responsive": "3.0.0",
"react-slick": "0.16.0", "react-slick": "0.16.0",
"react-string-replace": "0.4.1", "react-string-replace": "0.4.1",
"scratch-gui": "0.1.0-prerelease.20190501211108", "scratch-gui": "0.1.0-prerelease.20190516172446",
"react-telephone-input": "4.3.4", "react-telephone-input": "4.3.4",
"redux": "3.5.2", "redux": "3.5.2",
"redux-thunk": "2.0.1", "redux-thunk": "2.0.1",

View file

@ -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 (
<SocialModalPresentation
embedHtml={social.embedHtml(projectId)}
isOpen={this.props.isOpen}
projectUrl={social.projectUrl(projectId)}
setEmbedTextarea={this.setEmbedTextarea}
setLinkTextarea={this.setLinkTextarea}
showEmbedResult={this.state.showEmbedResult}
showLinkResult={this.state.showLinkResult}
onCopyEmbed={this.handleCopyEmbed}
onCopyProjectLink={this.handleCopyProjectLink}
onRequestClose={this.props.onRequestClose}
/>
);
}
}
SocialModal.propTypes = {
isOpen: PropTypes.bool,
onRequestClose: PropTypes.func,
projectId: PropTypes.oneOfType([PropTypes.number, PropTypes.string])
};
module.exports = SocialModal;

View file

@ -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;
}

View file

@ -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 (
<Modal
useStandardSizes
className="mod-social"
contentLabel={title}
isOpen={isOpen}
onRequestClose={onRequestClose}
>
<div className="social-modal-header modal-header">
<div className="social-content-label content-label">
<FormattedMessage id="general.copyLink" />
</div>
</div>
<div className="modal-content social-modal-content">
{/* top row: link */}
<div className="link-section">
<FlexRow className="social-row social-spaced-row">
<FlexRow className="social-label-row">
<div className="social-label-title">
{intl.formatMessage({id: 'social.linkLabel'})}
</div>
<FlexRow className="social-spaced-row social-row-right">
<div
className={classNames(
'social-label-item',
'social-label-result',
{'social-hidden': !showLinkResult}
)}
>
{intl.formatMessage({id: 'social.embedCopiedResultText'})}
</div>
<div className="social-label-item">
<a
onClick={onCopyProjectLink}
>
{intl.formatMessage({id: 'social.copyLinkLinkText'})}
</a>
</div>
</FlexRow>
</FlexRow>
<input
readOnly
className="social-form social-input"
name="link"
ref={textarea => setLinkTextarea(textarea)}
value={projectUrl}
/>
</FlexRow>
</div>
{/* bottom row: embed */}
<div className="embed-section">
<FlexRow className="social-row social-spaced-row">
<FlexRow className="social-label-row">
<div className="social-label-title">
{intl.formatMessage({id: 'social.embedLabel'})}
</div>
<FlexRow className="social-spaced-row social-row-right">
<div
className={classNames(
'social-label-item',
'social-label-result',
{'social-hidden': !showEmbedResult}
)}
>
{intl.formatMessage({id: 'social.embedCopiedResultText'})}
</div>
<div className="social-label-item">
<a
onClick={onCopyEmbed}
>
{intl.formatMessage({id: 'social.copyEmbedLinkText'})}
</a>
</div>
</FlexRow>
</FlexRow>
<textarea
readOnly
className="social-form social-textarea"
name="embed"
ref={textarea => setEmbedTextarea(textarea)}
value={embedHtml}
/>
</FlexRow>
</div>
</div>
</Modal>
);
};
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);

View file

@ -256,5 +256,11 @@
"comments.status.suspended": "Suspended", "comments.status.suspended": "Suspended",
"comments.status.acctdel": "Account deleted", "comments.status.acctdel": "Account deleted",
"comments.status.deleted": "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"
} }

17
src/lib/social.js Normal file
View file

@ -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 `<iframe src="https://scratch.mit.edu/projects/${projectId}/embed" ` +
'allowtransparency="true" width="485" height="402" ' +
'frameborder="0" scrolling="no" allowfullscreen></iframe>';
}
return '';
};

View file

@ -463,7 +463,7 @@
{ {
"name": "story-tutorial-redirect", "name": "story-tutorial-redirect",
"pattern": "^/story/?$", "pattern": "^/story/?$",
"redirect": "/projects/editor/?tutorial=story" "redirect": "/projects/editor/?tutorial=tell-a-story"
}, },
{ {
"name": "pong-tutorial-redirect", "name": "pong-tutorial-redirect",

View file

@ -53,7 +53,7 @@ const ConferenceSplash = () => (
src="/svgs/conference/flags/cl.svg" src="/svgs/conference/flags/cl.svg"
/> />
<div className="conf2019-panel-title-text"> <div className="conf2019-panel-title-text">
<h3>Conferencia Scratch al Sur</h3> <h3>Scratch al Sur Conferencia Chile 2019</h3>
</div> </div>
</FlexRow> </FlexRow>
<p className="conf2019-panel-desc"> <p className="conf2019-panel-desc">
@ -141,104 +141,7 @@ const ConferenceSplash = () => (
<FormattedMessage id="conference-2019.website" /> <FormattedMessage id="conference-2019.website" />
</a> </a>
</section> </section>
<section className="conf2019-panel mod-kenya"> <section className="conf2019-panel mod-uk">
<FlexRow className="conf2019-panel-title">
<img
alt="Kenya Flag"
className="conf2019-panel-flag"
src="/svgs/conference/flags/ke.svg"
/>
<div className="conf2019-panel-title-text">
<h3><FormattedMessage id="conference-2019.kenyaTitle" /></h3>
<h4><FormattedMessage id="conference-2019.kenyaSubTitle" /></h4>
</div>
</FlexRow>
<p className="conf2019-panel-desc">
<FormattedMessage id="conference-2019.kenyaDesc" />
</p>
<table className="conf2019-panel-details">
<tbody>
<tr className="conf2019-panel-row">
<td className="conf2019-panel-row-icon">
<img
alt="Calendar Icon"
className="conf2019-panel-row-icon-image"
src="/svgs/conference/index/calendar-icon-solid.svg"
/>
</td>
<td><FormattedMessage id="conference-2019.date" /></td>
<td>
<FormattedDate
day="2-digit"
month="long"
value={new Date(2019, 6, 8)}
year="numeric"
/>
{' - '}
<FormattedDate
day="2-digit"
month="long"
value={new Date(2019, 6, 13)}
year="numeric"
/>
</td>
</tr>
<tr className="conf2019-panel-row">
<td className="conf2019-panel-row-icon">
<img
alt="Map Icon"
className="conf2019-panel-row-icon-image"
src="/svgs/conference/index/map-icon-solid.svg"
/>
</td>
<td><FormattedMessage id="conference-2019.location" /></td>
<td>{'Nairobi, Kenya'}</td>
</tr>
<tr className="conf2019-panel-row">
<td className="conf2019-panel-row-icon">
<img
alt="Audience Icon"
className="conf2019-panel-row-icon-image"
src="/svgs/conference/index/audience-icon-solid.svg"
/>
</td>
<td><FormattedMessage id="conference-2019.audience" /></td>
<td><FormattedMessage id="conference-2019.kenyaAudience" /></td>
</tr>
<tr className="conf2019-panel-row">
<td className="conf2019-panel-row-icon">
<img
alt="Language Icon"
className="conf2019-panel-row-icon-image"
src="/svgs/conference/index/language-icon-solid.svg"
/>
</td>
<td><FormattedMessage id="conference-2019.language" /></td>
<td><FormattedMessage id="general.english" /></td>
</tr>
<tr className="conf2019-panel-row">
<td className="conf2019-panel-row-icon">
<img
alt="Language Icon"
className="conf2019-panel-row-icon-image"
src="/svgs/conference/index/hashtag-icon-solid.svg"
/>
</td>
<td><FormattedMessage id="conference-2019.hashtag" /></td>
<td>{'#Scratch2019AFR'}</td>
</tr>
</tbody>
</table>
<a
className="button mod-2019-conf mod-2019-conf-website-button"
href="https://www.scratchafrica.com"
rel="noopener noreferrer"
target="_blank"
>
<FormattedMessage id="conference-2019.website" />
</a>
</section>
<section className="conf2019-panel mod-uk mod-last">
<FlexRow className="conf2019-panel-title"> <FlexRow className="conf2019-panel-title">
<img <img
alt="EU Flag" alt="EU Flag"
@ -327,13 +230,28 @@ const ConferenceSplash = () => (
</table> </table>
<a <a
className="button mod-2019-conf mod-2019-conf-website-button" className="button mod-2019-conf mod-2019-conf-website-button"
href="https://www.raspberrypi.org/blog/announcing-scratch-conference-europe-2019/" href="https://scratchconferenceeurope.raspberrypi.org/"
rel="noopener noreferrer" rel="noopener noreferrer"
target="_blank" target="_blank"
> >
<FormattedMessage id="conference-2019.website" /> <FormattedMessage id="conference-2019.website" />
</a> </a>
</section> </section>
<section className="conf2019-panel mod-kenya mod-last">
<FlexRow className="conf2019-panel-title">
<img
alt="Kenya Flag"
className="conf2019-panel-flag"
src="/svgs/conference/flags/ke.svg"
/>
<div className="conf2019-panel-title-text">
<h3><FormattedMessage id="conference-2019.kenyaTitle" /></h3>
</div>
</FlexRow>
<p className="conf2019-panel-desc">
<FormattedMessage id="conference-2019.kenyaPostpone" />
</p>
</section>
</div> </div>
<div className="conf2019-title-band conf2019-mailing-list"> <div className="conf2019-title-band conf2019-mailing-list">
<img <img

View file

@ -99,6 +99,7 @@ h1.title-banner-h1.mod-2019 {
.conf2019-panel.mod-last { .conf2019-panel.mod-last {
border-bottom: 0; border-bottom: 0;
padding-bottom: 1rem;
} }
.flex-row.conf2019-panel-title { .flex-row.conf2019-panel-title {
@ -118,6 +119,12 @@ h1.title-banner-h1.mod-2019 {
margin: 2rem 0; margin: 2rem 0;
} }
.mod-kenya {
.conf2019-panel-desc {
font-style: italic;
}
}
.conf2019-mailing-list { .conf2019-mailing-list {
font-weight: normal; font-weight: normal;
} }

View file

@ -23,9 +23,10 @@
"conference-2019.kenyaTitle": "Scratch2019NBO", "conference-2019.kenyaTitle": "Scratch2019NBO",
"conference-2019.kenyaSubTitle": "Waves of Innovation", "conference-2019.kenyaSubTitle": "Waves of Innovation",
"conference-2019.kenyaDesc": "In recognition of Africa's technological contributions to the world and the potential of the youth of Africa, Scratch2019NBO will be held in Nairobi, Kenya. Join educators from around the world to share lessons, empower young people, and celebrate accomplishments in creative coding.", "conference-2019.kenyaDesc": "In recognition of Africa's technological contributions to the world and the potential of the youth of Africa, Scratch2019NBO will be held in Nairobi, Kenya. Join educators from around the world to share lessons, empower young people, and celebrate accomplishments in creative coding.",
"conference-2019.kenyaPostpone": "The Scratch2019NBO conference, originally planned for Nairobi, Kenya in July 2019, has been postponed. Information about future plans will be available later this year.",
"conference-2019.kenyaAudience": "Educators, students, and enthusiasts", "conference-2019.kenyaAudience": "Educators, students, and enthusiasts",
"conference-2019.chileDesc": "Scratch al Sur is a gathering for teachers and policy makers to learn about the importance of introducing programming languages in schools. All lectures and workshops will provide an opportunity to share different experiences, from higher levels to those who are beginning to participate in Scratch's global community.", "conference-2019.chileDesc": "Scratch al Sur Conferencia Chile 2019 is an event aimed at teachers of all educational areas and levels, who seek to innovate in the classroom through creative learning, thus supporting public policies that are promoted through the National Plan of Digital Languages, launched by the Chilean government as of 2019. Various workshops, panels, experiences, stands, a presentation of the new Scratch 3.0, Makey-Makey, and much more will be offered.",
"conference-2019.chileAudience": "Teachers and policy makers", "conference-2019.chileAudience": "Teachers and policy makers",
"conference-2019.spanishWithSimultaneous": "Spanish - simultaneous translation into English during plenary sessions" "conference-2019.spanishWithSimultaneous": "Spanish - simultaneous translation into English during plenary sessions"
} }

View file

@ -111,7 +111,10 @@ class Comment extends React.Component {
visibility visibility
} = this.props; } = this.props;
const visible = visibility === 'visible'; // we allow comments that are fully visible, or markedByFilter (flagged by
// our bad words filter, but not at the critical level of offensiveness)
const markedByFilter = visibility === 'markedbyfilter';
const visible = markedByFilter || visibility === 'visible';
let commentText = content; let commentText = content;
if (replyUsername) { if (replyUsername) {
@ -182,6 +185,7 @@ class Comment extends React.Component {
<div <div
className={classNames({ className={classNames({
'comment-bubble': true, 'comment-bubble': true,
'comment-bubble-markedbyfilter': markedByFilter,
'comment-bubble-reported': !visible 'comment-bubble-reported': !visible
})} })}
> >

View file

@ -197,6 +197,19 @@
background: $reported-background; 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 { .comment-content {

View file

@ -88,7 +88,6 @@ const PreviewPresentation = ({
onAddToStudioClicked, onAddToStudioClicked,
onAddToStudioClosed, onAddToStudioClosed,
onCloseAdminPanel, onCloseAdminPanel,
onCopyProjectLink,
onDeleteComment, onDeleteComment,
onFavoriteClicked, onFavoriteClicked,
onGreenFlag, onGreenFlag,
@ -108,6 +107,8 @@ const PreviewPresentation = ({
onSeeInside, onSeeInside,
onSetProjectThumbnailer, onSetProjectThumbnailer,
onShare, onShare,
onSocialClicked,
onSocialClosed,
onToggleComments, onToggleComments,
onToggleStudio, onToggleStudio,
onUpdateProjectId, onUpdateProjectId,
@ -126,6 +127,7 @@ const PreviewPresentation = ({
showAdminPanel, showAdminPanel,
showModInfo, showModInfo,
singleCommentId, singleCommentId,
socialOpen,
userOwnsProject, userOwnsProject,
visibilityInfo visibilityInfo
}) => { }) => {
@ -365,16 +367,19 @@ const PreviewPresentation = ({
addToStudioOpen={addToStudioOpen} addToStudioOpen={addToStudioOpen}
canReport={canReport} canReport={canReport}
isAdmin={isAdmin} isAdmin={isAdmin}
isShared={isShared}
projectInfo={projectInfo} projectInfo={projectInfo}
reportOpen={reportOpen} reportOpen={reportOpen}
shareDate={shareDate} shareDate={shareDate}
socialOpen={socialOpen}
userOwnsProject={userOwnsProject} userOwnsProject={userOwnsProject}
onAddToStudioClicked={onAddToStudioClicked} onAddToStudioClicked={onAddToStudioClicked}
onAddToStudioClosed={onAddToStudioClosed} onAddToStudioClosed={onAddToStudioClosed}
onCopyProjectLink={onCopyProjectLink}
onReportClicked={onReportClicked} onReportClicked={onReportClicked}
onReportClose={onReportClose} onReportClose={onReportClose}
onReportSubmit={onReportSubmit} onReportSubmit={onReportSubmit}
onSocialClicked={onSocialClicked}
onSocialClosed={onSocialClosed}
onToggleStudio={onToggleStudio} onToggleStudio={onToggleStudio}
/> />
</div> </div>
@ -511,16 +516,19 @@ const PreviewPresentation = ({
canAddToStudio={canAddToStudio} canAddToStudio={canAddToStudio}
canReport={canReport} canReport={canReport}
isAdmin={isAdmin} isAdmin={isAdmin}
isShared={isShared}
projectInfo={projectInfo} projectInfo={projectInfo}
reportOpen={reportOpen} reportOpen={reportOpen}
shareDate={shareDate} shareDate={shareDate}
socialOpen={socialOpen}
userOwnsProject={userOwnsProject} userOwnsProject={userOwnsProject}
onAddToStudioClicked={onAddToStudioClicked} onAddToStudioClicked={onAddToStudioClicked}
onAddToStudioClosed={onAddToStudioClosed} onAddToStudioClosed={onAddToStudioClosed}
onCopyProjectLink={onCopyProjectLink}
onReportClicked={onReportClicked} onReportClicked={onReportClicked}
onReportClose={onReportClose} onReportClose={onReportClose}
onReportSubmit={onReportSubmit} onReportSubmit={onReportSubmit}
onSocialClicked={onSocialClicked}
onSocialClosed={onSocialClosed}
onToggleStudio={onToggleStudio} onToggleStudio={onToggleStudio}
/> />
</FlexRow> </FlexRow>
@ -693,7 +701,6 @@ PreviewPresentation.propTypes = {
onAddToStudioClicked: PropTypes.func, onAddToStudioClicked: PropTypes.func,
onAddToStudioClosed: PropTypes.func, onAddToStudioClosed: PropTypes.func,
onCloseAdminPanel: PropTypes.func, onCloseAdminPanel: PropTypes.func,
onCopyProjectLink: PropTypes.func,
onDeleteComment: PropTypes.func, onDeleteComment: PropTypes.func,
onFavoriteClicked: PropTypes.func, onFavoriteClicked: PropTypes.func,
onGreenFlag: PropTypes.func, onGreenFlag: PropTypes.func,
@ -713,6 +720,8 @@ PreviewPresentation.propTypes = {
onSeeInside: PropTypes.func, onSeeInside: PropTypes.func,
onSetProjectThumbnailer: PropTypes.func, onSetProjectThumbnailer: PropTypes.func,
onShare: PropTypes.func, onShare: PropTypes.func,
onSocialClicked: PropTypes.func,
onSocialClosed: PropTypes.func,
onToggleComments: PropTypes.func, onToggleComments: PropTypes.func,
onToggleStudio: PropTypes.func, onToggleStudio: PropTypes.func,
onUpdateProjectId: PropTypes.func, onUpdateProjectId: PropTypes.func,
@ -731,6 +740,7 @@ PreviewPresentation.propTypes = {
showModInfo: PropTypes.bool, showModInfo: PropTypes.bool,
showUsernameBlockAlert: PropTypes.bool, showUsernameBlockAlert: PropTypes.bool,
singleCommentId: PropTypes.oneOfType([PropTypes.number, PropTypes.bool]), singleCommentId: PropTypes.oneOfType([PropTypes.number, PropTypes.bool]),
socialOpen: PropTypes.bool,
userOwnsProject: PropTypes.bool, userOwnsProject: PropTypes.bool,
visibilityInfo: PropTypes.shape({ visibilityInfo: PropTypes.shape({
censored: PropTypes.bool, censored: PropTypes.bool,

View file

@ -8,7 +8,6 @@ const PropTypes = require('prop-types');
const connect = require('react-redux').connect; const connect = require('react-redux').connect;
const injectIntl = require('react-intl').injectIntl; const injectIntl = require('react-intl').injectIntl;
const parser = require('scratch-parser'); const parser = require('scratch-parser');
const copy = require('clipboard-copy');
const Page = require('../../components/page/www/page.jsx'); const Page = require('../../components/page/www/page.jsx');
const storage = require('../../lib/storage.js').default; const storage = require('../../lib/storage.js').default;
@ -55,8 +54,9 @@ class Preview extends React.Component {
'fetchCommunityData', 'fetchCommunityData',
'handleAddComment', 'handleAddComment',
'handleClickLogo', 'handleClickLogo',
'handleCopyProjectLink',
'handleDeleteComment', 'handleDeleteComment',
'handleSocialClick',
'handleSocialClose',
'handleToggleStudio', 'handleToggleStudio',
'handleFavoriteToggle', 'handleFavoriteToggle',
'handleLoadMore', 'handleLoadMore',
@ -109,6 +109,7 @@ class Preview extends React.Component {
clientFaved: false, clientFaved: false,
clientLoved: false, clientLoved: false,
extensions: [], extensions: [],
socialOpen: false,
favoriteCount: 0, favoriteCount: 0,
isProjectLoaded: false, isProjectLoaded: false,
isRemixing: false, isRemixing: false,
@ -381,6 +382,12 @@ class Preview extends React.Component {
handleAddToStudioClose () { handleAddToStudioClose () {
this.setState({addToStudioOpen: false}); this.setState({addToStudioOpen: false});
} }
handleSocialClick () {
this.setState({socialOpen: true});
}
handleSocialClose () {
this.setState({socialOpen: false});
}
handleReportSubmit (formData) { handleReportSubmit (formData) {
const submit = data => this.props.reportProject(this.state.projectId, data, this.props.user.token); const submit = data => this.props.reportProject(this.state.projectId, data, this.props.user.token);
if (this.getProjectThumbnail) { if (this.getProjectThumbnail) {
@ -579,11 +586,6 @@ class Preview extends React.Component {
this.props.user.token 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) { initCounts (favorites, loves) {
this.setState({ this.setState({
favoriteCount: favorites, favoriteCount: favorites,
@ -678,13 +680,13 @@ class Preview extends React.Component {
showModInfo={this.props.isAdmin} showModInfo={this.props.isAdmin}
showUsernameBlockAlert={this.state.showUsernameBlockAlert} showUsernameBlockAlert={this.state.showUsernameBlockAlert}
singleCommentId={this.state.singleCommentId} singleCommentId={this.state.singleCommentId}
socialOpen={this.state.socialOpen}
userOwnsProject={this.props.userOwnsProject} userOwnsProject={this.props.userOwnsProject}
visibilityInfo={this.props.visibilityInfo} visibilityInfo={this.props.visibilityInfo}
onAddComment={this.handleAddComment} onAddComment={this.handleAddComment}
onAddToStudioClicked={this.handleAddToStudioClick} onAddToStudioClicked={this.handleAddToStudioClick}
onAddToStudioClosed={this.handleAddToStudioClose} onAddToStudioClosed={this.handleAddToStudioClose}
onCloseAdminPanel={this.handleCloseAdminPanel} onCloseAdminPanel={this.handleCloseAdminPanel}
onCopyProjectLink={this.handleCopyProjectLink}
onDeleteComment={this.handleDeleteComment} onDeleteComment={this.handleDeleteComment}
onFavoriteClicked={this.handleFavoriteToggle} onFavoriteClicked={this.handleFavoriteToggle}
onGreenFlag={this.handleGreenFlag} onGreenFlag={this.handleGreenFlag}
@ -704,6 +706,8 @@ class Preview extends React.Component {
onSeeInside={this.handleSeeInside} onSeeInside={this.handleSeeInside}
onSetProjectThumbnailer={this.handleSetProjectThumbnailer} onSetProjectThumbnailer={this.handleSetProjectThumbnailer}
onShare={this.handleShare} onShare={this.handleShare}
onSocialClicked={this.handleSocialClick}
onSocialClosed={this.handleSocialClose}
onToggleComments={this.handleToggleComments} onToggleComments={this.handleToggleComments}
onToggleStudio={this.handleToggleStudio} onToggleStudio={this.handleToggleStudio}
onUpdateProjectId={this.handleUpdateProjectId} onUpdateProjectId={this.handleUpdateProjectId}

View file

@ -6,7 +6,9 @@ const FlexRow = require('../../components/flex-row/flex-row.jsx');
const Button = require('../../components/forms/button.jsx'); const Button = require('../../components/forms/button.jsx');
const AddToStudioModal = require('./add-to-studio.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 ReportModal = require('../../components/modal/report/modal.jsx');
const projectShape = require('./projectshape.jsx').projectShape;
require('./subactions.scss'); require('./subactions.scss');
@ -48,12 +50,25 @@ const Subactions = props => (
)} )}
</React.Fragment> </React.Fragment>
} }
<Button {/* only show copy link button, modal if project is shared */}
className="action-button copy-link-button" {props.isShared && props.projectInfo && props.projectInfo.id && (
onClick={props.onCopyProjectLink} <React.Fragment>
> <Button
<FormattedMessage id="general.copyLink" /> className="action-button copy-link-button"
</Button> onClick={props.onSocialClicked}
>
<FormattedMessage id="general.copyLink" />
</Button>
{props.socialOpen && (
<SocialModal
isOpen
key="social-modal"
projectId={props.projectInfo && props.projectInfo.id}
onRequestClose={props.onSocialClosed}
/>
)}
</React.Fragment>
)}
{(props.canReport) && {(props.canReport) &&
<React.Fragment> <React.Fragment>
<Button <Button
@ -83,15 +98,19 @@ Subactions.propTypes = {
canAddToStudio: PropTypes.bool, canAddToStudio: PropTypes.bool,
canReport: PropTypes.bool, canReport: PropTypes.bool,
isAdmin: PropTypes.bool, isAdmin: PropTypes.bool,
isShared: PropTypes.bool,
onAddToStudioClicked: PropTypes.func, onAddToStudioClicked: PropTypes.func,
onAddToStudioClosed: PropTypes.func, onAddToStudioClosed: PropTypes.func,
onCopyProjectLink: PropTypes.func,
onReportClicked: PropTypes.func.isRequired, onReportClicked: PropTypes.func.isRequired,
onReportClose: PropTypes.func.isRequired, onReportClose: PropTypes.func.isRequired,
onReportSubmit: PropTypes.func.isRequired, onReportSubmit: PropTypes.func.isRequired,
onSocialClicked: PropTypes.func,
onSocialClosed: PropTypes.func,
onToggleStudio: PropTypes.func, onToggleStudio: PropTypes.func,
projectInfo: projectShape,
reportOpen: PropTypes.bool, reportOpen: PropTypes.bool,
shareDate: PropTypes.string, shareDate: PropTypes.string,
socialOpen: PropTypes.bool,
userOwnsProject: PropTypes.bool userOwnsProject: PropTypes.bool
}; };