diff --git a/src/views/preview/l10n.json b/src/views/preview/l10n.json index 7f0e429e1..eb29ba620 100644 --- a/src/views/preview/l10n.json +++ b/src/views/preview/l10n.json @@ -21,5 +21,8 @@ "project.instructionsLabel": "Instructions", "project.notesAndCreditsLabel": "Notes and Credits", "project.credit": "Thanks to {userLink} for the original project {projectLink}.", - "project.deletedBanner": "Note: This project is in the trash folder" + "project.deletedBanner": "Note: This project is in the trash folder", + "project.moderationInfoLabel": "Moderation Info", + "project.numScripts": "{number} scripts", + "project.numSprites": "{number} sprites" } diff --git a/src/views/preview/mod-info.jsx b/src/views/preview/mod-info.jsx new file mode 100644 index 000000000..2d0d4bbb1 --- /dev/null +++ b/src/views/preview/mod-info.jsx @@ -0,0 +1,58 @@ +const PropTypes = require('prop-types'); +const React = require('react'); +const FormattedDate = require('react-intl').FormattedDate; +const FormattedMessage = require('react-intl').FormattedMessage; +const FormattedTime = require('react-intl').FormattedTime; + +const FlexRow = require('../../components/flex-row/flex-row.jsx'); + +require('./mod-info.scss'); + +const ModInfo = props => ( + + + {/* eslint-disable react/jsx-sort-props */} + {props.revisedDate && +
+ + {' - '} + +
+ } + {/* eslint-enable react/jsx-sort-props */} +
+ +
+
+ +
+
+); + +ModInfo.propTypes = { + revisedDate: PropTypes.string, + scripts: PropTypes.number, + sprites: PropTypes.number +}; + +module.exports = ModInfo; diff --git a/src/views/preview/mod-info.scss b/src/views/preview/mod-info.scss new file mode 100644 index 000000000..e96a1a2c6 --- /dev/null +++ b/src/views/preview/mod-info.scss @@ -0,0 +1,64 @@ +@import "../../frameless"; + +.mod-info { + line-height: 2rem; + justify-content: flex-start; + + @media #{$medium-and-smaller} { + margin: 0; + width: 100%; + justify-content: center; + flex-direction: row; + } + + & > div { + @media #{$medium-and-smaller} { + padding: 0 1rem; + } + } +} + +.mod-date, +.mod-sprites, +.mod-scripts { + + display: inline; + padding-right: 2rem; + font-size: .875rem; + + &:before { + display: inline-block; + margin-right: .5rem; + background-repeat: no-repeat; + background-position: center center; + background-size: contain; + width: 1.5rem; + height: 1.5rem; + vertical-align: -.35rem; + content: ""; + } +} + +.mod-date { + + &:before { + opacity: .5; + background-image: url("/svgs/project/last-revised.svg"); + } +} + +.mod-sprites { + + &:before { + opacity: .5; + background-image: url("/svgs/project/sprite-count.svg"); + } +} + +.mod-scripts { + + &:before { + opacity: .5; + background-image: url("/svgs/project/block-count.svg"); + } +} diff --git a/src/views/preview/presentation.jsx b/src/views/preview/presentation.jsx index 907f5f2d9..6f20b36d4 100644 --- a/src/views/preview/presentation.jsx +++ b/src/views/preview/presentation.jsx @@ -16,6 +16,7 @@ const FlexRow = require('../../components/flex-row/flex-row.jsx'); const Button = require('../../components/forms/button.jsx'); const Avatar = require('../../components/avatar/avatar.jsx'); const Banner = require('./banner.jsx'); +const ModInfo = require('./mod-info.jsx'); const RemixCredit = require('./remix-credit.jsx'); const RemixList = require('./remix-list.jsx'); const Stats = require('./stats.jsx'); @@ -65,6 +66,7 @@ const PreviewPresentation = ({ isShared, loveCount, loved, + modInfo, moreCommentsToLoad, onAddComment, onAddToStudioClicked, @@ -96,11 +98,13 @@ const PreviewPresentation = ({ remixes, replies, reportOpen, + showModInfo, singleCommentId, userOwnsProject, visibilityInfo }) => { const shareDate = ((projectInfo.history && projectInfo.history.shared)) ? projectInfo.history.shared : ''; + const revisedDate = ((projectInfo.history && projectInfo.history.modified)) ? projectInfo.history.modified : ''; // Allow embedding html in banner messages coming from the server const embedCensorMessage = message => ( @@ -376,6 +380,21 @@ const PreviewPresentation = ({ /> + {showModInfo && + +
+ +
+ + +
+ } + + @@ -512,6 +531,10 @@ PreviewPresentation.propTypes = { isShared: PropTypes.bool, loveCount: PropTypes.number, loved: PropTypes.bool, + modInfo: PropTypes.shape({ + scripts: PropTypes.number, + sprites: PropTypes.number + }), moreCommentsToLoad: PropTypes.bool, onAddComment: PropTypes.func, onAddToStudioClicked: PropTypes.func, @@ -543,6 +566,7 @@ PreviewPresentation.propTypes = { remixes: PropTypes.arrayOf(PropTypes.object), replies: PropTypes.objectOf(PropTypes.array), reportOpen: PropTypes.bool, + showModInfo: PropTypes.bool, singleCommentId: PropTypes.oneOfType([PropTypes.number, PropTypes.bool]), userOwnsProject: PropTypes.bool, visibilityInfo: PropTypes.shape({ diff --git a/src/views/preview/project-view.jsx b/src/views/preview/project-view.jsx index cc70c42fc..b5df885de 100644 --- a/src/views/preview/project-view.jsx +++ b/src/views/preview/project-view.jsx @@ -82,6 +82,10 @@ class Preview extends React.Component { extensions: [], favoriteCount: 0, loveCount: 0, + modInfo: { + scripts: 0, + sprites: 0 + }, projectId: parts[1] === 'editor' ? '0' : parts[1], reportOpen: false, singleCommentId: singleCommentId @@ -96,7 +100,7 @@ class Preview extends React.Component { this.props.sessionStatus === sessionActions.Status.FETCHED) || (this.state.projectId !== prevState.projectId))) { this.fetchCommunityData(); - this.getExtensions(this.state.projectId); + this.getProjectData(this.state.projectId); } if (this.state.projectId === '0' && this.state.projectId !== prevState.projectId) { this.props.resetProject(); @@ -175,7 +179,7 @@ class Preview extends React.Component { } } } - getExtensions (projectId) { + getProjectData (projectId) { if (projectId > 0) { storage .load(storage.AssetType.Project, projectId, storage.DataFormat.JSON) @@ -205,14 +209,29 @@ class Preview extends React.Component { } }); } + const sprites = projectData[0].targets.length - 1; // don't count stage + const scripts = projectData[0].targets + .map(target => + Object.values(target.blocks) + .filter(block => block.topLevel).length + ) + .reduce((accumulator, currentVal) => accumulator + currentVal, 0); this.setState({ - extensions: Array.from(extensionSet) + extensions: Array.from(extensionSet), + modInfo: { + scripts: scripts, + sprites: sprites + } }); }); }); } else { // projectId is default or invalid; empty the extensions array this.setState({ - extensions: [] + extensions: [], + modInfo: { + scripts: 0, + sprites: 0 + } }); } } @@ -454,6 +473,7 @@ class Preview extends React.Component { isShared={this.props.isShared} loveCount={this.state.loveCount} loved={this.props.loved} + modInfo={this.state.modInfo} moreCommentsToLoad={this.props.moreCommentsToLoad} originalInfo={this.props.original} parentInfo={this.props.parent} @@ -464,6 +484,7 @@ class Preview extends React.Component { remixes={this.props.remixes} replies={this.props.replies} reportOpen={this.state.reportOpen} + showModInfo={this.props.isAdmin} singleCommentId={this.state.singleCommentId} userOwnsProject={this.props.userOwnsProject} visibilityInfo={this.props.visibilityInfo} diff --git a/static/svgs/project/block-count.svg b/static/svgs/project/block-count.svg new file mode 100644 index 000000000..8d1ca6bfd --- /dev/null +++ b/static/svgs/project/block-count.svg @@ -0,0 +1,18 @@ + + + + Moderation/Block Count + Created with Sketch. + + + + + + + + + + + + + \ No newline at end of file diff --git a/static/svgs/project/last-revised.svg b/static/svgs/project/last-revised.svg new file mode 100644 index 000000000..e760cf814 --- /dev/null +++ b/static/svgs/project/last-revised.svg @@ -0,0 +1,18 @@ + + + + Moderation/Last Revised + Created with Sketch. + + + + + + + + + + + + + \ No newline at end of file diff --git a/static/svgs/project/sprite-count.svg b/static/svgs/project/sprite-count.svg new file mode 100644 index 000000000..989b06509 --- /dev/null +++ b/static/svgs/project/sprite-count.svg @@ -0,0 +1,18 @@ + + + + Moderation/Sprite Count + Created with Sketch. + + + + + + + + + + + + + \ No newline at end of file