mirror of
https://github.com/scratchfoundation/scratch-www.git
synced 2024-11-27 01:25:52 -05:00
Report first pass
Report button opens a modal with the report project form. All the content is currently in `preview/presentation` because components cannot have their own l10n files.
This commit is contained in:
parent
bd391b422a
commit
34042c9e8e
6 changed files with 568 additions and 336 deletions
|
@ -18,8 +18,9 @@ $ui-border: hsla(0, 0, 85, 1); //#D9D9D9
|
|||
|
||||
/* 3.0 colors */
|
||||
/* Using www naming convention for now, should be consistent with gui */
|
||||
$ui-green: hsla(163, 83, 40, 1); //#0fbd8c Pen Primary
|
||||
$ui-coral: hsla(350, 100, 70, 1); //#FF6680 More Priamry
|
||||
$ui-green: hsla(163, 83, 40, 1); // #0fbd8c Pen Primary
|
||||
$ui-coral: hsla(350, 100, 70, 1); // #FF6680 More Primary
|
||||
$ui-coral-dark: hsla(350, 100, 60, 1); // #FF3355 More tertiary
|
||||
$ui-blue-10percent: hsla(215, 100, 65, .1);
|
||||
$ui-orange-25percent: hsla(35, 90, 55, .25);
|
||||
|
||||
|
|
34
src/components/modal/report/modal.jsx
Normal file
34
src/components/modal/report/modal.jsx
Normal file
|
@ -0,0 +1,34 @@
|
|||
const PropTypes = require('prop-types');
|
||||
const React = require('react');
|
||||
const Modal = require('../base/modal.jsx');
|
||||
|
||||
require('../../forms/button.scss');
|
||||
require('./modal.scss');
|
||||
|
||||
const ReportModal = props => (
|
||||
<Modal
|
||||
className="mod-report"
|
||||
{...props}
|
||||
>
|
||||
<div>
|
||||
<div className="report-modal-header">
|
||||
<div className="report-content-label">
|
||||
{props.contentLabel}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="report-modal-content">
|
||||
{props.children}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</Modal>
|
||||
);
|
||||
|
||||
ReportModal.propTypes = {
|
||||
children: PropTypes.node,
|
||||
contentLabel: PropTypes.string,
|
||||
onRequestClose: PropTypes.func
|
||||
};
|
||||
|
||||
module.exports = ReportModal;
|
41
src/components/modal/report/modal.scss
Normal file
41
src/components/modal/report/modal.scss
Normal file
|
@ -0,0 +1,41 @@
|
|||
@import "../../../colors";
|
||||
@import "../../../frameless";
|
||||
|
||||
.mod-report * {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.mod-report {
|
||||
margin: 100px auto;
|
||||
width: 30rem;
|
||||
outline: none;
|
||||
padding: 0;
|
||||
user-select: none;
|
||||
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.report-modal-header {
|
||||
height: 3rem;
|
||||
padding-top: .75rem;
|
||||
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
background-color: $ui-coral;
|
||||
box-shadow: inset 0 -1px 0 0 $ui-coral-dark;
|
||||
}
|
||||
|
||||
.report-content-label {
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
font-size: 1rem;
|
||||
font-weight: bold;
|
||||
color: $type-white;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.report-modal-content {
|
||||
width: 80%;
|
||||
margin: 1rem auto;
|
||||
font-size: .875rem;
|
||||
line-height: 1.5rem;
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
const bindAll = require('lodash.bindall');
|
||||
const FormattedDate = require('react-intl').FormattedDate;
|
||||
const injectIntl = require('react-intl').injectIntl;
|
||||
const intlShape = require('react-intl').intlShape;
|
||||
|
@ -13,329 +14,499 @@ const sessionActions = require('../../redux/session.js');
|
|||
const decorateText = require('../../lib/decorate-text.jsx');
|
||||
const FlexRow = require('../../components/flex-row/flex-row.jsx');
|
||||
const Button = require('../../components/forms/button.jsx');
|
||||
const Form = require('../../components/forms/form.jsx');
|
||||
const Input = require('../../components/forms/input.jsx');
|
||||
const Select = require('../../components/forms/select.jsx');
|
||||
const Spinner = require('../../components/spinner/spinner.jsx');
|
||||
const TextArea = require('../../components/forms/textarea.jsx');
|
||||
const Avatar = require('../../components/avatar/avatar.jsx');
|
||||
const CappedNumber = require('../../components/cappednumber/cappednumber.jsx');
|
||||
const ShareBanner = require('../../components/share-banner/share-banner.jsx');
|
||||
const ThumbnailColumn = require('../../components/thumbnailcolumn/thumbnailcolumn.jsx');
|
||||
const InplaceInput = require('../../components/forms/inplace-input.jsx');
|
||||
const ReportModal = require('../../components/modal/report/modal.jsx');
|
||||
|
||||
|
||||
require('./preview.scss');
|
||||
|
||||
const PreviewPresentation = props => {
|
||||
const {
|
||||
editable,
|
||||
faved,
|
||||
favoriteCount,
|
||||
intl,
|
||||
isFullScreen,
|
||||
loved,
|
||||
loveCount,
|
||||
originalInfo,
|
||||
parentInfo,
|
||||
projectId,
|
||||
projectInfo,
|
||||
remixes,
|
||||
sessionStatus,
|
||||
studios,
|
||||
user,
|
||||
onFavoriteClicked,
|
||||
onLoveClicked,
|
||||
onSeeInside,
|
||||
onUpdate
|
||||
// ...otherProps TBD
|
||||
} = props;
|
||||
const shareDate = (projectInfo.history && projectInfo.history.shared) ? projectInfo.history.shared : '';
|
||||
return (
|
||||
<div className="preview">
|
||||
{projectInfo.history && shareDate === '' &&
|
||||
<ShareBanner>
|
||||
<FlexRow className="preview-row">
|
||||
<span className="share-text">
|
||||
This project is not shared — so only you can see it. Click share to let everyone see it!
|
||||
</span>
|
||||
<Button className="button share-button">
|
||||
Share
|
||||
</Button>
|
||||
</FlexRow>
|
||||
</ShareBanner>
|
||||
}
|
||||
|
||||
{ projectInfo && projectInfo.author && projectInfo.author.id && (
|
||||
<div className="inner">
|
||||
<Formsy>
|
||||
class PreviewPresentation extends React.Component {
|
||||
constructor (props) {
|
||||
super(props);
|
||||
bindAll(this, [
|
||||
'handleReportClick',
|
||||
'handleReportClose',
|
||||
'handleReportReasonSelect',
|
||||
'handleReportSubmit'
|
||||
]);
|
||||
this.state = {
|
||||
reportOpen: false,
|
||||
reportPrompt: 'Select a reason why above.',
|
||||
reportReason: ''
|
||||
};
|
||||
}
|
||||
handleReportClick (e) {
|
||||
e.preventDefault();
|
||||
this.setState({reportOpen: true});
|
||||
}
|
||||
handleReportClose () {
|
||||
this.setState({reportOpen: false});
|
||||
}
|
||||
handleReportReasonSelect (name, value) {
|
||||
const prompts = [
|
||||
'Please provide a link to the original project',
|
||||
'Please provide links to the uncredited content',
|
||||
'Please say why the project is too violent or scary',
|
||||
'Please say where the inappropriate language occurs in the project (For example: Notes & Credits, sprite name, project text, etc.)',
|
||||
'Please say the name of the audio file with the inappropriate music',
|
||||
'Please say where the personal contact information is shared (For example: Notes & Credits, sprite name, project text, etc.)',
|
||||
'Please be specific about why this project does not follow our Community Guidelines',
|
||||
'not used',
|
||||
'Please say the name of the sprite or the backdrop with the inappropriate image'
|
||||
];
|
||||
this.setState({reportPrompt: prompts[value]});
|
||||
}
|
||||
handleReportSubmit (formData) {
|
||||
console.log('submit report data', formData);
|
||||
this.setState({reportOpen: false});
|
||||
}
|
||||
render () {
|
||||
const {
|
||||
editable,
|
||||
faved,
|
||||
favoriteCount,
|
||||
intl,
|
||||
isFullScreen,
|
||||
loved,
|
||||
loveCount,
|
||||
originalInfo,
|
||||
parentInfo,
|
||||
projectId,
|
||||
projectInfo,
|
||||
remixes,
|
||||
sessionStatus,
|
||||
studios,
|
||||
user,
|
||||
onFavoriteClicked,
|
||||
onLoveClicked,
|
||||
onSeeInside,
|
||||
onUpdate
|
||||
// ...otherProps TBD
|
||||
} = this.props;
|
||||
const shareDate = (projectInfo.history && projectInfo.history.shared) ? projectInfo.history.shared : '';
|
||||
return (
|
||||
<div className="preview">
|
||||
{projectInfo.history && shareDate === '' &&
|
||||
<ShareBanner>
|
||||
<FlexRow className="preview-row">
|
||||
<FlexRow className="project-header">
|
||||
<Avatar
|
||||
src={`https://cdn2.scratch.mit.edu/get_image/user/${projectInfo. author.id}_48x48.png`}
|
||||
/>
|
||||
<div className="title">
|
||||
{editable ?
|
||||
|
||||
<InplaceInput
|
||||
className="project-title"
|
||||
handleUpdate={onUpdate}
|
||||
name="title"
|
||||
validationErrors={{
|
||||
maxLength: 'Sorry title is too long'
|
||||
// maxLength: props.intl.formatMessage({
|
||||
// id: 'project.titleMaxLength'
|
||||
// })
|
||||
}}
|
||||
validations={{
|
||||
// TODO: actual 100
|
||||
maxLength: 40
|
||||
}}
|
||||
value={projectInfo.title}
|
||||
/> :
|
||||
<div className="project-title">{projectInfo.title}</div>
|
||||
<span className="share-text">
|
||||
This project is not shared — so only you can see it. Click share to let everyone see it!
|
||||
</span>
|
||||
<Button className="button share-button">
|
||||
Share
|
||||
</Button>
|
||||
</FlexRow>
|
||||
</ShareBanner>
|
||||
}
|
||||
|
||||
{ projectInfo && projectInfo.author && projectInfo.author.id && (
|
||||
<div className="inner">
|
||||
<Formsy>
|
||||
<FlexRow className="preview-row">
|
||||
<FlexRow className="project-header">
|
||||
<Avatar
|
||||
src={`https://cdn2.scratch.mit.edu/get_image/user/${projectInfo. author.id}_48x48.png`}
|
||||
/>
|
||||
<div className="title">
|
||||
{editable ?
|
||||
|
||||
<InplaceInput
|
||||
className="project-title"
|
||||
handleUpdate={onUpdate}
|
||||
name="title"
|
||||
validationErrors={{
|
||||
maxLength: 'Sorry title is too long'
|
||||
// maxLength: props.intl.formatMessage({
|
||||
// id: 'project.titleMaxLength'
|
||||
// })
|
||||
}}
|
||||
validations={{
|
||||
// TODO: actual 100
|
||||
maxLength: 40
|
||||
}}
|
||||
value={projectInfo.title}
|
||||
/> :
|
||||
<div className="project-title">{projectInfo.title}</div>
|
||||
}
|
||||
{`${intl.formatMessage({id: 'thumbnail.by'})} `}
|
||||
<a href={`/users/${projectInfo.author.username}`}>
|
||||
{projectInfo.author.username}
|
||||
</a>
|
||||
</div>
|
||||
</FlexRow>
|
||||
<div className="project-buttons">
|
||||
{sessionStatus === sessionActions.Status.FETCHED &&
|
||||
Object.keys(user).length > 0 &&
|
||||
user.id !== projectInfo.author.id &&
|
||||
<Button className="button remix-button">
|
||||
Remix
|
||||
</Button>
|
||||
}
|
||||
{`${intl.formatMessage({id: 'thumbnail.by'})} `}
|
||||
<a href={`/users/${projectInfo.author.username}`}>
|
||||
{projectInfo.author.username}
|
||||
</a>
|
||||
</div>
|
||||
</FlexRow>
|
||||
<div className="project-buttons">
|
||||
{sessionStatus === sessionActions.Status.FETCHED &&
|
||||
Object.keys(user).length > 0 &&
|
||||
user.id !== projectInfo.author.id &&
|
||||
<Button className="button remix-button">
|
||||
Remix
|
||||
<Button
|
||||
className="button see-inside-button"
|
||||
onClick={onSeeInside}
|
||||
>
|
||||
See Inside
|
||||
</Button>
|
||||
}
|
||||
<Button
|
||||
className="button see-inside-button"
|
||||
onClick={onSeeInside}
|
||||
>
|
||||
See Inside
|
||||
</Button>
|
||||
</div>
|
||||
</FlexRow>
|
||||
<FlexRow className="preview-row">
|
||||
<div className="guiPlayer">
|
||||
<IntlGUI
|
||||
isPlayerOnly
|
||||
basePath="/"
|
||||
className="guiPlayer"
|
||||
isFullScreen={isFullScreen}
|
||||
previewInfoVisible="false"
|
||||
projectId={projectId}
|
||||
/>
|
||||
</div>
|
||||
<FlexRow className="project-notes">
|
||||
{parentInfo && parentInfo.author && parentInfo.id && (
|
||||
<FlexRow className="remix-credit">
|
||||
<Avatar
|
||||
className="remix"
|
||||
src={`https://cdn2.scratch.mit.edu/get_image/user/${parentInfo.author.id}_48x48.png`}
|
||||
/>
|
||||
<div className="credit-text">
|
||||
Thanks to <a
|
||||
href={`/users/${parentInfo.author.username}`}
|
||||
>
|
||||
{parentInfo.author.username}
|
||||
</a> for the original project <a
|
||||
href={`/preview/${parentInfo.id}`}
|
||||
>
|
||||
{parentInfo.title}
|
||||
</a>.
|
||||
</div>
|
||||
</FlexRow>
|
||||
)}
|
||||
{originalInfo && originalInfo.author && originalInfo.id && (
|
||||
<FlexRow className="remix-credit">
|
||||
<Avatar
|
||||
className="remix"
|
||||
src={`https://cdn2.scratch.mit.edu/get_image/user/${originalInfo.author.id}_48x48.png`}
|
||||
/>
|
||||
<div className="credit-text">
|
||||
Thanks to <a
|
||||
href={`/users/${originalInfo.author.username}`}
|
||||
>
|
||||
{originalInfo.author.username}
|
||||
</a> for the original project <a
|
||||
href={`/preview/${originalInfo.id}`}
|
||||
>
|
||||
{originalInfo.title}
|
||||
</a>.
|
||||
</div>
|
||||
</FlexRow>
|
||||
)}
|
||||
|
||||
<FlexRow className="description-block">
|
||||
<div className="project-textlabel">
|
||||
Instructions
|
||||
</div>
|
||||
{editable ?
|
||||
<InplaceInput
|
||||
className={classNames(
|
||||
'project-description-edit',
|
||||
{remixes: parentInfo && parentInfo.author}
|
||||
)}
|
||||
handleUpdate={onUpdate}
|
||||
name="instructions"
|
||||
type="textarea"
|
||||
validationErrors={{
|
||||
maxLength: 'Sorry description is too long'
|
||||
// maxLength: props.intl.formatMessage({
|
||||
// id: 'project.descriptionMaxLength'
|
||||
// })
|
||||
}}
|
||||
validations={{
|
||||
// TODO: actual 5000
|
||||
maxLength: 1000
|
||||
}}
|
||||
value={projectInfo.instructions}
|
||||
/> :
|
||||
<div className="project-description">
|
||||
{decorateText(projectInfo.instructions)}
|
||||
</div>
|
||||
}
|
||||
</FlexRow>
|
||||
<FlexRow className="description-block">
|
||||
<div className="project-textlabel">
|
||||
Notes and Credits
|
||||
</div>
|
||||
{editable ?
|
||||
<InplaceInput
|
||||
className={classNames(
|
||||
'project-description-edit',
|
||||
{remixes: parentInfo && parentInfo.author}
|
||||
)}
|
||||
handleUpdate={onUpdate}
|
||||
name="description"
|
||||
type="textarea"
|
||||
validationErrors={{
|
||||
maxLength: 'Sorry description is too long'
|
||||
// maxLength: props.intl.formatMessage({
|
||||
// id: 'project.descriptionMaxLength'
|
||||
// })
|
||||
}}
|
||||
validations={{
|
||||
// TODO: actual 5000
|
||||
maxLength: 1000
|
||||
}}
|
||||
value={projectInfo.description}
|
||||
/> :
|
||||
<div className="project-description last">
|
||||
{decorateText(projectInfo.description)}
|
||||
</div>
|
||||
}
|
||||
</FlexRow>
|
||||
</FlexRow>
|
||||
</FlexRow>
|
||||
<FlexRow className="preview-row">
|
||||
<FlexRow className="stats">
|
||||
<div
|
||||
className={classNames('project-loves', {loved: loved})}
|
||||
key="loves"
|
||||
onClick={onLoveClicked}
|
||||
>
|
||||
{loveCount}
|
||||
</div>
|
||||
<div
|
||||
className={classNames('project-favorites', {favorited: faved})}
|
||||
key="favorites"
|
||||
onClick={onFavoriteClicked}
|
||||
>
|
||||
{favoriteCount}
|
||||
</div>
|
||||
<div
|
||||
className="project-remixes"
|
||||
key="remixes"
|
||||
>
|
||||
{projectInfo.remix.count}
|
||||
</div>
|
||||
<div
|
||||
className="project-views"
|
||||
key="views"
|
||||
>
|
||||
<CappedNumber value={projectInfo.stats.views} />
|
||||
</div>
|
||||
</FlexRow>
|
||||
<FlexRow className="subactions">
|
||||
<div className="share-date">
|
||||
<div className="copyleft">©</div>
|
||||
{' '}
|
||||
{/* eslint-disable react/jsx-sort-props */}
|
||||
{shareDate === null ?
|
||||
'Unshared' :
|
||||
<FormattedDate
|
||||
value={Date.parse(shareDate)}
|
||||
day="2-digit"
|
||||
month="short"
|
||||
year="numeric"
|
||||
/>
|
||||
}
|
||||
{/* eslint-enable react/jsx-sort-props */}
|
||||
<FlexRow className="preview-row">
|
||||
<div className="guiPlayer">
|
||||
<IntlGUI
|
||||
isPlayerOnly
|
||||
basePath="/"
|
||||
className="guiPlayer"
|
||||
isFullScreen={isFullScreen}
|
||||
previewInfoVisible="false"
|
||||
projectId={projectId}
|
||||
/>
|
||||
</div>
|
||||
<FlexRow className="project-notes">
|
||||
{parentInfo && parentInfo.author && parentInfo.id && (
|
||||
<FlexRow className="remix-credit">
|
||||
<Avatar
|
||||
className="remix"
|
||||
src={`https://cdn2.scratch.mit.edu/get_image/user/${parentInfo.author.id}_48x48.png`}
|
||||
/>
|
||||
<div className="credit-text">
|
||||
Thanks to <a
|
||||
href={`/users/${parentInfo.author.username}`}
|
||||
>
|
||||
{parentInfo.author.username}
|
||||
</a> for the original project <a
|
||||
href={`/preview/${parentInfo.id}`}
|
||||
>
|
||||
{parentInfo.title}
|
||||
</a>.
|
||||
</div>
|
||||
</FlexRow>
|
||||
)}
|
||||
{originalInfo && originalInfo.author && originalInfo.id && (
|
||||
<FlexRow className="remix-credit">
|
||||
<Avatar
|
||||
className="remix"
|
||||
src={`https://cdn2.scratch.mit.edu/get_image/user/${originalInfo.author.id}_48x48.png`}
|
||||
/>
|
||||
<div className="credit-text">
|
||||
Thanks to <a
|
||||
href={`/users/${originalInfo.author.username}`}
|
||||
>
|
||||
{originalInfo.author.username}
|
||||
</a> for the original project <a
|
||||
href={`/preview/${originalInfo.id}`}
|
||||
>
|
||||
{originalInfo.title}
|
||||
</a>.
|
||||
</div>
|
||||
</FlexRow>
|
||||
)}
|
||||
|
||||
<FlexRow className="description-block">
|
||||
<div className="project-textlabel">
|
||||
Instructions
|
||||
</div>
|
||||
{editable ?
|
||||
<InplaceInput
|
||||
className={classNames(
|
||||
'project-description-edit',
|
||||
{remixes: parentInfo && parentInfo.author}
|
||||
)}
|
||||
handleUpdate={onUpdate}
|
||||
name="instructions"
|
||||
type="textarea"
|
||||
validationErrors={{
|
||||
maxLength: 'Sorry description is too long'
|
||||
// maxLength: props.intl.formatMessage({
|
||||
// id: 'project.descriptionMaxLength'
|
||||
// })
|
||||
}}
|
||||
validations={{
|
||||
// TODO: actual 5000
|
||||
maxLength: 1000
|
||||
}}
|
||||
value={projectInfo.instructions}
|
||||
/> :
|
||||
<div className="project-description">
|
||||
{decorateText(projectInfo.instructions)}
|
||||
</div>
|
||||
}
|
||||
</FlexRow>
|
||||
<FlexRow className="description-block">
|
||||
<div className="project-textlabel">
|
||||
Notes and Credits
|
||||
</div>
|
||||
{editable ?
|
||||
<InplaceInput
|
||||
className={classNames(
|
||||
'project-description-edit',
|
||||
{remixes: parentInfo && parentInfo.author}
|
||||
)}
|
||||
handleUpdate={onUpdate}
|
||||
name="description"
|
||||
type="textarea"
|
||||
validationErrors={{
|
||||
maxLength: 'Sorry description is too long'
|
||||
// maxLength: props.intl.formatMessage({
|
||||
// id: 'project.descriptionMaxLength'
|
||||
// })
|
||||
}}
|
||||
validations={{
|
||||
// TODO: actual 5000
|
||||
maxLength: 1000
|
||||
}}
|
||||
value={projectInfo.description}
|
||||
/> :
|
||||
<div className="project-description last">
|
||||
{decorateText(projectInfo.description)}
|
||||
</div>
|
||||
}
|
||||
</FlexRow>
|
||||
</FlexRow>
|
||||
</FlexRow>
|
||||
<FlexRow className="preview-row">
|
||||
<FlexRow className="stats">
|
||||
<div
|
||||
className={classNames('project-loves', {loved: loved})}
|
||||
key="loves"
|
||||
onClick={onLoveClicked}
|
||||
>
|
||||
{loveCount}
|
||||
</div>
|
||||
<div
|
||||
className={classNames('project-favorites', {favorited: faved})}
|
||||
key="favorites"
|
||||
onClick={onFavoriteClicked}
|
||||
>
|
||||
{favoriteCount}
|
||||
</div>
|
||||
<div
|
||||
className="project-remixes"
|
||||
key="remixes"
|
||||
>
|
||||
{projectInfo.remix.count}
|
||||
</div>
|
||||
<div
|
||||
className="project-views"
|
||||
key="views"
|
||||
>
|
||||
<CappedNumber value={projectInfo.stats.views} />
|
||||
</div>
|
||||
</FlexRow>
|
||||
<FlexRow className="subactions">
|
||||
<div className="share-date">
|
||||
<div className="copyleft">©</div>
|
||||
{' '}
|
||||
{/* eslint-disable react/jsx-sort-props */}
|
||||
{shareDate === null ?
|
||||
'Unshared' :
|
||||
<FormattedDate
|
||||
value={Date.parse(shareDate)}
|
||||
day="2-digit"
|
||||
month="short"
|
||||
year="numeric"
|
||||
/>
|
||||
}
|
||||
{/* eslint-enable react/jsx-sort-props */}
|
||||
</div>
|
||||
|
||||
<FlexRow className="action-buttons">
|
||||
<Button className="action-button studio-button">
|
||||
Add to Studio
|
||||
</Button>
|
||||
<Button className="action-button copy-link-button">
|
||||
Copy Link
|
||||
</Button>
|
||||
<Button className="action-button report-button">
|
||||
Report
|
||||
</Button>
|
||||
<FlexRow className="action-buttons">
|
||||
<Button className="action-button studio-button">
|
||||
Add to Studio
|
||||
</Button>
|
||||
<Button className="action-button copy-link-button">
|
||||
Copy Link
|
||||
</Button>
|
||||
{
|
||||
sessionStatus === sessionActions.Status.FETCHED &&
|
||||
Object.keys(user).length > 0 &&
|
||||
user.id !== projectInfo.author.id && [
|
||||
<Button
|
||||
className="action-button report-button"
|
||||
key="report-button"
|
||||
onClick={this.handleReportClick}
|
||||
>
|
||||
Report
|
||||
</Button>,
|
||||
<ReportModal
|
||||
contentLabel="Report Project"
|
||||
isOpen={this.state.reportOpen}
|
||||
key="report-modal"
|
||||
onRequestClose={this.handleReportClose}
|
||||
onSubmit={this.handleReportSubmit}
|
||||
>
|
||||
From the dropdown below, please select the reason why you feel this project is disrespectful or inappropriate or otherwise breaks the Scratch Community Guidelines.
|
||||
<Form
|
||||
className="report"
|
||||
onSubmit={this.handleReportSubmit}
|
||||
>
|
||||
<Input
|
||||
name="projectId"
|
||||
type="hidden"
|
||||
value={projectId}
|
||||
/>
|
||||
<Input
|
||||
name="username"
|
||||
type="hidden"
|
||||
value={user.username}
|
||||
/>
|
||||
<Select
|
||||
required
|
||||
name="reason"
|
||||
options={[
|
||||
{
|
||||
value: '',
|
||||
label: 'Select a reason'
|
||||
},
|
||||
{
|
||||
value: '0',
|
||||
label: 'Exact Copy of Project'
|
||||
},
|
||||
{
|
||||
value: '1',
|
||||
label: 'Uses Image/Music Without Credit'
|
||||
},
|
||||
{
|
||||
value: '2',
|
||||
label: 'Too Violent or Scary'
|
||||
},
|
||||
{
|
||||
value: '3',
|
||||
label: 'Inappropriate Language'
|
||||
},
|
||||
{
|
||||
value: '4',
|
||||
label: 'Inappropriate Music'
|
||||
},
|
||||
{
|
||||
value: '8',
|
||||
label: 'Inappropriate Images'
|
||||
},
|
||||
{
|
||||
value: '5',
|
||||
label: 'Sharing Personal Contact Information'
|
||||
},
|
||||
{
|
||||
value: '6',
|
||||
label: 'Other'
|
||||
}
|
||||
]}
|
||||
value={this.state.reportReason}
|
||||
onChange={this.handleReportReasonSelect}
|
||||
/>
|
||||
<TextArea
|
||||
required
|
||||
className="report-text"
|
||||
name="reportText"
|
||||
placeholder={this.state.reportPrompt}
|
||||
validationErrors={{
|
||||
maxLength: 'That\'s too long! Please find a way to shorten your text.',
|
||||
minLength: 'That\'s too short. Please describe in detail what\'s inappropriate or disrespectful about the project.'
|
||||
}}
|
||||
validations={{
|
||||
// TODO find out max and min characters
|
||||
maxLength: 500,
|
||||
minLength: 30
|
||||
}}
|
||||
/>
|
||||
{this.state.reportWaiting ? [
|
||||
<Button
|
||||
className="submit-button white"
|
||||
disabled="disabled"
|
||||
key="submitButton"
|
||||
type="submit"
|
||||
>
|
||||
<Spinner />
|
||||
</Button>
|
||||
] : [
|
||||
<Button
|
||||
className="submit-button white"
|
||||
key="submitButton"
|
||||
type="submit"
|
||||
>
|
||||
Send
|
||||
</Button>
|
||||
]}
|
||||
</Form>
|
||||
</ReportModal>
|
||||
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
</FlexRow>
|
||||
</FlexRow>
|
||||
|
||||
</FlexRow>
|
||||
|
||||
</FlexRow>
|
||||
<FlexRow className="preview-row">
|
||||
<div className="comments-container">
|
||||
<div className="project-title">
|
||||
Comments go here
|
||||
<FlexRow className="preview-row">
|
||||
<div className="comments-container">
|
||||
<div className="project-title">
|
||||
Comments go here
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<FlexRow className="column">
|
||||
<FlexRow className="remix-list">
|
||||
<div className="project-title">
|
||||
Remixes
|
||||
</div>
|
||||
{remixes && remixes.length === 0 ? (
|
||||
<span>No remixes</span>
|
||||
) : (
|
||||
<ThumbnailColumn
|
||||
cards
|
||||
showAvatar
|
||||
itemType="preview"
|
||||
items={remixes.slice(0, 5)}
|
||||
showFavorites={false}
|
||||
showLoves={false}
|
||||
showViews={false}
|
||||
/>
|
||||
<FlexRow className="column">
|
||||
{/* hide remixes if there aren't any */}
|
||||
{remixes && remixes.length !== 0 && (
|
||||
<FlexRow className="remix-list">
|
||||
<div className="project-title">
|
||||
Remixes
|
||||
</div>
|
||||
{remixes && remixes.length === 0 ? (
|
||||
// TODO: style remix invitation
|
||||
<span>Invite user to remix</span>
|
||||
) : (
|
||||
<ThumbnailColumn
|
||||
cards
|
||||
showAvatar
|
||||
itemType="preview"
|
||||
items={remixes.slice(0, 5)}
|
||||
showFavorites={false}
|
||||
showLoves={false}
|
||||
showViews={false}
|
||||
/>
|
||||
)}
|
||||
</FlexRow>
|
||||
)}
|
||||
</FlexRow>
|
||||
<FlexRow className="studio-list">
|
||||
<div className="project-title">
|
||||
Studios
|
||||
</div>
|
||||
{studios && studios.length === 0 ? (
|
||||
<span>No studios</span>
|
||||
) : (
|
||||
<ThumbnailColumn
|
||||
cards
|
||||
showAvatar
|
||||
itemType="gallery"
|
||||
items={studios.slice(0, 5)}
|
||||
showFavorites={false}
|
||||
showLoves={false}
|
||||
showViews={false}
|
||||
/>
|
||||
{/* hide studios if there aren't any */}
|
||||
{studios && studios.length !== 0 && (
|
||||
<FlexRow className="studio-list">
|
||||
<div className="project-title">
|
||||
Studios
|
||||
</div>
|
||||
{studios && studios.length === 0 ? (
|
||||
// TODO: invite user to add to studio?
|
||||
<span>None </span>
|
||||
) : (
|
||||
<ThumbnailColumn
|
||||
cards
|
||||
showAvatar
|
||||
itemType="gallery"
|
||||
items={studios.slice(0, 5)}
|
||||
showFavorites={false}
|
||||
showLoves={false}
|
||||
showViews={false}
|
||||
/>
|
||||
)}
|
||||
</FlexRow>
|
||||
)}
|
||||
</FlexRow>
|
||||
</FlexRow>
|
||||
</FlexRow>
|
||||
</Formsy>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
);
|
||||
};
|
||||
</Formsy>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
PreviewPresentation.propTypes = {
|
||||
editable: PropTypes.bool,
|
||||
|
@ -389,17 +560,6 @@ PreviewPresentation.propTypes = {
|
|||
root: PropTypes.number
|
||||
})
|
||||
}),
|
||||
editable: PropTypes.bool,
|
||||
faved: PropTypes.bool,
|
||||
favoriteCount: PropTypes.number,
|
||||
intl: intlShape,
|
||||
isFullScreen: PropTypes.bool,
|
||||
loveCount: PropTypes.number,
|
||||
loved: PropTypes.bool,
|
||||
onFavoriteClicked: PropTypes.func,
|
||||
onLoveClicked: PropTypes.func,
|
||||
onSeeInside: PropTypes.func,
|
||||
onUpdate: PropTypes.func,
|
||||
projectId: PropTypes.string,
|
||||
projectInfo: PropTypes.shape({
|
||||
id: PropTypes.number,
|
||||
|
|
|
@ -69,6 +69,19 @@ class Preview extends React.Component {
|
|||
componentWillUnmount () {
|
||||
this.removeEventListeners();
|
||||
}
|
||||
initState () {
|
||||
const pathname = window.location.pathname.toLowerCase();
|
||||
const parts = pathname.split('/').filter(Boolean);
|
||||
// parts[0]: 'preview'
|
||||
// parts[1]: either :id or 'editor'
|
||||
// parts[2]: undefined if no :id, otherwise either 'editor' or 'fullscreen'
|
||||
return {
|
||||
editable: false,
|
||||
favoriteCount: 0,
|
||||
loveCount: 0,
|
||||
projectId: parts[1] === 'editor' ? 0 : parts[1]
|
||||
};
|
||||
}
|
||||
addEventListeners () {
|
||||
window.addEventListener('popstate', this.handlePopState);
|
||||
}
|
||||
|
@ -108,19 +121,6 @@ class Preview extends React.Component {
|
|||
);
|
||||
}
|
||||
}
|
||||
initState () {
|
||||
const pathname = window.location.pathname.toLowerCase();
|
||||
const parts = pathname.split('/').filter(Boolean);
|
||||
// parts[0]: 'preview'
|
||||
// parts[1]: either :id or 'editor'
|
||||
// parts[2]: undefined if no :id, otherwise either 'editor' or 'fullscreen'
|
||||
return {
|
||||
editable: false,
|
||||
favoriteCount: 0,
|
||||
loveCount: 0,
|
||||
projectId: parts[1] === 'editor' ? 0 : parts[1]
|
||||
};
|
||||
}
|
||||
handleFavoriteToggle () {
|
||||
this.props.setFavedStatus(
|
||||
!this.props.faved,
|
||||
|
@ -269,15 +269,6 @@ Preview.propTypes = {
|
|||
root: PropTypes.number
|
||||
})
|
||||
}),
|
||||
faved: PropTypes.bool,
|
||||
fullScreen: PropTypes.bool,
|
||||
getCreditInfo: PropTypes.func.isRequired,
|
||||
getFavedStatus: PropTypes.func.isRequired,
|
||||
getLovedStatus: PropTypes.func.isRequired,
|
||||
getProjectInfo: PropTypes.func.isRequired,
|
||||
getRemixes: PropTypes.func.isRequired,
|
||||
getStudios: PropTypes.func.isRequired,
|
||||
loved: PropTypes.bool,
|
||||
playerMode: PropTypes.bool,
|
||||
projectInfo: PropTypes.shape({
|
||||
author: PropTypes.shape({
|
||||
|
|
|
@ -462,3 +462,8 @@ $stage-height: 406px;
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.report-text textarea {
|
||||
// override min-height from default settings (for teacher registration)
|
||||
min-height: 8rem;
|
||||
}
|
Loading…
Reference in a new issue