Skip to content

Commit

Permalink
Merge pull request #278 from uclaacm/add-fork-button
Browse files Browse the repository at this point in the history
Add fork button
  • Loading branch information
krashanoff authored May 26, 2020
2 parents f784ed5 + bb71b97 commit e519970
Show file tree
Hide file tree
Showing 4 changed files with 135 additions and 9 deletions.
4 changes: 2 additions & 2 deletions src/components/EditorAndOutput/EditorAndOutput.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,11 @@ class EditorAndOutput extends React.Component {
viewOnly={this.props.viewOnly}
program={this.props.programid}
sketchName={this.props.sketchName}
language={this.props.language}
vlanguage={this.props.language}
handleDownload={this.handleDownload}
handleSave={this.props.handleSave}
saveText={this.props.saveText}
thumbnail={this.props.thumbnail}
vthumbnail={this.props.thumbnail}
/>
);

Expand Down
124 changes: 117 additions & 7 deletions src/components/TextEditor/components/TextEditor.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from "react";

import ReactModal from "react-modal";
import { CODEMIRROR_CONVERSIONS } from "../../../constants";
import * as fetch from "../../../lib/fetch.js";
import sketch from "../../../lib/";
Expand All @@ -12,9 +12,10 @@ import OpenPanelButtonContainer from "../../common/containers/OpenPanelButtonCon
import { EDITOR_WIDTH_BREAKPOINT } from "../../../constants";
import ViewportAwareButton from "../../common/ViewportAwareButton.js";
import DropdownButtonContainer from "../../common/containers/DropdownButtonContainer";
import { faDownload, faSave, faShare } from "@fortawesome/free-solid-svg-icons";
import { faDownload, faSave, faShare, faCodeBranch } from "@fortawesome/free-solid-svg-icons";
import { SketchThumbnailArray } from "../../Sketches/constants";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { Redirect } from "react-router-dom";

let CodeMirror = null;
if (typeof window !== "undefined" && typeof window.navigator !== "undefined") {
Expand All @@ -37,6 +38,10 @@ class TextEditor extends React.Component {
codeMirrorInstance: null,
currentLine: 0,
sketch: null,
showForkModal: false,
forking: false,
forked: false,
redirectToSketch: false,
showShareModal: false,
};
}
Expand All @@ -53,6 +58,14 @@ class TextEditor extends React.Component {
window.removeEventListener("close", this.onLeave);
};

openForkModal = () => {
this.setState({ showForkModal: true });
};

closeForkModal = () => {
this.setState({ showForkModal: false });
};

