Skip to content

Commit

Permalink
View-Only Sketches and Major Main/Editor Refactoring (#219)
Browse files Browse the repository at this point in the history
Wow, it's a relief to finally write out this PR! In sum, this is the PR that implements all the necessary work for a View-Only page for each sketch (which works whether or not the user is logged in). It required a lot of code motion and refactoring, and likely has lots of side effects (so I'd prefer some semi-rigorous testing and multiple reviewers). Many thanks to @jamieliu386 and @emmyc for the solid (and gruelling) work they put into this feature!

(this supersedes #103)

Here's a semi-detailed list of what we did:
* moves the logic that combines `Editor`, `Output`, and `SplitPane` out of `Main` to new component `EditorAndOutput`; similarly moves requisite props/state and `CodeDownloader` work. necessary to remove code duplication
* creates `ViewOnly` and accompanying `ViewOnlyContainer` for view-only programs. this component manages fetching the program from the pid, handling errors, and passing down the relevant props to `EditorAndOutput`
* creates app route of `/p/:pid` for users to visit view-only sketches - directs to `ViewOnly`
* adds custom props `this.props.viewOnly`, `this.props.vLanguage` to `Output`, to process props differently depending on program state
* makes `Editor` banner behaviour (e.g. Dropdown vs. text, save button) dependent on view-only mode
* adjusts `ProfilePanel` behaviour to have Create User and Login buttons when the user isn't logged in; maintains themeing capabilities
* adjusts tests for profile panel for new behaviour (TODO: test non-logged in profile panel)
  • Loading branch information
mattxwang authored Apr 3, 2020
1 parent 3818b3c commit 650ef1d
Show file tree
Hide file tree
Showing 16 changed files with 496 additions and 188 deletions.
30 changes: 16 additions & 14 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

24 changes: 12 additions & 12 deletions src/actions/outputActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,21 @@ export function clearOutput() {
return { type: CLEAR_OUTPUT };
}

export const SET_RUN_RESULT = 'SET_RUN_RESULT'
export function setRunResult(value){
return {type: SET_RUN_RESULT, value}
export const SET_RUN_RESULT = "SET_RUN_RESULT";
export function setRunResult(value) {
return { type: SET_RUN_RESULT, value };
}

export const SET_OUTPUT_LANGUAGE = 'SET_OUTPUT_LANGAUGE'
export function setOutputLanguage(value){
return {type: SET_OUTPUT_LANGUAGE, value}
export const SET_OUTPUT_LANGUAGE = "SET_OUTPUT_LANGAUGE";
export function setOutputLanguage(value) {
return { type: SET_OUTPUT_LANGUAGE, value };
}

export const SET_OUTPUT = 'SET_OUTPUT'
export function setOutput({output}){
export const SET_OUTPUT = "SET_OUTPUT";
export function setOutput({ output }) {
//if output is not an object
if(!output){
return {type:"IGNORE"}
if (!output) {
return { type: "IGNORE" };
}
return {type:SET_OUTPUT, runResult:output.code, language: output.language}
}
return { type: SET_OUTPUT, runResult: output.code, language: output.language };
}
101 changes: 101 additions & 0 deletions src/components/EditorAndOutput/EditorAndOutput.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import React from "react";
import SplitPane from "react-split-pane";
import OutputContainer from "../Output/OutputContainer.js";
import TextEditorContainer from "../TextEditor/containers/TextEditorContainer.js";

import { EDITOR_WIDTH_BREAKPOINT, CODE_ONLY, OUTPUT_ONLY, PANEL_SIZE } from "../../constants";
import CodeDownloader from "../../util/languages/CodeDownloader";

import "codemirror/lib/codemirror.css";
import "codemirror/theme/material.css";
import "codemirror/theme/duotone-light.css";
import "styles/CustomCM.scss";
import "styles/Resizer.scss";
import "styles/Editor.scss";

class EditorAndOutput extends React.Component {
handleDownload = () => {
CodeDownloader.download(this.props.sketchName, this.props.language, this.props.code);
};

renderCodeAndOutput = () => (
<SplitPane
resizerStyle={{
height: "67px",
borderLeft: "2px solid #333",
borderRight: "2px solid #333",
width: "10px",
}}
pane1Style={this.props.pane1Style}
//functions called when you start and finish a drag
//removes and re-addsthe transition effect on the first panel when manually resizing
onDragStarted={() => this.props.changePane1Style({ pane1Style: {} })}
onDragFinished={() =>
this.props.changePane1Style({ pane1Style: { transition: "width .5s ease" } })
}
split="vertical" //the resizer is a vertical line (horizontal means resizer is a horizontal bar)
minSize={
(this.props.panelOpen ? this.props.screenWidth - PANEL_SIZE : this.props.screenWidth) * 0.33
} //minimum size of code is 33% of the remaining screen size
maxSize={
(this.props.panelOpen ? this.props.screenWidth - PANEL_SIZE : this.props.screenWidth) * 0.75
} //max size of code is 75% of the remaining screen size
size={
((this.props.panelOpen ? this.props.screenWidth - PANEL_SIZE : this.props.screenWidth) /
5) *
3
} //the initial size of the text editor section
allowResize={true}
>
{this.renderCode()}
{this.renderOutput()}
</SplitPane>
);

renderCode = () => (
<TextEditorContainer
key={this.props.mostRecentProgram}
viewMode={this.props.viewMode}
updateViewMode={this.props.updateViewMode}
screenHeight={this.props.screenHeight}
screenWidth={this.props.screenWidth}
theme={this.props.theme}
viewOnly={this.props.viewOnly}
program={this.props.programid}
sketchName={this.props.sketchName}
language={this.props.language}
handleDownload={this.handleDownload}
handleSave={this.props.handleSave}
saveText={this.props.saveText}
thumbnail={this.props.thumbnail}
/>
);

renderOutput = () => (
<OutputContainer
viewMode={this.props.viewMode}
updateViewMode={this.props.updateViewMode}
isSmall={this.props.screenWidth <= EDITOR_WIDTH_BREAKPOINT}
viewOnly={this.props.viewOnly}
vLanguage={this.props.language}
code={this.props.code}
/>
);
render = () => {
const codeStyle = {
width: this.props.screenWidth - (this.props.left || 0),
height: this.props.screenHeight,
};

switch (this.props.viewMode) {
case CODE_ONLY:
return <div style={codeStyle}>{this.renderCode()}</div>;
case OUTPUT_ONLY:
return <div style={codeStyle}>{this.renderOutput()}</div>;
default:
return this.renderCodeAndOutput();
}
};
}

export default EditorAndOutput;
118 changes: 30 additions & 88 deletions src/components/Main.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,16 @@
import React from "react";
import { Redirect } from "react-router-dom";
import SplitPane from "react-split-pane";
import OutputContainer from "./Output/OutputContainer.js";
import TextEditorContainer from "./TextEditor/containers/TextEditorContainer.js";

import * as fetch from "../lib/fetch.js";
import * as cookies from "../lib/cookies.js";
import SketchesPageContainer from "./Sketches/containers/SketchesContainer";
import "styles/Main.scss";

import ProfilePanelContainer from "./common/containers/ProfilePanelContainer";
import SketchesPageContainer from "./Sketches/containers/SketchesContainer";
import EditorAndOutput from "./EditorAndOutput/EditorAndOutput";

import { EDITOR_WIDTH_BREAKPOINT, CODE_AND_OUTPUT, CODE_ONLY, OUTPUT_ONLY } from "../constants";
import CodeDownloader from "../util/languages/CodeDownloader";
import { EDITOR_WIDTH_BREAKPOINT, CODE_AND_OUTPUT, CODE_ONLY } from "../constants";

import { PANEL_SIZE } from "../constants";
import "codemirror/lib/codemirror.css";
import "codemirror/theme/material.css";
import "codemirror/theme/duotone-light.css";
import "styles/CustomCM.scss";
import "styles/Resizer.scss";
import "styles/Editor.scss";
import "styles/Main.scss";

/**------Props-------
* togglePanel: function to call when you want the Profile Panel to disappear/reapper
Expand All @@ -39,7 +31,6 @@ class Main extends React.Component {
this.props.setTheme(cookies.getThemeFromCookie());
}

//==============React Lifecycle Functions Start===================//
componentDidUpdate(prevProps) {
if (this.props.screenWidth !== prevProps.screenWidth) {
if (this.props.screenWidth <= EDITOR_WIDTH_BREAKPOINT) {
Expand Down Expand Up @@ -84,10 +75,6 @@ class Main extends React.Component {
this.props.cleanCode(this.props.mostRecentProgram); // Set code's "dirty" state to false
};

handleDownload = () => {
CodeDownloader.download(this.props.name, this.props.language, this.props.code);
};

renderContent = () => {
switch (this.props.contentType) {
case "editor":
Expand All @@ -98,80 +85,35 @@ class Main extends React.Component {
}
};

renderCodeAndOutput = () => (
<SplitPane
resizerStyle={{
height: "67px",
borderLeft: "2px solid #333",
borderRight: "2px solid #333",
width: "10px",
}}
pane1Style={this.state.pane1Style}
//functions called when you start and finish a drag
//removes and re-addsthe transition effect on the first panel when manually resizing
onDragStarted={() => this.setState({ pane1Style: {} })}
onDragFinished={() => this.setState({ pane1Style: { transition: "width .5s ease" } })}
split="vertical" //the resizer is a vertical line (horizontal means resizer is a horizontal bar)
minSize={
(this.props.panelOpen ? this.props.screenWidth - PANEL_SIZE : this.props.screenWidth) * 0.33
} //minimum size of code is 33% of the remaining screen size
maxSize={
(this.props.panelOpen ? this.props.screenWidth - PANEL_SIZE : this.props.screenWidth) * 0.75
} //max size of code is 75% of the remaining screen size
size={
((this.props.panelOpen ? this.props.screenWidth - PANEL_SIZE : this.props.screenWidth) /
5) *
3
} //the initial size of the text editor section
allowResize={true}
>
{this.renderCode()}
{this.renderOutput()}
</SplitPane>
);

updateViewMode = viewMode => {
this.setState({ viewMode });
};

renderCode = () => (
<TextEditorContainer
key={this.props.mostRecentProgram}
renderEditor = () => (
<EditorAndOutput
// view mode
viewMode={this.state.viewMode}
updateViewMode={this.updateViewMode}
handleSave={this.handleSave}
saveText={this.state.saveText}
screenHeight={this.props.screenHeight}
screenWidth={this.props.screenWidth}
handleDownload={this.handleDownload}
updateViewMode={viewMode => this.setState({ viewMode })}
// theme
theme={this.props.theme}
// sizing
left={this.props.left}
screenWidth={this.props.screenWidth}
screenHeight={this.props.screenHeight}
// view only trigger
viewOnly={false}
// pane
panelOpen={this.props.panelOpen}
pane1Style={this.state.pane1Style}
changePane1Style={newStyle => this.setState(newStyle)}
// program information
mostRecentProgram={this.props.mostRecentProgram}
language={this.state.language}
code={this.state.code}
programid={this.props.programid}
sketchName={this.state.sketchName}
// save handler
saveText={this.state.saveText}
handleSave={this.handleSave}
/>
);

renderOutput = () => (
<OutputContainer
viewMode={this.state.viewMode}
updateViewMode={this.updateViewMode}
isSmall={this.props.screenWidth <= EDITOR_WIDTH_BREAKPOINT}
/>
);

renderEditor = () => {
const codeStyle = {
width: this.props.screenWidth - (this.props.left || 0),
height: this.props.screenHeight,
};

switch (this.state.viewMode) {
case CODE_ONLY:
return <div style={codeStyle}>{this.renderCode()}</div>;
case OUTPUT_ONLY:
return <div style={codeStyle}>{this.renderOutput()}</div>;
default:
return this.renderCodeAndOutput();
}
};

render() {
// this stops us from rendering editor with no sketches available
if (this.props.contentType === "editor" && this.props.listOfPrograms.length === 0) {
Expand Down
3 changes: 2 additions & 1 deletion src/components/Output/Output.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,8 @@ class Output extends React.Component {
};

renderOutput = () => {
let { language, runResult } = this.props;
let language = this.props.viewOnly ? this.props.vLanguage : this.props.language;
let runResult = this.props.viewOnly ? this.props.code : this.props.runResult;
const { showConsole } = this.state;

if (this.firstLoad) {
Expand Down
Loading

0 comments on commit 650ef1d

Please sign in to comment.