resolve merging conflicts

This commit is contained in:
Linda 2018-10-03 10:54:53 -04:00
commit 437334b872
8 changed files with 658 additions and 503 deletions

View file

@ -1,10 +1,6 @@
@import "../../../colors";
@import "../../../frameless";
$small: "only screen and (max-width : #{$mobile})";
$medium: "only screen and (min-width : #{$mobile}+1) and (max-width : #{$tablet}-1)";
$small-height: "only screen and (max-height : #{$mobile})";
$medium-height: "only screen and (min-height : #{$mobile} + 1) and (max-height : #{$tablet} - 1)";
$medium-and-small: "screen and (max-width : #{$tablet}-1)";
.report-modal-header {

View file

@ -1,4 +1,3 @@
const FormattedDate = require('react-intl').FormattedDate;
const injectIntl = require('react-intl').injectIntl;
const PropTypes = require('prop-types');
const intlShape = require('react-intl').intlShape;
@ -6,7 +5,6 @@ const MediaQuery = require('react-responsive').default;
const React = require('react');
const Formsy = require('formsy-react').default;
const classNames = require('classnames');
const approx = require('approximate-number');
const GUI = require('scratch-gui').default;
const IntlGUI = injectIntl(GUI);
@ -15,14 +13,13 @@ 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 CappedNumber = require('../../components/cappednumber/cappednumber.jsx');
const ShareBanner = require('./share-banner.jsx');
const RemixCredit = require('./remix-credit.jsx');
const RemixList = require('./remix-list.jsx');
const Stats = require('./stats.jsx');
const StudioList = require('./studio-list.jsx');
const Subactions = require('./subactions.jsx');
const InplaceInput = require('../../components/forms/inplace-input.jsx');
const AddToStudioModal = require('../../components/modal/addtostudio/container.jsx');
const ReportModal = require('../../components/modal/report/modal.jsx');
const TopLevelComment = require('./comment/top-level-comment.jsx');
const ExtensionChip = require('./extension-chip.jsx');
@ -78,314 +75,275 @@ const PreviewPresentation = ({
onToggleStudio,
onSeeInside,
onUpdate
}) => {
const shareDate = ((projectInfo.history && projectInfo.history.shared)) ? projectInfo.history.shared : '';
return (
<div className="preview">
<ShareBanner shared={isShared} />
{ projectInfo && projectInfo.author && projectInfo.author.id && (
<Formsy onKeyPress={onKeyPress}>
<div className="inner">
<FlexRow className="preview-row">
<FlexRow className="project-header">
<a href={`/users/${projectInfo.author.username}`}>
<Avatar
alt={projectInfo.author.username}
src={`https://cdn2.scratch.mit.edu/get_image/user/${projectInfo.author.id}_48x48.png`}
/>
</a>
<div className="title">
{editable ?
<InplaceInput
className="project-title"
handleUpdate={onUpdate}
name="title"
validationErrors={{
maxLength: intl.formatMessage({
id: 'preview.titleMaxLength'
})
}}
validations={{
maxLength: 100
}}
value={projectInfo.title}
/> :
<React.Fragment>
<div
className="project-title no-edit"
title={projectInfo.title}
>{projectInfo.title}</div>
{'by '}
<a href={`/users/${projectInfo.author.username}`}>
{projectInfo.author.username}
</a>
</React.Fragment>
}
</div>
</FlexRow>
<div className="project-buttons">
{/* TODO: Hide Remix button for now until implemented */}
{(!userOwnsProject && false) &&
<Button className="button remix-button">
Remix
</Button>
}
<Button
className="button see-inside-button"
onClick={onSeeInside}
>
See Inside
</Button>
</div>
</FlexRow>
<FlexRow className="preview-row">
<div className="guiPlayer">
<IntlGUI
isPlayerOnly
assetHost={assetHost}
backpackOptions={backpackOptions}
basePath="/"
className="guiPlayer"
isFullScreen={isFullScreen}
previewInfoVisible="false"
projectHost={projectHost}
projectId={projectId}
}) => (
<div className="preview">
<ShareBanner shared={isShared} />
{ projectInfo && projectInfo.author && projectInfo.author.id && (
<Formsy onKeyPress={onKeyPress}>
<div className="inner">
<FlexRow className="preview-row wrap-to-col">
<FlexRow className="project-header">
<a href={`/users/${projectInfo.author.username}`}>
<Avatar
alt={projectInfo.author.username}
src={`https://cdn2.scratch.mit.edu/get_image/user/${projectInfo.author.id}_48x48.png`}
/>
</a>
<div className="title">
{editable ?
<InplaceInput
className="project-title"
handleUpdate={onUpdate}
name="title"
validationErrors={{
maxLength: intl.formatMessage({
id: 'preview.titleMaxLength'
})
}}
validations={{
maxLength: 100
}}
value={projectInfo.title}
/> :
<React.Fragment>
<div
className="project-title no-edit"
title={projectInfo.title}
>{projectInfo.title}</div>
{'by '}
<a href={`/users/${projectInfo.author.username}`}>
{projectInfo.author.username}
</a>
</React.Fragment>
}
</div>
<FlexRow className="project-notes">
<RemixCredit projectInfo={parentInfo} />
<RemixCredit projectInfo={originalInfo} />
{/* eslint-disable max-len */}
<MediaQuery maxWidth={frameless.tablet - 1}>
<FlexRow className="preview-row">
<FlexRow className="extension-list">
{extensions && extensions.map(extension => (
<ExtensionChip
extensionL10n={extension.l10nId}
extensionName={extension.name}
hasStatus={extension.hasStatus}
iconURI={extension.icon && `/svgs/project/${extension.icon}`}
key={extension.name || extension.l10nId}
/>
))}
</FlexRow>
</FlexRow>
</MediaQuery>
<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"
placeholder="Tell people how to use your project (such as which keys to press)."
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',
'last',
{remixes: parentInfo && parentInfo.author}
)}
handleUpdate={onUpdate}
name="description"
placeholder="How did you make this project? Did you use ideas scripts or artwork from other people? Thank them here."
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>
{/* eslint-enable max-len */}
</FlexRow>
</FlexRow>
<FlexRow className="preview-row">
<FlexRow className="stats">
<div
className={classNames('project-loves', {loved: loved})}
key="loves"
onClick={onLoveClicked}
>
{approx(loveCount, {decimal: false})}
</div>
<div
className={classNames('project-favorites', {favorited: faved})}
key="favorites"
onClick={onFavoriteClicked}
>
{approx(favoriteCount, {decimal: false})}
</div>
<div
className="project-remixes"
key="remixes"
>
{approx(projectInfo.stats.remixes, {decimal: false})}
</div>
<div
className="project-views"
key="views"
>
<CappedNumber value={projectInfo.stats.views} />
</div>
</FlexRow>
<FlexRow className="subactions">
<div className="share-date">
<div className="copyleft">&copy;</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">
{(isLoggedIn && userOwnsProject) &&
<React.Fragment>
<Button
className="action-button studio-button"
key="add-to-studio-button"
onClick={onAddToStudioClicked}
>
Add to Studio
</Button>,
<AddToStudioModal
isOpen={addToStudioOpen}
key="add-to-studio-modal"
studios={studios}
onRequestClose={onAddToStudioClosed}
onToggleStudio={onToggleStudio}
/>
</React.Fragment>
}
<Button className="action-button copy-link-button">
Copy Link
</Button>
{(isLoggedIn && !userOwnsProject) &&
<React.Fragment>
<Button
className="action-button report-button"
key="report-button"
onClick={onReportClicked}
>
Report
</Button>,
<ReportModal
isOpen={reportOpen}
key="report-modal"
type="project"
onReport={onReportSubmit}
onRequestClose={onReportClose}
/>
</React.Fragment>
}
</FlexRow>
</FlexRow>
</FlexRow>
<MediaQuery minWidth={frameless.tablet}>
<FlexRow className="preview-row">
<FlexRow className="extension-list">
{extensions && extensions.map(extension => (
<ExtensionChip
extensionL10n={extension.l10nId}
extensionName={extension.name}
hasStatus={extension.hasStatus}
iconURI={extension.icon && `/svgs/project/${extension.icon}`}
key={extension.name || extension.l10nId}
/>
))}
</FlexRow>
<div className="project-buttons">
{/* TODO: Hide Remix button for now until implemented */}
{(!userOwnsProject && false) &&
<Button className="button remix-button">
Remix
</Button>
}
<Button
className="button see-inside-button"
onClick={onSeeInside}
>
See Inside
</Button>
</div>
</FlexRow>
<FlexRow className="preview-row">
<div className="guiPlayer">
<IntlGUI
isPlayerOnly
assetHost={assetHost}
backpackOptions={backpackOptions}
basePath="/"
className="guiPlayer"
isFullScreen={isFullScreen}
previewInfoVisible="false"
projectHost={projectHost}
projectId={projectId}
/>
</div>
<MediaQuery maxWidth={frameless.tablet - 1}>
<FlexRow className="preview-row force-center">
<Stats
faved={faved}
favoriteCount={favoriteCount}
loveCount={loveCount}
loved={loved}
projectInfo={projectInfo}
onFavoriteClicked={onFavoriteClicked}
onLoveClicked={onLoveClicked}
/>
<Subactions
addToStudioOpen={addToStudioOpen}
isLoggedIn={isLoggedIn}
projectInfo={projectInfo}
reportOpen={reportOpen}
studios={studios}
userOwnsProject={userOwnsProject}
onAddToStudioClicked={onAddToStudioClicked}
onAddToStudioClosed={onAddToStudioClosed}
onReportClicked={onReportClicked}
onReportClose={onReportClose}
onReportSubmit={onReportSubmit}
onToggleStudio={onToggleStudio}
/>
</FlexRow>
</MediaQuery>
</div>
<div className="project-lower-container">
<div className="inner">
<FlexRow className="preview-row">
<div className="comments-container">
<FlexRow className="comments-header">
<h4>Comments</h4>
{/* TODO: Add toggle comments component and logic*/}
</FlexRow>
<FlexRow className="comments-list">
{comments.map(comment => (
<TopLevelComment
author={comment.author}
content={comment.content}
datetimeCreated={comment.datetime_created}
id={comment.id}
key={comment.id}
parentId={comment.parent_id}
projectId={projectId}
replies={replies && replies[comment.id] ? replies[comment.id] : []}
<FlexRow className="project-notes">
<RemixCredit projectInfo={parentInfo} />
<RemixCredit projectInfo={originalInfo} />
{/* eslint-disable max-len */}
<MediaQuery maxWidth={frameless.tablet - 1}>
<FlexRow className="preview-row">
<FlexRow className="extension-list">
{extensions && extensions.map(extension => (
<ExtensionChip
extensionL10n={extension.l10nId}
extensionName={extension.name}
hasStatus={extension.hasStatus}
iconURI={extension.icon && `/svgs/project/${extension.icon}`}
key={extension.name || extension.l10nId}
/>
))}
{comments.length < projectInfo.stats.comments &&
<Button
className="button load-more-button"
onClick={onLoadMore}
>
Load More
</Button>
}
</FlexRow>
</div>
<FlexRow className="column">
<RemixList remixes={remixes} />
<StudioList studios={projectStudios} />
</FlexRow>
</MediaQuery>
<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"
placeholder="Tell people how to use your project (such as which keys to press)."
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>
</div>
<FlexRow className="description-block">
<div className="project-textlabel">
Notes and Credits
</div>
{editable ?
<InplaceInput
className={classNames(
'project-description-edit',
'last',
{remixes: parentInfo && parentInfo.author}
)}
handleUpdate={onUpdate}
name="description"
placeholder="How did you make this project? Did you use ideas scripts or artwork from other people? Thank them here."
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>
{/* eslint-enable max-len */}
</FlexRow>
</FlexRow>
<MediaQuery minWidth={frameless.tablet}>
<FlexRow className="preview-row">
<Stats
faved={faved}
favoriteCount={favoriteCount}
loveCount={loveCount}
loved={loved}
projectInfo={projectInfo}
onFavoriteClicked={onFavoriteClicked}
onLoveClicked={onLoveClicked}
/>
<Subactions
addToStudioOpen={addToStudioOpen}
isLoggedIn={isLoggedIn}
projectInfo={projectInfo}
reportOpen={reportOpen}
studios={studios}
userOwnsProject={userOwnsProject}
onAddToStudioClicked={onAddToStudioClicked}
onAddToStudioClosed={onAddToStudioClosed}
onReportClicked={onReportClicked}
onReportClose={onReportClose}
onReportSubmit={onReportSubmit}
onToggleStudio={onToggleStudio}
/>
</FlexRow>
</MediaQuery>
<MediaQuery minWidth={frameless.tablet}>
<FlexRow className="preview-row">
<FlexRow className="extension-list">
{extensions && extensions.map(extension => (
<ExtensionChip
extensionL10n={extension.l10nId}
extensionName={extension.name}
hasStatus={extension.hasStatus}
iconURI={extension.icon && `/svgs/project/${extension.icon}`}
key={extension.name || extension.l10nId}
/>
))}
</FlexRow>
</FlexRow>
</MediaQuery>
</div>
<div className="project-lower-container">
<div className="inner">
<FlexRow className="preview-row">
<div className="comments-container">
<FlexRow className="comments-header">
<h4>Comments</h4>
{/* TODO: Add toggle comments component and logic*/}
</FlexRow>
<FlexRow className="comments-list">
{comments.map(comment => (
<TopLevelComment
author={comment.author}
content={comment.content}
datetimeCreated={comment.datetime_created}
id={comment.id}
key={comment.id}
parentId={comment.parent_id}
projectId={projectId}
replies={replies && replies[comment.id] ? replies[comment.id] : []}
/>
))}
{comments.length < projectInfo.stats.comments &&
<Button
className="button load-more-button"
onClick={onLoadMore}
>
Load More
</Button>
}
</FlexRow>
</div>
<FlexRow className="column">
<RemixList remixes={remixes} />
<StudioList studios={projectStudios} />
</FlexRow>
</FlexRow>
</div>
</Formsy>
)}
</div>
);
};
</div>
</Formsy>
)}
</div>
);
PreviewPresentation.propTypes = {
addToStudioOpen: PropTypes.bool,

View file

@ -23,6 +23,8 @@ const sessionActions = require('../../redux/session.js');
const navigationActions = require('../../redux/navigation.js');
const previewActions = require('../../redux/preview.js');
const frameless = require('../../lib/frameless');
const GUI = require('scratch-gui');
const IntlGUI = injectIntl(GUI.default);
@ -46,7 +48,8 @@ class Preview extends React.Component {
'handleUpdate',
'initCounts',
'pushHistory',
'renderLogin'
'renderLogin',
'setScreenFromOrientation'
]);
const pathname = window.location.pathname.toLowerCase();
const parts = pathname.split('/').filter(Boolean);
@ -63,6 +66,8 @@ class Preview extends React.Component {
};
this.getExtensions(this.state.projectId);
this.addEventListeners();
/* In the beginning, if user is on mobile and landscape, go to fullscreen */
this.setScreenFromOrientation();
}
componentDidUpdate (prevProps) {
if (this.props.sessionStatus !== prevProps.sessionStatus &&
@ -106,9 +111,26 @@ class Preview extends React.Component {
}
addEventListeners () {
window.addEventListener('popstate', this.handlePopState);
window.addEventListener('orientationchange', this.setScreenFromOrientation);
}
removeEventListeners () {
window.removeEventListener('popstate', this.handlePopState);
window.removeEventListener('orientationchange', this.setScreenFromOrientation);
}
setScreenFromOrientation () {
/*
* If the user is on a mobile device, switching to
* landscape format should make the fullscreen mode active
*/
const isMobileDevice = screen.height <= frameless.mobile || screen.width <= frameless.mobile;
if (this.props.playerMode && isMobileDevice) {
const isLandscape = screen.height < screen.width;
if (isLandscape) {
this.props.setFullScreen(true);
} else {
this.props.setFullScreen(false);
}
}
}
getExtensions (projectId) {
storage

View file

@ -6,10 +6,7 @@ $player-width: 482px;
$player-height: 406px;
$stage-width: 480px;
/* screen sizes */
$small: "screen and (max-width : #{$mobile}-1)";
$medium: "screen and (min-width : #{$mobile}) and (max-width : #{$tablet}-1)";
$big: "screen and (min-width : #{$tablet})";
$medium-and-small: "screen and (max-width : #{$tablet}-1)";
/* override view padding for share banner */
@ -33,7 +30,6 @@ $medium-and-small: "screen and (max-width : #{$tablet}-1)";
font-weight: 500;
&.has-error {
.validation-message {
right: 0;
}
@ -86,10 +82,10 @@ $medium-and-small: "screen and (max-width : #{$tablet}-1)";
}
.validation-message {
$arrow-border-width: 1rem;
display: block;
position: absolute;
z-index: 1;
$arrow-border-width: 1rem;
margin-top: $arrow-border-width;
border: 1px solid $active-gray;
border-radius: 5px;
@ -211,6 +207,15 @@ $medium-and-small: "screen and (max-width : #{$tablet}-1)";
justify-content: space-between;
align-items: flex-start;
flex-wrap: nowrap;
&.wrap-to-col {
@media #{$medium-and-small} {
width: 100%;
flex-wrap: wrap-reverse;
flex-direction: column;
justify-content: center;
}
}
}
.guiPlayer {
@ -226,6 +231,12 @@ $medium-and-small: "screen and (max-width : #{$tablet}-1)";
}
}
.force-center {
@media #{$medium-and-small} {
align-self: center;
}
}
.project-notes {
margin-left: 1rem;
height: $player-height;
@ -244,21 +255,6 @@ $medium-and-small: "screen and (max-width : #{$tablet}-1)";
}
}
.share-date {
margin-right: .75rem;
vertical-align: middle;
line-height: 2rem;
color: $type-gray;
font-size: .875rem;
}
.subactions {
margin-left: 1.5rem;
justify-content: flex-end;
align-items: flex-start;
flex: 1;
}
.remix-credit {
margin-bottom: 1rem;
border: 1px solid $ui-blue-10percent;
@ -342,146 +338,6 @@ $medium-and-small: "screen and (max-width : #{$tablet}-1)";
}
}
.copyleft {
display: inline-block;
transform: scale(-1, 1);
margin: 0;
text-align: right;
}
.stats {
line-height: 2rem;
justify-content: flex-start;
}
.project-loves,
.project-favorites,
.project-remixes,
.project-views {
display: inline;
padding-right: 2rem;
font-size: 1rem;
font-weight: bold;
&: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: -.25rem;
content: "";
}
}
.project-loves {
cursor: pointer;
&:before {
opacity: .5;
background-image: url("/svgs/project/love-gray.svg");
}
}
.project-loves.loved {
&:before {
opacity: 1;
background-image: url("/svgs/project/love-red.svg");
}
}
.project-favorites {
cursor: pointer;
&:before {
opacity: .5;
background-image: url("/svgs/project/fav-gray.svg");
}
}
.project-favorites.favorited {
&:before {
opacity: 1;
background-image: url("/svgs/project/fav-yellow.svg");
}
}
.project-remixes {
&:before {
opacity: .5;
background-image: url("/svgs/project/remix-gray.svg");
}
}
.project-views {
&:before {
opacity: .5;
background-image: url("/svgs/project/views-gray.svg");
}
}
.action-buttons {
display: flex;
margin-top: 0;
color: $type-white;
font-size: .8rem;
font-weight: 500;
justify-content: flex-end;
flex-wrap: wrap;
}
.action-button {
margin: 0 0 0 .5rem;
border-radius: 19px;
background-color: $ui-blue;
padding: 0 .75rem;
height: 2rem;
text-decoration: none;
line-height: .875rem;
font-size: .75rem;
font-weight: normal;
// &:hover {
// transition: background-color .25s ease;
// border-color: transparent;
// background-color: $active-gray;
// }
//
// &:active {
// border: 0 solid transparent;
// box-shadow: inset 0 0 5px $box-shadow-gray;
// background-color: $active-dark-gray;
// padding: calc(.75em + 1px) calc(1.5em + 1px);
// }
//
// &.report {
// border: 1px solid $ui-coral;
// background-color: $ui-coral;
//
// &:hover {
// transition: background-color .25s ease;
// border-color: transparent;
// background-color: $active-gray;
// }
//
// &:active {
// border: 0 solid transparent;
// box-shadow: inset 0 0 5px $box-shadow-gray;
// background-color: $active-dark-gray;
// padding: calc(.75em + 1px) calc(1.5em + 1px);
// }
// }
}
.comments-header {
padding: 0 0 1rem 0;
justify-content: space-between;
@ -491,42 +347,6 @@ $medium-and-small: "screen and (max-width : #{$tablet}-1)";
}
}
.studio-button,
.copy-link-button,
.report-button {
&:before {
display: inline-block;
margin-right: .25rem;
background-repeat: no-repeat;
background-position: center center;
background-size: contain;
width: .875rem;
height: .875rem;
vertical-align: bottom;
content: "";
}
}
.studio-button {
&:before {
background-image: url("/svgs/project/studio-add-white.svg");
}
}
.copy-link-button {
&:before {
background-image: url("/svgs/project/copy-link-white.svg");
}
}
.report-button {
background-color: $ui-coral;
&:before {
background-image: url("/svgs/project/report-white.svg");
}
}
.project-lower-container {
margin-top: 1rem;
background-color: $ui-blue-10percent;

View file

@ -0,0 +1,53 @@
const PropTypes = require('prop-types');
const React = require('react');
const FlexRow = require('../../components/flex-row/flex-row.jsx');
const classNames = require('classnames');
const approx = require('approximate-number');
const CappedNumber = require('../../components/cappednumber/cappednumber.jsx');
const projectShape = require('./projectshape.jsx').projectShape;
require('./stats.scss');
const Stats = props => (
<FlexRow className="stats">
<div
className={classNames('project-loves', {loved: props.loved})}
key="loves"
onClick={props.onLoveClicked}
>
{approx(props.loveCount, {decimal: false})}
</div>
<div
className={classNames('project-favorites', {favorited: props.faved})}
key="favorites"
onClick={props.onFavoriteClicked}
>
{approx(props.favoriteCount, {decimal: false})}
</div>
<div
className="project-remixes"
key="remixes"
>
{approx(props.projectInfo.stats.remixes, {decimal: false})}
</div>
<div
className="project-views"
key="views"
>
<CappedNumber value={props.projectInfo.stats.views} />
</div>
</FlexRow>
);
Stats.propTypes = {
faved: PropTypes.bool,
favoriteCount: PropTypes.number,
loveCount: PropTypes.number,
loved: PropTypes.bool,
onFavoriteClicked: PropTypes.func,
onLoveClicked: PropTypes.func,
projectInfo: projectShape
};
module.exports = Stats;

View file

@ -0,0 +1,96 @@
@import "../../frameless";
$medium-and-small: "screen and (max-width : #{$tablet}-1)";
.stats {
line-height: 2rem;
justify-content: flex-start;
@media #{$medium-and-small} {
margin: 0;
width: 100%;
justify-content: center;
flex-direction: row;
}
& > div {
@media #{$medium-and-small} {
padding: 0 1rem;
}
}
}
.project-loves,
.project-favorites,
.project-remixes,
.project-views {
display: inline;
padding-right: 2rem;
font-size: 1rem;
font-weight: bold;
&: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: -.25rem;
content: "";
}
}
.project-loves {
cursor: pointer;
&:before {
opacity: .5;
background-image: url("/svgs/project/love-gray.svg");
}
}
.project-loves.loved {
&:before {
opacity: 1;
background-image: url("/svgs/project/love-red.svg");
}
}
.project-favorites {
cursor: pointer;
&:before {
opacity: .5;
background-image: url("/svgs/project/fav-gray.svg");
}
}
.project-favorites.favorited {
&:before {
opacity: 1;
background-image: url("/svgs/project/fav-yellow.svg");
}
}
.project-remixes {
&:before {
opacity: .5;
background-image: url("/svgs/project/remix-gray.svg");
}
}
.project-views {
&:before {
opacity: .5;
background-image: url("/svgs/project/views-gray.svg");
}
}

