first take on embed modal

This commit is contained in:
Ben Wheeler 2019-04-22 14:50:49 -04:00
parent 9587de480c
commit 1e63b51565
8 changed files with 258 additions and 214 deletions

View file

@ -54,6 +54,7 @@
"bowser": "1.9.4",
"cheerio": "1.0.0-rc.2",
"classnames": "2.2.5",
"clipboard-copy": "3.0.0",
"cookie": "0.2.2",
"copy-webpack-plugin": "0.2.0",
"create-react-class": "15.6.2",

View file

@ -2,18 +2,47 @@ const bindAll = require('lodash.bindall');
const PropTypes = require('prop-types');
const React = require('react');
const ExternalShareModalPresentation = require('./presentation.jsx');
const clipboardCopy = require('clipboard-copy');
const externalShare = require('../../../lib/external-share');
class ExternalShareModal extends React.Component {
constructor (props) {
super(props);
this.embedTextarea = {};
bindAll(this, [
'handleClickCopyEmbed',
'setEmbedTextarea'
]);
}
// componentDidMount () {
// if (this.embedTextarea) this.embedTextarea.select();
// }
componentDidUpdate () {
if (this.embedTextarea) {
console.log('selecting');
this.embedTextarea.select();
} else {
console.log('NOT selecting');
}
}
handleClickCopyEmbed () {
if (this.embedTextarea) this.embedTextarea.select();
clipboardCopy(this.embedTextarea.value);
}
setEmbedTextarea (textarea) {
this.embedTextarea = textarea;
}
render () {
const projectId = this.props.projectId;
return (
<ExternalShareModalPresentation
fbUrl={externalShare.facebookIntentLink(projectId)}
googUrl={externalShare.googleClassroomIntentLink(projectId)}
isOpen={this.props.isOpen}
setEmbedTextarea={this.setEmbedTextarea}
twitterUrl={externalShare.twitterIntentLink(projectId)}
onClickCopyEmbed={this.handleClickCopyEmbed}
onRequestClose={this.props.onRequestClose}
onSubmit={this.props.onRequestClose}
/>
);
}
@ -21,7 +50,8 @@ class ExternalShareModal extends React.Component {
ExternalShareModal.propTypes = {
isOpen: PropTypes.bool,
onRequestClose: PropTypes.func
onRequestClose: PropTypes.func,
projectId: PropTypes.string
};
module.exports = ExternalShareModal;

View file

@ -6,16 +6,17 @@
max-height: calc(100% - 8rem);
/* Some value for height must be set for scrolling to work */
height: 100%;
// height: 100%;
overflow: hidden;
@media #{$small}, #{$small-height} {
overflow: hidden;
}
// overflow: hidden;
//
// @media #{$small}, #{$small-height} {
// overflow: hidden;
// }
}
.externalShare-modal-header {
border-radius: 1rem 1rem 0 0;
box-shadow: inset 0 -1px 0 0 $ui-blue-dark;
background-color: $ui-blue;
}
@ -23,172 +24,85 @@
.externalShare-modal-content {
margin: 0 auto;
box-shadow: none;
width: 85%;
height: calc(100% - 0rem);
margin: 1rem auto;
}
.externalShare-embed-row {
justify-content: space-between;
width: 100%;
height: calc(100% - 3rem);
margin-top: .5rem;
margin-bottom: .5rem;
}
.external-target-outer-scrollbox {
position: relative;
background-color: $ui-blue-10percent;
flex: 1;
height: calc(100% - 5rem);
@media #{$small-height} {
min-height: 0;
}
}
.external-target-inner-scrollbox {
margin-right: .5rem;
.externalShare-embed-textarea {
width: calc(100% - 3rem);
height: 4rem;
padding-left: .5rem;
padding-right: .5rem;
height: 100%;
overflow: scroll;
overflow-x: hidden;
&::-webkit-scrollbar {
width: 8px;
}
&::-webkit-scrollbar-thumb {
border-radius: 4px;
background-color: $active-dark-gray;
height: 92px;
}
&::-webkit-scrollbar-track {
margin-top: 8px;
margin-bottom: 10px;
textarea {
min-height: 4rem;
}
}
.external-target-container {
display: flex;
padding: .40625rem 0 0 1.46875rem;
justify-content: flex-start;
flex-flow: row wrap;
.externalShare-copy-icon {
width: 2rem;
height: 2rem;
padding-left: .5rem;
padding-right: .5rem;
// &:before {
background-image: url("/svgs/forms/temp-copy.svg");
background-repeat: no-repeat;
background-size: contain;
// }
}
// .external-target-outer-scrollbox {
// position: relative;
// background-color: $ui-blue-10percent;
// flex: 1;
// height: calc(100% - 0rem);
//
// @media #{$small-height} {
// min-height: 0;
// }
// }
//
// .external-target-inner-scrollbox {
// margin-right: .5rem;
// padding-right: .5rem;
// height: 100%;
// overflow: scroll;
// overflow-x: hidden;
//
// &::-webkit-scrollbar {
// width: 8px;
// }
//
// &::-webkit-scrollbar-thumb {
// border-radius: 4px;
// background-color: $active-dark-gray;
// height: 92px;
// }
//
// &::-webkit-scrollbar-track {
// margin-top: 8px;
// margin-bottom: 10px;
// }
// }
//
// .external-target-container {
// display: flex;
// padding: .40625rem 0 0 1.46875rem;
// justify-content: flex-start;
// flex-flow: row wrap;
// }
/* NOTE: force scrolling: add to above:
min-height: 30rem;
*/
.external-target-bottom-gradient {
position: absolute;
right: 1rem;
bottom: 0;
left: 0;
background: linear-gradient(
$transparent-light-blue,
$ui-light-primary
);
height: 32px;
pointer-events: none; /* pass clicks through to buttons underneath */
}
.studio-selector-button {
display: flex;
position: relative;
transition: all .5s;
margin: .21875rem;
border-radius: .5rem;
background-color: $ui-white;
cursor: pointer;
padding: 0;
width: 48%;
height: 2.5rem;
justify-content: space-between;
align-items: center;
@media #{$small} {
min-width: 98%;
flex-shrink: 1;
}
}
.studio-selector-button-text {
margin: auto 2.18375rem auto .6875rem;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: .875rem;
font-weight: regular;
flex-shrink: 1;
}
.studio-selector-button-selected {
background-color: $ui-mint-green;
color: $ui-white;
}
.studio-selector-button-waiting {
background-color: $ui-light-mint;
color: $ui-white;
}
.studio-selector-button-text-selected {
color: $ui-white;
}
.studio-selector-button-text-unselected {
color: $type-gray;
}
.studio-selector-button-enabled {
pointer-events: auto;
}
.studio-selector-button-disabled {
pointer-events: none;
}
.studio-status-icon {
position: absolute;
right: .625rem;
border-radius: .75rem;
padding: .0625rem .075rem;
width: 1.5rem;
height: 1.5rem;
color: $ui-white;
box-sizing: border-box;
}
.studio-status-icon-unselected {
background-color: $ui-blue;
}
.submit-button {
background-color: $ui-blue;
}
.submit-button-waiting {
background-color: $ui-blue;
}
.studio-status-icon-plus-img,
.studio-status-icon-checkmark-img {
animation-direction: normal;
width: 1.4rem;
height: 1.4rem;
transform-origin: center;
}
.studio-status-icon-with-animation {
animation-name: bump;
animation-duration: .25s;
animation-timing-function: cubic-bezier(.3, -3, .6, 3);
animation-iteration-count: 1;
}
@keyframes bump {
0% {
transform: scale(0);
opacity: 0;
-webkit-transform: scale(0);
}
100% {
transform: scale(1);
opacity: 1;
-webkit-transform: scale(1);
}
.externalShare-form {
width: 100%;
}

View file

@ -1,27 +1,28 @@
const bindAll = require('lodash.bindall');
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 Modal = require('../base/modal.jsx');
const Form = require('../../forms/form.jsx');
const Button = require('../../forms/button.jsx');
const Modal = require('../base/modal.jsx');
// const Form = require('../../forms/form.jsx');
// const Button = require('../../forms/button.jsx');
const FlexRow = require('../../flex-row/flex-row.jsx');
const Input = require('../../components/forms/input.jsx');
const TextArea = require('../../components/forms/textarea.jsx');
// const Input = require('../../forms/input.jsx');
// const TextArea = require('../../forms/textarea.jsx');
require('../../forms/button.scss');
require('./modal.scss');
const ExternalShareModalPresentation = ({
fbUrl,
googUrl,
intl,
isOpen,
studios,
waitingToClose,
onToggleStudio,
onRequestClose,
onSubmit
projectId,
twitterUrl
}) => {
const contentLabel = intl.formatMessage({id: 'externalshare.title'});
@ -35,53 +36,63 @@ const ExternalShareModalPresentation = ({
>
<div className="externalShare-modal-header modal-header">
<div className="externalShare-content-label content-label">
{contentLabel}
Send outside Scratch
</div>
</div>
<div className="externalShare-modal-content modal-content">
{/*
<div className="external-target-outer-scrollbox">
<div className="external-target-inner-scrollbox">
<div className="external-target-container">
<Form
ref={form => {
this.form = form;
}}
onValidSubmit={this.handleValidSubmit}
>
<div>
<div className="username-label">
<b>
{this.props.intl.formatMessage({id: 'registration.createUsername'})}
</b>
{this.props.usernameHelp ? (
<p className="help-text">{this.props.usernameHelp}</p>
) : (
null
)}
</div>
<Input
name="user.username"
type="text"
onBlur={this.handleUsernameBlur}
/>
<TextArea
required
className="report-text"
elementWrapperClassName="report-modal-field"
label={null}
name="notes"
placeholder={this.lookupPrompt(this.state.category)}
value={this.state.notes}
/>
</div>
</Form>
*/}
<div className="username-label">
<b>
{this.props.intl.formatMessage({id: 'externalShare.embedHtmlContent'})}
</b>
</div>
<FlexRow className="externalShare-embed-row">
<textarea
readOnly
className="externalShare-embed-textarea"
name="embed"
ref={textarea => this.embedTextarea = textarea}
value={externalShare.embedHtml(projectId)}
onClick={this.onClickCopyEmbed}
/>
<div
className="externalShare-copy-icon"
onClick={this.onClickCopyEmbed}
/>
</FlexRow>
<FlexRow className="externalShare-embed-row">
<a
href={twitterUrl}
target="_blank"
>
Tweet
</a>
<a
href={fbUrl}
target="_blank"
>
FB Post
</a>
<a
href={googUrl}
target="_blank"
>
G classroom
</a>
</FlexRow>
{/*
</div>
<div className="external-target-bottom-gradient" />
</div>
</div>
<Form
// className="external-share"
onSubmit={onSubmit}
@ -98,16 +109,59 @@ const ExternalShareModalPresentation = ({
</Button>
</FlexRow>
</Form>
*/}
</div>
</Modal>
);
};
}
ExternalShareModalPresentation.propTypes = {
fbUrl: PropTypes.string,
googUrl: PropTypes.string,
intl: intlShape,
isOpen: PropTypes.bool,
onClickCopyEmbed: PropTypes.func,
onRequestClose: PropTypes.func,
onSubmit: PropTypes.func
projectId: PropTypes.string,
setEmbedTextarea: PropTypes.string,
twitterUrl: PropTypes.string,
};
module.exports = injectIntl(ExternalShareModalPresentation);
//
// {/*
// <Form
// className="externalShare-form"
// ref={form => {
// this.form = form;
// }}
// onSubmit={onSubmit}
// onValidSubmit={this.handleValidSubmit}
// >
// */}
// {/*
// <FlexRow className="action-buttons">
// <Button
// className="action-button close-button white"
// key="closeButton"
// name="closeButton"
// type="button"
// onClick={onRequestClose}
// >
// <div className="action-button-text">
// <FormattedMessage id="general.close" />
// </div>
// </Button>
// </FlexRow>
// */}
//
//
// {/*
// {this.props.usernameHelp ? (
// <p className="help-text">{this.props.usernameHelp}</p>
// ) : (
// null
// )}
// */}
//

View file

@ -91,6 +91,7 @@
"general.whatsHappening": "What's Happening?",
"general.wiki": "Scratch Wiki",
"general.copyLink": "Copy Link",
"general.externalShareButton": "Share outside Scratch",
"general.report": "Report",
"general.notAvailableHeadline": "Whoops! Our server is Scratch'ing its head",
"general.notAvailableSubtitle": "We couldn't find the page you're looking for. Check to make sure you've typed the URL correctly.",
@ -256,5 +257,8 @@
"comments.status.suspended": "Suspended",
"comments.status.acctdel": "Account deleted",
"comments.status.deleted": "Deleted",
"comments.status.reported": "Reported"
"comments.status.reported": "Reported",
"externalShare.title": "Share outside Scratch",
"externalShare.embedHtmlContent": "Embed this html yo"
}

37
src/lib/external-share.js Normal file
View file

@ -0,0 +1,37 @@
module.exports = {};
module.exports.embedHtml = projectId => {
return (
'<iframe allowtransparency="true" width="485" height="402" ' +
`src="//scratch.mit.edu/projects/${projectId}/embed?autostart=false" ` +
'frameborder="0" allowfullscreen></iframe>'
);
};
module.exports.twitterIntentLink = projectId => {
const baseUrl = 'https://twitter.com/intent/tweet?';
const escapedScratchUrl = `https%3A%2F%2Fscratch.mit.edu%2Fprojects%2F${projectId}`;
const escapedTweetText = 'Check%20out%20what%20you%20can%20make%20on%20Scratch%3A';
const escapedHashtags = 'creativecode';
return `${baseUrl}url=${escapedScratchUrl}&text=${escapedTweetText}&hashtags=${escapedHashtags}`;
};
module.exports.facebookIntentLink = projectId => {
const baseUrl = 'https://www.facebook.com/sharer.php?';
const escapedScratchUrl = `https%3A%2F%2Fscratch.mit.edu%2Fprojects%2F${projectId}`;
return `${baseUrl}u=${escapedScratchUrl}`;
};
// needs fb app id
module.exports.facebookIntentDialog = (scratchFBAppId, projectId) => {
const baseUrl = 'https://www.facebook.com/dialog/share?';
const escapedScratchUrl = `https%3A%2F%2Fscratch.mit.edu%2Fprojects%2F${projectId}`;
const escapedHashtag = '%23creativecode';
return `${baseUrl}app_id=${scratchFBAppId}href=${escapedScratchUrl}&hashtag=${escapedHashtag}`;
};
module.exports.googleClassroomIntentLink = projectId => {
const baseUrl = 'https://classroom.google.com/share?';
const escapedScratchUrl = `https%3A%2F%2Fscratch.mit.edu%2Fprojects%2F${projectId}`;
return (`${baseUrl}url=${escapedScratchUrl}`);
};

View file

@ -8,6 +8,7 @@ const Button = require('../../components/forms/button.jsx');
const AddToStudioModal = require('./add-to-studio.jsx');
const ExternalShareModal = require('../../components/modal/externalshare/container.jsx');
const ReportModal = require('../../components/modal/report/modal.jsx');
const projectShape = require('./projectshape.jsx').projectShape;
require('./subactions.scss');
@ -59,12 +60,13 @@ const Subactions = props => (
className="action-button external-share-button"
onClick={props.onExternalShareClicked}
>
<FormattedMessage id="general.externalShare" />
<FormattedMessage id="general.externalShareButton" />
</Button>
{props.externalShareOpen && (
{props.externalShareOpen && props.projectInfo && props.projectInfo.id && (
<ExternalShareModal
isOpen
key="external-share-modal"
projectId={props.projectInfo && props.projectInfo.id}
onRequestClose={props.onExternalShareClosed}
/>
)}
@ -107,6 +109,7 @@ Subactions.propTypes = {
onReportClose: PropTypes.func.isRequired,
onReportSubmit: PropTypes.func.isRequired,
onToggleStudio: PropTypes.func,
projectInfo: projectShape,
reportOpen: PropTypes.bool,
shareDate: PropTypes.string,
userOwnsProject: PropTypes.bool

View file

@ -3,6 +3,7 @@ const connect = require('react-redux').connect;
const defaults = require('lodash.defaultsdeep');
const PropTypes = require('prop-types');
const React = require('react');
// const formik = require('formik');
const api = require('../../lib/api');
const injectIntl = require('../../lib/intl.jsx').injectIntl;