diff --git a/src/components/Output/Output.js b/src/components/Output/Output.js
index acc3913e..402f7a35 100644
--- a/src/components/Output/Output.js
+++ b/src/components/Output/Output.js
@@ -1,9 +1,10 @@
import React from "react";
-import { PYTHON, HTML, PROCESSING } from "../../constants";
+import { PYTHON, HTML, PROCESSING, REACT } from "../../constants";
import { OUTPUT_ONLY } from "../../constants";
import EditorRadio from "../TextEditor/components/EditorRadio.js";
import CreateProcessingDoc from "../Output/Processing";
import CreatePythonDoc from "../Output/Python";
+import CreateReactDoc from "../Output/React";
import { Button } from "reactstrap";
import ViewportAwareButton from "../common/ViewportAwareButton.js";
import OpenPanelButtonContainer from "../common/containers/OpenPanelButtonContainer.js";
@@ -99,6 +100,9 @@ class Output extends React.Component {
case PROCESSING:
srcDocFunc = () => CreateProcessingDoc(runResult, showConsole);
break;
+ case REACT:
+ srcDocFunc = () => CreateReactDoc(runResult, showConsole);
+ break;
case PYTHON:
runResult = btoa(runResult);
srcDocFunc = () => CreatePythonDoc(runResult, showConsole);
diff --git a/src/components/Output/Processing.js b/src/components/Output/Processing.js
index 90857b4d..3d8628da 100644
--- a/src/components/Output/Processing.js
+++ b/src/components/Output/Processing.js
@@ -1,39 +1,4 @@
-const getProcessingSrcDocLoggingScript = () => `
-
- `;
+import { getJsSrcDocLoggingScript } from "./constants";
const getUserScript = code => `
+
+
+
+
+
+ `;
+
+const getUserScript = (code) => `
+
+`;
+
+const getReactSrcDocBody = (code, showConsole) => `
+
+ ${
+ showConsole
+ ? ``
+ : ``
+ }
+ ${getJsSrcDocLoggingScript()}
+ ${getUserScript(code)}
+
+
+ `;
+
+export default function (code, showConsole) {
+ return ` ${getReactSrcDocHead()} ${getReactSrcDocBody(code, showConsole)}`;
+}
diff --git a/src/components/Output/constants/index.js b/src/components/Output/constants/index.js
new file mode 100644
index 00000000..59342ec7
--- /dev/null
+++ b/src/components/Output/constants/index.js
@@ -0,0 +1,36 @@
+export const getJsSrcDocLoggingScript = () => `
+
+`;
diff --git a/src/components/Sketches/components/ConfirmDeleteModal.js b/src/components/Sketches/components/ConfirmDeleteModal.js
index 29870508..66997de8 100644
--- a/src/components/Sketches/components/ConfirmDeleteModal.js
+++ b/src/components/Sketches/components/ConfirmDeleteModal.js
@@ -20,13 +20,10 @@ class ConfirmDeleteModal extends React.Component {
fetch
.deleteSketch(data)
.then((res) => {
- return res.json();
- })
- .then((json) => {
- if (!json.ok) {
+ if (!res.ok) {
this.setState({
spinner: false,
- error: json.error || "Failed to create sketch, please try again later",
+ error: res.text() || "Failed to delete sketch, please try again later",
});
return;
}
diff --git a/src/components/Sketches/components/CreateSketchModal.js b/src/components/Sketches/components/CreateSketchModal.js
index defc34c4..d8d19752 100644
--- a/src/components/Sketches/components/CreateSketchModal.js
+++ b/src/components/Sketches/components/CreateSketchModal.js
@@ -1,6 +1,6 @@
import React from "react";
import DropdownButton from "./DropdownButton";
-import ImageSelector from "../../common/ImageSelector"
+import ImageSelector from "../../common/ImageSelector";
import {
SketchThumbnailArray,
LanguageDropdownValues,
@@ -46,7 +46,7 @@ class CreateSketchModal extends React.Component {
});
};
- setNext = val => {
+ setNext = (val) => {
this.setState({
next: val,
error: "",
@@ -111,7 +111,7 @@ class CreateSketchModal extends React.Component {
return false;
};
- onFirstSubmit = e => {
+ onFirstSubmit = (e) => {
e.preventDefault();
if (this.badNameInput() || this.badLanguageInput()) {
return;
@@ -119,7 +119,7 @@ class CreateSketchModal extends React.Component {
this.setNext(true);
};
- onSecondSubmit = async e => {
+ onSecondSubmit = async (e) => {
e.preventDefault();
if (this.badThumbnailInput()) return;
@@ -135,23 +135,18 @@ class CreateSketchModal extends React.Component {
try {
fetch
.createSketch(data)
- .then(res => {
+ .then((res) => {
+ if (!res.ok) throw new Error(`Failed to create user! Got status ${res.status}`);
return res.json();
})
- .then(json => {
- if (!json.ok) {
- this.setState({
- disableSubmit: false,
- error: json.error || "Failed to create sketch, please try again later",
- });
- return;
- }
- this.props.addProgram(json.data.key, json.data.programData || {});
- this.props.setMostRecentProgram(json.data.key);
+ .then((json) => {
+ const { uid, ...programData } = json;
+ this.props.addProgram(uid, programData || {});
+ this.props.setMostRecentProgram(uid);
this.setState({ redirect: true });
this.closeModal();
})
- .catch(err => {
+ .catch((err) => {
this.setState({
disableSubmit: false,
error: "Failed to create sketch, please try again later",
@@ -245,7 +240,7 @@ class CreateSketchModal extends React.Component {
this.setState({ name: e.target.value })}
+ onChange={(e) => this.setState({ name: e.target.value })}
value={this.state.name}
id="sketch-name"
/>
@@ -259,7 +254,7 @@ class CreateSketchModal extends React.Component {
this.setState({ language: lang })}
+ onSelect={(lang) => this.setState({ language: lang })}
displayValue={this.state.language.display || LanguageDropdownDefault.display}
/>
diff --git a/src/components/Sketches/components/EditSketchModal.js b/src/components/Sketches/components/EditSketchModal.js
index 749aa91e..2777928e 100644
--- a/src/components/Sketches/components/EditSketchModal.js
+++ b/src/components/Sketches/components/EditSketchModal.js
@@ -80,7 +80,7 @@ class EditSketchModal extends React.Component {
return false;
};
- handleSubmitEdit = async e => {
+ handleSubmitEdit = async (e) => {
e.preventDefault();
if (this.badNameInput() || this.badLanguageInput()) {
@@ -109,29 +109,27 @@ class EditSketchModal extends React.Component {
try {
fetch
.updatePrograms(this.props.uid, updateData)
- .then(res => {
- return res.json();
- })
- .then(json => {
- if (!json.ok) {
+ .then((res) => {
+ if (res.ok) {
+ if (this.state.newLanguage !== -1) {
+ this.props.setProgramLanguage(this.props.sketchKey, this.state.newLanguage.value);
+ }
+ if (this.state.newName !== -1) {
+ this.props.setProgramName(this.props.sketchKey, this.state.newName);
+ }
+ if (this.state.newThumbnail !== -1) {
+ this.props.setProgramThumbnail(this.props.sketchKey, this.state.newThumbnail);
+ }
+ this.closeModal();
+ } else {
this.setState({
disableSubmit: false,
- error: json.error || "Failed to edit sketch, please try again later",
+ error: res.text() || "Failed to edit sketch, please try again later",
});
return;
}
- if (this.state.newLanguage !== -1) {
- this.props.setProgramLanguage(this.props.sketchKey, this.state.newLanguage.value);
- }
- if (this.state.newName !== -1) {
- this.props.setProgramName(this.props.sketchKey, this.state.newName);
- }
- if (this.state.newThumbnail !== -1) {
- this.props.setProgramThumbnail(this.props.sketchKey, this.state.newThumbnail);
- }
- this.closeModal();
})
- .catch(err => {
+ .catch((err) => {
this.setState({
disableSubmit: false,
error: "Failed to edit sketch, please try again later",
@@ -176,7 +174,7 @@ class EditSketchModal extends React.Component {
this.setState({ newName: e.target.value })}
+ onChange={(e) => this.setState({ newName: e.target.value })}
value={this.state.newName !== -1 ? this.state.newName : this.props.sketchName}
id="sketch-name"
/>
@@ -190,7 +188,7 @@ class EditSketchModal extends React.Component {
this.setState({ newLanguage: lang })}
+ onSelect={(lang) => this.setState({ newLanguage: lang })}
displayValue={
this.state.newLanguage !== -1
? this.state.newLanguage.display
@@ -289,7 +287,7 @@ class EditSketchModal extends React.Component {
/>
);
return (
- {
+ if (!res.ok) throw new Error(`Failed to create sketch! Got status ${res.status}.`);
return res.json();
})
.then((json) => {
- if (!json.ok) {
- this.setState({
- error: json.error || "Failed to create sketch, please try again later",
- });
- return;
- }
+ const { uid, ...programData } = json;
this.setState({ forking: false, forked: true });
- this.props.addProgram(json.data.key, json.data.programData || {});
+ this.props.addProgram(uid, programData || {});
})
.catch((err) => {
this.setState({
diff --git a/src/components/common/DropdownButton.js b/src/components/common/DropdownButton.js
index f640e4fd..537b6a0a 100644
--- a/src/components/common/DropdownButton.js
+++ b/src/components/common/DropdownButton.js
@@ -1,8 +1,8 @@
import React from "react";
import { Dropdown, DropdownToggle, DropdownMenu, DropdownItem } from "reactstrap";
import { faCogs } from "@fortawesome/free-solid-svg-icons";
-import { faPython } from "@fortawesome/free-brands-svg-icons";
-import { faHtml5 } from "@fortawesome/free-brands-svg-icons";
+import { faPython, faHtml5, faReact } from "@fortawesome/free-brands-svg-icons";
+import { PYTHON, PROCESSING, REACT, HTML } from "../../constants";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
/**--------Props---------------
@@ -25,11 +25,11 @@ export default class DropdownButton extends React.Component {
//==============React Lifecycle Functions===================//
componentDidMount() {}
- toggleHandler = prevVal => {
+ toggleHandler = (prevVal) => {
this.setState({ dropdownOpen: !prevVal });
};
- selectLanguage = program => {
+ selectLanguage = (program) => {
let result = true;
if (this.props.dirty) {
result = window.confirm("Are you sure you want to change programs? You have unsaved changes");
@@ -42,16 +42,19 @@ export default class DropdownButton extends React.Component {
renderDropdownItems = () => {
//map each program string in the array to a dropdown item
- return this.props.dropdownItems.map(program => {
+ return this.props.dropdownItems.map((program) => {
let faLanguage;
switch (program.language) {
- case "python":
+ case PYTHON:
faLanguage = faPython;
break;
- case "processing":
+ case PROCESSING:
faLanguage = faCogs;
break;
- case "html":
+ case REACT:
+ faLanguage = faReact;
+ break;
+ case HTML:
default:
faLanguage = faHtml5;
}
@@ -72,13 +75,16 @@ export default class DropdownButton extends React.Component {
let faLanguage;
switch (this.props.currentLanguage) {
- case "python":
+ case PYTHON:
faLanguage = faPython;
break;
- case "processing":
+ case PROCESSING:
faLanguage = faCogs;
break;
- case "html":
+ case REACT:
+ faLanguage = faReact;
+ break;
+ case HTML:
default:
faLanguage = faHtml5;
}
diff --git a/src/constants/index.js b/src/constants/index.js
index 24de4654..58ffeaf9 100644
--- a/src/constants/index.js
+++ b/src/constants/index.js
@@ -4,8 +4,9 @@ const PYTHON = "python";
const JAVASCRIPT = "javascript";
const HTML = "html";
const PROCESSING = "processing";
+const REACT = "react";
-const SUPPORTED_LANGUAGES = [PYTHON, JAVASCRIPT, HTML, PROCESSING];
+const SUPPORTED_LANGUAGES = [PYTHON, JAVASCRIPT, HTML, PROCESSING, REACT];
//used for syntax highlighting in editor
let CODEMIRROR_CONVERSIONS = {};
@@ -21,6 +22,8 @@ SUPPORTED_LANGUAGES.forEach((lang) => {
return (CODEMIRROR_CONVERSIONS[lang] = "htmlmixed");
case PROCESSING:
return (CODEMIRROR_CONVERSIONS[lang] = "javascript");
+ case REACT:
+ return (CODEMIRROR_CONVERSIONS[lang] = "jsx");
default:
console.error("SUPPORTED LANGUAGE WITH NO MODE");
}
@@ -51,10 +54,10 @@ const ROUTER_BASE_NAME = "/";
var SERVER_URL = "http://localhost:8081";
if (process && process.env) {
if (process.env.REACT_APP_SERVER_TYPE === "staging") {
- SERVER_URL = "https://teach-la-staging-backend.herokuapp.com";
+ SERVER_URL = "https://tla-backend-staging.herokuapp.com";
}
if (process.env.REACT_APP_SERVER_TYPE === "prod") {
- SERVER_URL = "https://teach-la-backend.herokuapp.com";
+ SERVER_URL = "https://tla-backend-prod.herokuapp.com";
}
}
@@ -76,6 +79,7 @@ module.exports = {
JAVASCRIPT,
HTML,
PROCESSING,
+ REACT,
// photo names
PHOTO_NAMES,
diff --git a/src/lib/fetch.js b/src/lib/fetch.js
index 3d9b3e62..16d4871f 100644
--- a/src/lib/fetch.js
+++ b/src/lib/fetch.js
@@ -16,7 +16,7 @@ import constants from "../constants";
*/
export const getUserData = async (uid = "", includePrograms = false) => {
const getUserDataEndpoint = (uid = "", includePrograms = false) =>
- `${constants.SERVER_URL}/getUserData/${uid}${includePrograms ? "?programs=true" : ""}`;
+ `${constants.SERVER_URL}/user/get?uid=${uid}${includePrograms ? "&programs=true" : ""}`;
const options = {
method: "get",
@@ -24,12 +24,19 @@ export const getUserData = async (uid = "", includePrograms = false) => {
};
try {
- let result = await fetch(getUserDataEndpoint(uid, includePrograms), options);
- let { ok, data, error } = await result.json();
-
+ const result = await fetch(getUserDataEndpoint(uid, includePrograms), options);
+ const status = await result.status;
+ const ok = status === 200;
+ if (status === 404) {
+ await createUser(uid);
+ return getUserData(uid, includePrograms);
+ }
+ let data = ok ? await result.json() : {};
+ let error = !ok ? await result.text() : "";
return { ok, data, error };
} catch (err) {
- return { ok: "false", error: "SERVER ERROR: Unable to get user data from server", err: err };
+ await createUser(uid);
+ return getUserData(uid, includePrograms);
}
};
@@ -48,7 +55,7 @@ const makeServerRequest = (data, endpoint, method = "post") => {
},
};
- if (method === "post" || method === "put") {
+ if (method !== "get") {
let body = "";
// if the passed-in data object has at least 1 key, set the body to the stringified data object
try {
@@ -72,8 +79,13 @@ const makeServerRequest = (data, endpoint, method = "post") => {
*/
export const updatePrograms = (uid = "", programs) => {
- const endpoint = `updatePrograms/${uid}`;
- return makeServerRequest(programs, endpoint, "put");
+ const endpoint = `program/update`;
+ return makeServerRequest({ uid, programs }, endpoint, "put");
+};
+
+export const createUser = (uid) => {
+ console.log("creating user");
+ return makeServerRequest({ uid }, "user/create", "post");
};
/**
@@ -83,8 +95,8 @@ export const updatePrograms = (uid = "", programs) => {
*/
export const updateUserData = (uid = "", userData) => {
- const endpoint = `updateUserData/${uid}`;
- return makeServerRequest(userData, endpoint);
+ const endpoint = `user/update`;
+ return makeServerRequest({ uid, ...userData }, endpoint, "put");
};
/**
@@ -92,8 +104,9 @@ export const updateUserData = (uid = "", userData) => {
* @param {Object} data required data to create program - might eventually become enumerated
*/
-export const createSketch = data => {
- return makeServerRequest(data, "createProgram");
+export const createSketch = (data) => {
+ const { uid, ...rest } = data;
+ return makeServerRequest({ uid, program: rest }, "program/create");
};
/**
@@ -101,8 +114,9 @@ export const createSketch = data => {
* @param {Object} data required data to delete program (uid, docID, name)
*/
-export const deleteSketch = data => {
- return makeServerRequest(data, "deleteProgram");
+export const deleteSketch = (data) => {
+ const { uid, name } = data;
+ return makeServerRequest({ uid, pid: name }, "program/delete", "delete");
};
/**
@@ -110,9 +124,10 @@ export const deleteSketch = data => {
* @param {string} docID the key for the requested program in the top-level programs object
*/
-export const getSketch = async docID => {
- const endpoint = `getProgram/${docID}`;
+export const getSketch = async (docID) => {
+ const endpoint = `program/get?pid=${docID}`;
let result = await makeServerRequest({}, endpoint, "get");
- let { ok, sketch } = await result.json();
+ let ok = await result.ok;
+ let sketch = await result.json();
return { ok, sketch };
};
diff --git a/src/util/languages/CodeDownloader.js b/src/util/languages/CodeDownloader.js
index 75afe25e..027df363 100644
--- a/src/util/languages/CodeDownloader.js
+++ b/src/util/languages/CodeDownloader.js
@@ -1,14 +1,17 @@
import ProcessingConstructor from "../../components/Output/Processing";
+import ReactConstructor from "../../components/Output/React";
+import { PYTHON, REACT, HTML, PROCESSING } from "../../constants";
export default class CodeDownloader {
static download = (name, language, code) => {
let extension = ".";
switch (language) {
- case "python":
+ case PYTHON:
extension += "py";
break;
- case "processing": // this is because we construct the processing result as an HTML file. jank.
- case "html":
+ case PROCESSING: // this is because we construct the processing result as an HTML file. jank.
+ case REACT: // same here
+ case HTML:
extension += "html";
break;
default:
@@ -17,10 +20,15 @@ export default class CodeDownloader {
// taken from this: https://stackoverflow.com/questions/44656610/download-a-string-as-txt-file-in-react
const element = document.createElement("a");
let file;
- if (language === "processing") {
- file = new Blob([ProcessingConstructor(code, true)], { type: "text/plain" });
- } else {
- file = new Blob([code], { type: "text/plain" });
+ switch (language) {
+ case PROCESSING:
+ file = new Blob([ProcessingConstructor(code, true)], { type: "text/plain" });
+ break;
+ case REACT:
+ file = new Blob([ReactConstructor(code, true)], { type: "text/plain" });
+ break;
+ default:
+ file = new Blob([code], { type: "text/plain" });
}
element.href = URL.createObjectURL(file);
element.download = name + extension;