View file

@ -0,0 +1,95 @@
const FormattedDate = require('react-intl').FormattedDate;
const PropTypes = require('prop-types');
const React = require('react');
const FlexRow = require('../../components/flex-row/flex-row.jsx');
const Button = require('../../components/forms/button.jsx');
const AddToStudioModal = require('../../components/modal/addtostudio/container.jsx');
const ReportModal = require('../../components/modal/report/modal.jsx');
const projectShape = require('./projectshape.jsx').projectShape;
require('./subactions.scss');
const Subactions = props => {
const shareDate = ((props.projectInfo.history && props.projectInfo.history.shared)) ?
props.projectInfo.history.shared : '';
return (
<FlexRow className="subactions">
<div className="share-date">
<div className="copyleft">&copy;</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">
{(props.isLoggedIn && props.userOwnsProject) &&
<React.Fragment>
<Button
className="action-button studio-button"
key="add-to-studio-button"
onClick={props.onAddToStudioClicked}
>
Add to Studio
</Button>,
<AddToStudioModal
isOpen={props.addToStudioOpen}
key="add-to-studio-modal"
studios={props.studios}
onRequestClose={props.onAddToStudioClosed}
onToggleStudio={props.onToggleStudio}
/>
</React.Fragment>
}
<Button className="action-button copy-link-button">
Copy Link
</Button>
{(props.isLoggedIn && !props.userOwnsProject) &&
<React.Fragment>
<Button
className="action-button report-button"
key="report-button"
onClick={props.onReportClicked}
>
Report
</Button>,
<ReportModal
isOpen={props.reportOpen}
key="report-modal"
type="project"
onReport={props.onReportSubmit}
onRequestClose={props.onReportClose}
/>
</React.Fragment>
}
</FlexRow>
</FlexRow>
);
};
Subactions.propTypes = {
addToStudioOpen: PropTypes.bool,
isLoggedIn: PropTypes.bool,
onAddToStudioClicked: PropTypes.func,
onAddToStudioClosed: PropTypes.func,
onReportClicked: PropTypes.func.isRequired,
onReportClose: PropTypes.func.isRequired,
onReportSubmit: PropTypes.func.isRequired,
onToggleStudio: PropTypes.func,
projectInfo: projectShape,
reportOpen: PropTypes.bool,
studios: PropTypes.arrayOf(PropTypes.object),
userOwnsProject: PropTypes.bool
};
module.exports = Subactions;