checkDirty = async () => {
if (!this.props.dirty) {
return;
Expand Down Expand Up @@ -102,6 +115,87 @@ class TextEditor extends React.Component {
this.setState({ currentLine: line });
};

renderForkModal = () => {
return (
<ReactModal
isOpen={this.state.showForkModal}
onRequestClose={this.closeForkModal}
className="fork-modal"
overlayClassName="profile-image-overlay"
ariaHideApp={false}
>
<h1 className="text-center">Fork This Sketch</h1>
{!(this.state.forking || this.state.forked) && (
<p className="text-center">Would you like to create your own copy of this sketch?</p>
)}
{this.state.forking ? (
<p className="text-center">Forking...</p>
) : this.state.forked ? (
<div>
<p className="text-center">Sketch forked! Go to your sketches to see your new copy!</p>
<Button color="danger" size="lg" onClick={this.closeForkModal} block>
Close
</Button>
<Button color="success" size="lg" onClick={this.redirectSketch} block>
Go to Sketches
</Button>
</div>
) : (
<div className="text-center">
<Button color="danger" size="lg" onClick={this.closeForkModal} block>
Cancel
</Button>
<Button color="success" size="lg" onClick={this.handleFork} block>
Fork
</Button>
</div>
)}
</ReactModal>
);
};

handleFork = async () => {
this.setState({ forking: true });
let data = {
uid: this.props.uid,
thumbnail: this.props.vthumbnail,
language: this.props.vlanguage,
name: this.props.sketchName,
code: this.props.code,
};

try {
fetch
.createSketch(data)
.then((res) => {
return res.json();
})
.then((json) => {
if (!json.ok) {
this.setState({
error: json.error || "Failed to create sketch, please try again later",
});
return;
}
this.setState({ forking: false, forked: true });
this.props.addProgram(json.data.key, json.data.programData || {});
})
.catch((err) => {
this.setState({
error: "Failed to create sketch, please try again later",
});
console.log(err);
});
} catch (err) {
console.log(err);
}
};

redirectSketch = () => {
this.closeForkModal();
this.setState({ redirectToSketch: true });
};

toggleShareModal = () => {
this.setState((prevState) => ({ showShareModal: !prevState.showShareModal }));
};
Expand All @@ -127,7 +221,8 @@ class TextEditor extends React.Component {
renderSketchName = () => <div className="program-sketch-name">{this.props.sketchName}</div>;

renderBanner = () => {
let thumbnail = SketchThumbnailArray[this.props.thumbnail];
let thumbnail =
SketchThumbnailArray[this.props.viewOnly ? this.props.vthumbnail : this.props.thumbnail];
return (
<div className="code-section-banner">
<OpenPanelButtonContainer />
Expand All @@ -144,7 +239,17 @@ class TextEditor extends React.Component {
isSmall={this.props.screenWidth <= EDITOR_WIDTH_BREAKPOINT}
/>
</div>
{this.props.viewOnly ? null : (
{this.props.viewOnly ? (
this.props.uid ? (
<ViewportAwareButton
size="lg"
onClick={this.openForkModal}
isSmall={this.props.screenWidth <= EDITOR_WIDTH_BREAKPOINT}
icon={<FontAwesomeIcon icon={faCodeBranch} />}
text={"Fork"}
/>
) : null
) : (
<ViewportAwareButton
className="mx-2"
color="success"
Expand All @@ -155,7 +260,7 @@ class TextEditor extends React.Component {
text={this.props.saveText}
/>
)}
{
{!this.props.viewOnly && (
<ViewportAwareButton
className="mx-2"
color="primary"
Expand All @@ -165,7 +270,7 @@ class TextEditor extends React.Component {
icon={<FontAwesomeIcon icon={faShare} />}
text={"Share"}
/>
}
)}
{
<Button className="mx-2" color="success" size="lg" onClick={this.props.handleDownload}>
<FontAwesomeIcon icon={faDownload} />
Expand All @@ -176,9 +281,13 @@ class TextEditor extends React.Component {
};

render() {
if (this.state.redirectToSketch === true) {
return <Redirect to="/sketches" />;
}
//json required by CodeMirror
const options = {
mode: CODEMIRROR_CONVERSIONS[this.props.language],
mode:
CODEMIRROR_CONVERSIONS[this.props.viewOnly ? this.props.vlanguage : this.props.language],
theme: this.getCMTheme(this.props.theme),
lineNumbers: true, //text editor has line numbers
lineWrapping: true, //text editor does not overflow in the x direction, uses word wrap (NOTE: it's like MO Word wrapping, so words are not cut in the middle, if a word overlaps, the whole word is brought to the next line)
Expand All @@ -189,6 +298,7 @@ class TextEditor extends React.Component {
<div className={`theme-` + this.props.theme} style={{ height: "100%" }}>
<div className="code-section">
{this.renderBanner()}
{this.renderForkModal()}
<ShareSketchModal
shareUrl={sketch.constructShareableSketchURL(this.props.mostRecentProgram)}
showModal={this.state.showShareModal}
Expand Down
4 changes: 4 additions & 0 deletions src/components/TextEditor/containers/TextEditorContainer.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import Immutable from "immutable";
import TextEditor from "../components/TextEditor";
import { connect } from "react-redux";
import { setProgramCode, setProgramDirty } from "../../../actions/programsActions.js";
import { addProgram } from "../../../actions/programsActions";
import { setMostRecentProgram } from "../../../actions/userDataActions.js";

const mapStateToProps = (state, ownProps) => {
const { uid, mostRecentProgram } = state.userData;
Expand All @@ -26,6 +28,8 @@ const mapDispatchToProps = (dispatch, ownProps) => {
dirtyCode: program => {
dispatch(setProgramDirty(program, true));
},
addProgram: (program, data) => dispatch(addProgram(program, data)),
setMostRecentProgram: value => dispatch(setMostRecentProgram(value)),
};
};

Expand Down
12 changes: 12 additions & 0 deletions src/styles/Editor.scss
Original file line number Diff line number Diff line change
Expand Up @@ -168,3 +168,15 @@
.text-editor-container::-webkit-scrollbar-thumb:hover {
background: #2f2d33;
}

.fork-modal {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 50%;
min-width: 400px;
background-color: rgb(255, 255, 255);
padding: 40px;
border-radius: 20px;
}

0 comments on commit e519970

Please sign in to comment.