diff --git a/src/_colors.scss b/src/_colors.scss
index 33c5560c3..08e4cfcae 100644
--- a/src/_colors.scss
+++ b/src/_colors.scss
@@ -8,6 +8,9 @@ $ui-orange: hsla(38, 100, 55, 1); // #FFAB19 Control Primary
$ui-orange-10percent: hsla(35, 90, 55, .1);
$ui-orange-25percent: hsla(35, 90, 55, .25);
+$ui-red: hsla(20, 100%, 55%, 1); /* #FF661A */
+$ui-red-25percent: hsla(20, 100%, 55%, .25);
+
$ui-light-gray: hsla(0, 0, 98, 1); //#FAFAFA
$ui-gray: hsla(0, 0, 95, 1); //#F2F2F2
$ui-dark-gray: hsla(0, 0, 70, 1); //#B3B3B3
diff --git a/src/redux/preview.js b/src/redux/preview.js
index 9216209fb..fccdc0830 100644
--- a/src/redux/preview.js
+++ b/src/redux/preview.js
@@ -25,6 +25,7 @@ module.exports.getInitialState = () => ({
report: module.exports.Status.NOT_FETCHED,
projectStudios: module.exports.Status.NOT_FETCHED,
curatedStudios: module.exports.Status.NOT_FETCHED,
+ visibility: module.exports.Status.NOT_FETCHED,
studioRequests: {}
},
projectInfo: {},
@@ -39,7 +40,8 @@ module.exports.getInitialState = () => ({
curatedStudios: [],
currentStudioIds: [],
moreCommentsToLoad: false,
- projectNotAvailable: false
+ projectNotAvailable: false,
+ visibilityInfo: {}
});
module.exports.previewReducer = (state, action) => {
@@ -169,6 +171,10 @@ module.exports.previewReducer = (state, action) => {
return Object.assign({}, state, {
moreCommentsToLoad: action.moreCommentsToLoad
});
+ case 'SET_VISIBILITY_INFO':
+ return Object.assign({}, state, {
+ visibilityInfo: action.visibilityInfo
+ });
case 'ERROR':
log.error(action.error);
return state;
@@ -321,6 +327,11 @@ module.exports.resetComments = () => ({
type: 'RESET_COMMENTS'
});
+module.exports.setVisibilityInfo = visibilityInfo => ({
+ type: 'SET_VISIBILITY_INFO',
+ visibilityInfo: visibilityInfo
+});
+
module.exports.getProjectInfo = (id, token) => (dispatch => {
const opts = {
uri: `/projects/${id}`
@@ -343,6 +354,27 @@ module.exports.getProjectInfo = (id, token) => (dispatch => {
}
dispatch(module.exports.setFetchStatus('project', module.exports.Status.FETCHED));
dispatch(module.exports.setProjectInfo(body));
+
+ // If the project is not public, make a follow-up request for why
+ if (!body.public) {
+ dispatch(module.exports.getVisibilityInfo(id, body.author.username, token));
+ }
+ });
+});
+
+module.exports.getVisibilityInfo = (id, ownerUsername, token) => (dispatch => {
+ dispatch(module.exports.setFetchStatus('visibility', module.exports.Status.FETCHING));
+ api({
+ uri: `/users/${ownerUsername}/projects/${id}/visibility`,
+ authentication: token
+ }, (err, body, response) => {
+ if (err || !body || response.statusCode !== 200) {
+ dispatch(module.exports.setFetchStatus('visibility', module.exports.Status.ERROR));
+ dispatch(module.exports.setError('No visibility info available'));
+ return;
+ }
+ dispatch(module.exports.setFetchStatus('visibility', module.exports.Status.FETCHED));
+ dispatch(module.exports.setVisibilityInfo(body));
});
});
diff --git a/src/views/preview/banner.jsx b/src/views/preview/banner.jsx
new file mode 100644
index 000000000..c3ab9bcc6
--- /dev/null
+++ b/src/views/preview/banner.jsx
@@ -0,0 +1,34 @@
+const PropTypes = require('prop-types');
+const React = require('react');
+const classNames = require('classnames');
+const FlexRow = require('../../components/flex-row/flex-row.jsx');
+const Button = require('../../components/forms/button.jsx');
+
+require('./banner.scss');
+
+const Banner = ({className, message, actionMessage, onAction}) => (
+
+
+
+ {message}
+
+ {actionMessage && onAction && (
+
+ )}
+
+
+);
+
+Banner.propTypes = {
+ actionMessage: PropTypes.node,
+ className: PropTypes.string,
+ message: PropTypes.node.isRequired,
+ onAction: PropTypes.func
+};
+
+module.exports = Banner;
diff --git a/src/views/preview/banner.scss b/src/views/preview/banner.scss
new file mode 100644
index 000000000..8f753cabd
--- /dev/null
+++ b/src/views/preview/banner.scss
@@ -0,0 +1,36 @@
+@import "../../colors";
+
+$navigation-height: 50px;
+
+.banner-outer {
+ background-color: $ui-orange-25percent;
+ width: 100%;
+ overflow: hidden;
+ color: $ui-orange;
+ font-weight: bold;
+}
+
+.banner-outer.banner-danger {
+ background-color: $ui-red-25percent;
+ color: $ui-red;
+}
+
+.banner-inner {
+ min-height: 60px;
+ align-items: center;
+ justify-content: space-between;
+ flex-wrap: nowrap;
+}
+
+.banner-text {
+ padding: .5rem 0;
+}
+
+.banner-button {
+ background-color: $ui-orange;
+ font-size: .875rem;
+}
+
+.banner-danger .banner-button {
+ background-color: $ui-red;
+}
diff --git a/src/views/preview/l10n.json b/src/views/preview/l10n.json
index e237c2b44..7f0e429e1 100644
--- a/src/views/preview/l10n.json
+++ b/src/views/preview/l10n.json
@@ -20,5 +20,6 @@
"project.inviteToRemix": "Invite user to remix",
"project.instructionsLabel": "Instructions",
"project.notesAndCreditsLabel": "Notes and Credits",
- "project.credit": "Thanks to {userLink} for the original project {projectLink}."
+ "project.credit": "Thanks to {userLink} for the original project {projectLink}.",
+ "project.deletedBanner": "Note: This project is in the trash folder"
}
diff --git a/src/views/preview/presentation.jsx b/src/views/preview/presentation.jsx
index 30b1eb1f5..02a57b255 100644
--- a/src/views/preview/presentation.jsx
+++ b/src/views/preview/presentation.jsx
@@ -15,7 +15,7 @@ const decorateText = require('../../lib/decorate-text.jsx');
const FlexRow = require('../../components/flex-row/flex-row.jsx');
const Button = require('../../components/forms/button.jsx');
const Avatar = require('../../components/avatar/avatar.jsx');
-const ShareBanner = require('./share-banner.jsx');
+const Banner = require('./banner.jsx');
const RemixCredit = require('./remix-credit.jsx');
const RemixList = require('./remix-list.jsx');
const Stats = require('./stats.jsx');
@@ -96,17 +96,50 @@ const PreviewPresentation = ({
replies,
reportOpen,
singleCommentId,
- userOwnsProject
+ userOwnsProject,
+ visibilityInfo
}) => {
const shareDate = ((projectInfo.history && projectInfo.history.shared)) ? projectInfo.history.shared : '';
+ // Allow embedding html in banner messages coming from the server
+ const embedCensorMessage = message => (
+ // eslint-disable-next-line react/no-danger
+
+ );
+
+ let banner;
+ if (visibilityInfo.deleted) { // If both censored and deleted, prioritize deleted banner
+ banner = (}
+ />);
+ } else if (visibilityInfo.censored) {
+ if (visibilityInfo.reshareable) {
+ banner = (}
+ className="banner-danger"
+ message={embedCensorMessage(visibilityInfo.censorMessage)}
+ onAction={onShare}
+ />);
+ } else {
+ banner = ();
+ }
+ } else if (canShare && !isShared) {
+ banner = (}
+ message={}
+ onAction={onShare}
+ />);
+ }
+
return (
- {canShare && !isShared && (
-
- )}
{ projectInfo && projectInfo.author && projectInfo.author.id && (
+ {banner}
@@ -507,7 +540,13 @@ PreviewPresentation.propTypes = {
replies: PropTypes.objectOf(PropTypes.array),
reportOpen: PropTypes.bool,
singleCommentId: PropTypes.oneOfType([PropTypes.number, PropTypes.bool]),
- userOwnsProject: PropTypes.bool
+ userOwnsProject: PropTypes.bool,
+ visibilityInfo: PropTypes.shape({
+ censored: PropTypes.bool,
+ censorMessage: PropTypes.string,
+ deleted: PropTypes.bool,
+ reshareable: PropTypes.bool
+ })
};
module.exports = injectIntl(PreviewPresentation);
diff --git a/src/views/preview/preview.jsx b/src/views/preview/preview.jsx
index 47ae5ae6e..a8740f7cf 100644
--- a/src/views/preview/preview.jsx
+++ b/src/views/preview/preview.jsx
@@ -459,6 +459,7 @@ class Preview extends React.Component {
reportOpen={this.state.reportOpen}
singleCommentId={this.state.singleCommentId}
userOwnsProject={this.props.userOwnsProject}
+ visibilityInfo={this.props.visibilityInfo}
onAddComment={this.handleAddComment}
onAddToStudioClicked={this.handleAddToStudioClick}
onAddToStudioClosed={this.handleAddToStudioClose}
@@ -597,7 +598,13 @@ Preview.propTypes = {
classroomId: PropTypes.string
}),
userOwnsProject: PropTypes.bool,
- userPresent: PropTypes.bool
+ userPresent: PropTypes.bool,
+ visibilityInfo: PropTypes.shape({
+ censored: PropTypes.bool,
+ censorMessage: PropTypes.string,
+ deleted: PropTypes.bool,
+ reshareable: PropTypes.bool
+ })
};
Preview.defaultProps = {
@@ -665,7 +672,8 @@ const mapStateToProps = state => {
sessionStatus: state.session.status, // check if used
user: state.session.session.user,
userOwnsProject: userOwnsProject,
- userPresent: userPresent
+ userPresent: userPresent,
+ visibilityInfo: state.preview.visibilityInfo
};
};
diff --git a/src/views/preview/share-banner.jsx b/src/views/preview/share-banner.jsx
deleted file mode 100644
index 725cbcf19..000000000
--- a/src/views/preview/share-banner.jsx
+++ /dev/null
@@ -1,29 +0,0 @@
-const PropTypes = require('prop-types');
-const React = require('react');
-const FormattedMessage = require('react-intl').FormattedMessage;
-const FlexRow = require('../../components/flex-row/flex-row.jsx');
-const Button = require('../../components/forms/button.jsx');
-
-require('./share-banner.scss');
-
-const ShareBanner = ({onShare}) => (
-
-
-
-
-
-
-
-
-);
-
-ShareBanner.propTypes = {
- onShare: PropTypes.func
-};
-
-module.exports = ShareBanner;
diff --git a/src/views/preview/share-banner.scss b/src/views/preview/share-banner.scss
deleted file mode 100644
index 0524699f4..000000000
--- a/src/views/preview/share-banner.scss
+++ /dev/null
@@ -1,35 +0,0 @@
-@import "../../colors";
-
-$navigation-height: 50px;
-
-.share-banner-outer {
- background-color: $ui-orange-25percent;
- width: 100%;
- overflow: hidden;
- color: $ui-orange;
-}
-
-.share-banner {
- align-items: center;
- justify-content: space-between;
-}
-
-.share-button {
- background-color: $ui-orange;
- font-size: .875rem;
- font-weight: normal;
-
- // don't show an image in share button, for now.
- // &:before {
- // display: inline-block;
- // margin-right: .5rem;
- // background-image: url("/svgs/project/share-white.svg");
- // background-repeat: no-repeat;
- // background-position: center center;
- // background-size: contain;
- // width: 1.25rem;
- // height: 1.25rem;
- // vertical-align: middle;
- // content: "";
- // }
-}