View file

@ -0,0 +1,115 @@
@import "../../colors";
@import "../../frameless";
$small: "screen and (max-width : #{$mobile}-1)";
$medium: "screen and (min-width : #{$mobile}) and (max-width : #{$tablet}-1)";
$intermediate: "screen and (min-width : #{$tablet}) and (max-width : 941px)"; /* 941 because currently breakpoint of .inner in www is 941 */
$medium-and-small: "screen and (max-width : #{$tablet}-1)";
.subactions {
margin-left: 1.5rem;
justify-content: flex-end;
align-items: flex-start;
flex: 1;
@media #{$medium-and-small} {
margin-top: 1rem;
width: 100%;
}
.share-date {
margin-right: .75rem;
vertical-align: middle;
line-height: 2rem;
color: $type-gray;
font-size: .875rem;
@media #{$small} {
margin-top: 1rem;
width: 100%;
order: 100;
}
.copyleft {
display: inline-block;
transform: scale(-1, 1);
margin: 0;
text-align: right;
@media #{$medium-and-small} {
padding: 0;
}
}
}
.action-buttons {
display: flex;
margin-top: 0;
color: $type-white;
font-size: .8rem;
font-weight: 500;
justify-content: flex-end;
flex-wrap: wrap;
.action-button {
margin: 0 0 0 .5rem;
border-radius: 19px;
background-color: $ui-blue;
padding: 0 .75rem;
height: 2rem;
text-decoration: none;
line-height: .875rem;
font-size: .75rem;
font-weight: normal;
&.studio-button,
&.copy-link-button,
&.report-button {
&:before {
display: inline-block;
margin-right: .25rem;
background-repeat: no-repeat;
background-position: center center;
background-size: contain;
width: .875rem;
height: .875rem;
vertical-align: bottom;
content: "";
}
}
&.studio-button {
&:before {
background-image: url("/svgs/project/studio-add-white.svg");
}
}
&.copy-link-button {
&:before {
background-image: url("/svgs/project/copy-link-white.svg");
}
}
&.report-button {
background-color: $ui-coral;
&:before {
background-image: url("/svgs/project/report-white.svg");
}
}
}
}
}
.subactions, .subactions .action-buttons {
@media #{$medium-and-small} {
margin: 0;
justify-content: center;
flex-direction: row;
}
& > div, .action-button {
@media #{$medium-and-small} {
padding: 0 1rem;
}
}
}