From 0edaa5f55536209538a600dd7e2278d74a788ef7 Mon Sep 17 00:00:00 2001 From: Sebas Maagnaldii Date: Fri, 5 Jun 2020 02:51:58 -0300 Subject: [PATCH 01/97] Fixed a bug that appears on preview when a .js file has a 2 or less name length --- client/utils/consoleUtils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/utils/consoleUtils.js b/client/utils/consoleUtils.js index 2f8ea1be..a6013924 100644 --- a/client/utils/consoleUtils.js +++ b/client/utils/consoleUtils.js @@ -60,7 +60,7 @@ export const getAllScriptOffsets = (htmlFile) => { if (ind === -1) { foundJSScript = false; } else { - endFilenameInd = htmlFile.indexOf('.js', ind + startTag.length + 3); + endFilenameInd = htmlFile.indexOf('.js', ind + startTag.length + 1); filename = htmlFile.substring(ind + startTag.length, endFilenameInd); lineOffset = htmlFile.substring(0, ind).split('\n').length + hijackConsoleErrorsScriptLength; offs.push([lineOffset, filename]); From 65aefcd45b1caa253e0eac406d7c56d25fe19f16 Mon Sep 17 00:00:00 2001 From: Andrew Nicolaou Date: Mon, 8 Jun 2020 11:46:38 +0200 Subject: [PATCH 02/97] Replace getConfig helper to read value from process.env --- client/components/Nav.jsx | 13 ++++----- client/modules/App/App.jsx | 5 ++-- client/modules/IDE/actions/uploader.js | 5 ++-- client/modules/IDE/components/AssetSize.jsx | 5 ++-- .../modules/IDE/components/FileUploader.jsx | 6 ++-- .../IDE/components/UploadFileModal.jsx | 4 +-- client/modules/IDE/selectors/users.js | 4 +-- client/store.js | 5 ++-- client/utils/getConfig.js | 17 +++++++++++ client/utils/getConfig.test.js | 28 +++++++++++++++++++ 10 files changed, 68 insertions(+), 24 deletions(-) create mode 100644 client/utils/getConfig.js create mode 100644 client/utils/getConfig.test.js diff --git a/client/components/Nav.jsx b/client/components/Nav.jsx index 22305b3a..dd7c800e 100644 --- a/client/components/Nav.jsx +++ b/client/components/Nav.jsx @@ -10,14 +10,13 @@ import * as projectActions from '../modules/IDE/actions/project'; import { setAllAccessibleOutput } from '../modules/IDE/actions/preferences'; import { logoutUser } from '../modules/User/actions'; +import getConfig from '../utils/getConfig'; import { metaKeyName, } from '../utils/metaKey'; import CaretLeftIcon from '../images/left-arrow.svg'; import TriangleIcon from '../images/down-filled-triangle.svg'; import LogoIcon from '../images/p5js-logo-small.svg'; -const __process = (typeof global !== 'undefined' ? global : window).process; - class Nav extends React.PureComponent { constructor(props) { super(props); @@ -272,7 +271,7 @@ class Nav extends React.PureComponent { New - { __process.env.LOGIN_ENABLED && (!this.props.project.owner || this.isUserOwner()) && + { getConfig('LOGIN_ENABLED') && (!this.props.project.owner || this.isUserOwner()) &&
  • } - {__process.env.UI_COLLECTIONS_ENABLED && + {getConfig('UI_COLLECTIONS_ENABLED') && this.props.user.authenticated && this.props.project.id &&
  • @@ -337,7 +336,7 @@ class Nav extends React.PureComponent { Add to Collection
  • } - { __process.env.EXAMPLES_ENABLED && + { getConfig('EXAMPLES_ENABLED') &&
  • - {__process.env.UI_COLLECTIONS_ENABLED && + {getConfig('UI_COLLECTIONS_ENABLED') &&
  • - {this.state.isMounted && !window.devToolsExtension && __process.env.NODE_ENV === 'development' && } + {this.state.isMounted && !window.devToolsExtension && getConfig('NODE_ENV') === 'development' && } {this.props.children} ); diff --git a/client/modules/IDE/actions/uploader.js b/client/modules/IDE/actions/uploader.js index c7a0139f..c3518630 100644 --- a/client/modules/IDE/actions/uploader.js +++ b/client/modules/IDE/actions/uploader.js @@ -1,10 +1,11 @@ import axios from 'axios'; +import getConfig from '../../../utils/getConfig'; import { createFile } from './files'; import { TEXT_FILE_REGEX } from '../../../../server/utils/fileUtils'; const __process = (typeof global !== 'undefined' ? global : window).process; -const s3BucketHttps = __process.env.S3_BUCKET_URL_BASE || - `https://s3-${__process.env.AWS_REGION}.amazonaws.com/${__process.env.S3_BUCKET}/`; +const s3BucketHttps = getConfig('S3_BUCKET_URL_BASE') || + `https://s3-${getConfig('AWS_REGION')}.amazonaws.com/${getConfig('S3_BUCKET')}/`; const ROOT_URL = __process.env.API_URL; const MAX_LOCAL_FILE_SIZE = 80000; // bytes, aka 80 KB diff --git a/client/modules/IDE/components/AssetSize.jsx b/client/modules/IDE/components/AssetSize.jsx index 2e4c1282..cf2356e2 100644 --- a/client/modules/IDE/components/AssetSize.jsx +++ b/client/modules/IDE/components/AssetSize.jsx @@ -3,8 +3,9 @@ import React from 'react'; import { connect } from 'react-redux'; import prettyBytes from 'pretty-bytes'; -const __process = (typeof global !== 'undefined' ? global : window).process; -const limit = __process.env.UPLOAD_LIMIT || 250000000; +import getConfig from '../../../utils/getConfig'; + +const limit = getConfig('UPLOAD_LIMIT') || 250000000; const MAX_SIZE_B = limit; const formatPercent = (percent) => { diff --git a/client/modules/IDE/components/FileUploader.jsx b/client/modules/IDE/components/FileUploader.jsx index c9515f5c..e2e6e509 100644 --- a/client/modules/IDE/components/FileUploader.jsx +++ b/client/modules/IDE/components/FileUploader.jsx @@ -4,11 +4,11 @@ import Dropzone from 'dropzone'; import { bindActionCreators } from 'redux'; import { connect } from 'react-redux'; import * as UploaderActions from '../actions/uploader'; +import getConfig from '../../../utils/getConfig'; import { fileExtensionsAndMimeTypes } from '../../../../server/utils/fileUtils'; -const __process = (typeof global !== 'undefined' ? global : window).process; -const s3Bucket = __process.env.S3_BUCKET_URL_BASE || - `https://s3-${__process.env.AWS_REGION}.amazonaws.com/${__process.env.S3_BUCKET}/`; +const s3Bucket = getConfig('S3_BUCKET_URL_BASE') || + `https://s3-${getConfig('AWS_REGION')}.amazonaws.com/${getConfig('S3_BUCKET')}/`; class FileUploader extends React.Component { componentDidMount() { diff --git a/client/modules/IDE/components/UploadFileModal.jsx b/client/modules/IDE/components/UploadFileModal.jsx index 27fa7c6f..ff7e9c2d 100644 --- a/client/modules/IDE/components/UploadFileModal.jsx +++ b/client/modules/IDE/components/UploadFileModal.jsx @@ -3,12 +3,12 @@ import PropTypes from 'prop-types'; import { connect } from 'react-redux'; import { Link } from 'react-router'; import prettyBytes from 'pretty-bytes'; +import getConfig from '../../../utils/getConfig'; import FileUploader from './FileUploader'; import { getreachedTotalSizeLimit } from '../selectors/users'; import ExitIcon from '../../../images/exit.svg'; -const __process = (typeof global !== 'undefined' ? global : window).process; -const limit = __process.env.UPLOAD_LIMIT || 250000000; +const limit = getConfig('UPLOAD_LIMIT') || 250000000; const limitText = prettyBytes(limit); class UploadFileModal extends React.Component { diff --git a/client/modules/IDE/selectors/users.js b/client/modules/IDE/selectors/users.js index 556d99dd..eabf2e5c 100644 --- a/client/modules/IDE/selectors/users.js +++ b/client/modules/IDE/selectors/users.js @@ -1,10 +1,10 @@ import { createSelector } from 'reselect'; +import getConfig from '../../../utils/getConfig'; -const __process = (typeof global !== 'undefined' ? global : window).process; const getAuthenticated = state => state.user.authenticated; const getTotalSize = state => state.user.totalSize; const getAssetsTotalSize = state => state.assets.totalSize; -const limit = __process.env.UPLOAD_LIMIT || 250000000; +const limit = getConfig('UPLOAD_LIMIT') || 250000000; export const getCanUploadMedia = createSelector( getAuthenticated, diff --git a/client/store.js b/client/store.js index dbb3685e..a8d2114e 100644 --- a/client/store.js +++ b/client/store.js @@ -3,15 +3,14 @@ import thunk from 'redux-thunk'; import DevTools from './modules/App/components/DevTools'; import rootReducer from './reducers'; import { clearState, loadState } from './persistState'; - -const __process = (typeof global !== 'undefined' ? global : window).process; +import getConfig from './utils/getConfig'; export default function configureStore(initialState) { const enhancers = [ applyMiddleware(thunk), ]; - if (__process.env.CLIENT && __process.env.NODE_ENV === 'development') { + if (getConfig('CLIENT') && getConfig('NODE_ENV') === 'development') { // Enable DevTools only when rendering on client and during development. enhancers.push(window.devToolsExtension ? window.devToolsExtension() : DevTools.instrument()); } diff --git a/client/utils/getConfig.js b/client/utils/getConfig.js new file mode 100644 index 00000000..3d8331a2 --- /dev/null +++ b/client/utils/getConfig.js @@ -0,0 +1,17 @@ +/** + * Returns config item from environment + */ +export default function getConfig(key) { + if (key == null) { + throw new Error('"key" must be provided to getConfig()'); + } + + const __process = (typeof global !== 'undefined' ? global : window).process; + const value = __process.env[key]; + + if (value == null) { + console.warn(`getConfig("${key}") returned null`); + } + + return value; +} diff --git a/client/utils/getConfig.test.js b/client/utils/getConfig.test.js new file mode 100644 index 00000000..05659cae --- /dev/null +++ b/client/utils/getConfig.test.js @@ -0,0 +1,28 @@ +import getConfig from './getConfig'; + +describe('utils/getConfig()', () => { + beforeEach(() => { + delete global.process.env.CONFIG_TEST_KEY_NAME; + delete window.process.env.CONFIG_TEST_KEY_NAME; + }); + + it('throws if key is not defined', () => { + expect(() => getConfig(/* key is missing */)).toThrow(/must be provided/); + }); + + it('fetches from global.process', () => { + global.process.env.CONFIG_TEST_KEY_NAME = 'editor.p5js.org'; + + expect(getConfig('CONFIG_TEST_KEY_NAME')).toBe('editor.p5js.org'); + }); + + it('fetches from window.process', () => { + window.process.env.CONFIG_TEST_KEY_NAME = 'editor.p5js.org'; + + expect(getConfig('CONFIG_TEST_KEY_NAME')).toBe('editor.p5js.org'); + }); + + it('warns but does not throw if no value found', () => { + expect(() => getConfig('CONFIG_TEST_KEY_NAME')).not.toThrow(); + }); +}); From a225d28f7553f3732730930b2d3ae625e3c74e9b Mon Sep 17 00:00:00 2001 From: Andrew Nicolaou Date: Mon, 8 Jun 2020 12:29:24 +0200 Subject: [PATCH 03/97] Use apiClient instance instead of directly calling Axios Reduces the amount of duplication and provides a single place where we can configure base URL, crendentials and other headers --- client/modules/IDE/actions/assets.js | 9 +++---- client/modules/IDE/actions/collections.js | 30 ++++++++++----------- client/modules/IDE/actions/files.js | 10 +++---- client/modules/IDE/actions/preferences.js | 7 ++--- client/modules/IDE/actions/project.js | 22 ++++++++-------- client/modules/IDE/actions/projects.js | 11 +++----- client/modules/IDE/actions/uploader.js | 21 +++++---------- client/modules/User/actions.js | 32 ++++++++++------------- client/modules/User/pages/AccountView.jsx | 7 ++--- client/modules/User/pages/SignupView.jsx | 7 ++--- client/utils/apiClient.js | 17 ++++++++++++ 11 files changed, 80 insertions(+), 93 deletions(-) create mode 100644 client/utils/apiClient.js diff --git a/client/modules/IDE/actions/assets.js b/client/modules/IDE/actions/assets.js index 483e6d4e..79df4285 100644 --- a/client/modules/IDE/actions/assets.js +++ b/client/modules/IDE/actions/assets.js @@ -1,10 +1,7 @@ -import axios from 'axios'; +import apiClient from '../../../utils/apiClient'; import * as ActionTypes from '../../../constants'; import { startLoader, stopLoader } from './loader'; -const __process = (typeof global !== 'undefined' ? global : window).process; -const ROOT_URL = __process.env.API_URL; - function setAssets(assets, totalSize) { return { type: ActionTypes.SET_ASSETS, @@ -16,7 +13,7 @@ function setAssets(assets, totalSize) { export function getAssets() { return (dispatch) => { dispatch(startLoader()); - axios.get(`${ROOT_URL}/S3/objects`, { withCredentials: true }) + apiClient.get('/S3/objects') .then((response) => { dispatch(setAssets(response.data.assets, response.data.totalSize)); dispatch(stopLoader()); @@ -39,7 +36,7 @@ export function deleteAsset(assetKey) { export function deleteAssetRequest(assetKey) { return (dispatch) => { - axios.delete(`${ROOT_URL}/S3/${assetKey}`, { withCredentials: true }) + apiClient.delete(`/S3/${assetKey}`) .then((response) => { dispatch(deleteAsset(assetKey)); }) diff --git a/client/modules/IDE/actions/collections.js b/client/modules/IDE/actions/collections.js index 03cf2a64..3aa954ac 100644 --- a/client/modules/IDE/actions/collections.js +++ b/client/modules/IDE/actions/collections.js @@ -1,11 +1,9 @@ -import axios from 'axios'; import { browserHistory } from 'react-router'; +import apiClient from '../../../utils/apiClient'; import * as ActionTypes from '../../../constants'; import { startLoader, stopLoader } from './loader'; import { setToastText, showToast } from './toast'; -const __process = (typeof global !== 'undefined' ? global : window).process; -const ROOT_URL = __process.env.API_URL; const TOAST_DISPLAY_TIME_MS = 1500; @@ -15,11 +13,11 @@ export function getCollections(username) { dispatch(startLoader()); let url; if (username) { - url = `${ROOT_URL}/${username}/collections`; + url = `/${username}/collections`; } else { - url = `${ROOT_URL}/collections`; + url = '/collections'; } - axios.get(url, { withCredentials: true }) + apiClient.get(url) .then((response) => { dispatch({ type: ActionTypes.SET_COLLECTIONS, @@ -41,8 +39,8 @@ export function getCollections(username) { export function createCollection(collection) { return (dispatch) => { dispatch(startLoader()); - const url = `${ROOT_URL}/collections`; - return axios.post(url, collection, { withCredentials: true }) + const url = '/collections'; + return apiClient.post(url, collection) .then((response) => { dispatch({ type: ActionTypes.CREATE_COLLECTION @@ -73,8 +71,8 @@ export function createCollection(collection) { export function addToCollection(collectionId, projectId) { return (dispatch) => { dispatch(startLoader()); - const url = `${ROOT_URL}/collections/${collectionId}/${projectId}`; - return axios.post(url, { withCredentials: true }) + const url = `/collections/${collectionId}/${projectId}`; + return apiClient.post(url) .then((response) => { dispatch({ type: ActionTypes.ADD_TO_COLLECTION, @@ -105,8 +103,8 @@ export function addToCollection(collectionId, projectId) { export function removeFromCollection(collectionId, projectId) { return (dispatch) => { dispatch(startLoader()); - const url = `${ROOT_URL}/collections/${collectionId}/${projectId}`; - return axios.delete(url, { withCredentials: true }) + const url = `/collections/${collectionId}/${projectId}`; + return apiClient.delete(url) .then((response) => { dispatch({ type: ActionTypes.REMOVE_FROM_COLLECTION, @@ -136,8 +134,8 @@ export function removeFromCollection(collectionId, projectId) { export function editCollection(collectionId, { name, description }) { return (dispatch) => { - const url = `${ROOT_URL}/collections/${collectionId}`; - return axios.patch(url, { name, description }, { withCredentials: true }) + const url = `/collections/${collectionId}`; + return apiClient.patch(url, { name, description }) .then((response) => { dispatch({ type: ActionTypes.EDIT_COLLECTION, @@ -159,8 +157,8 @@ export function editCollection(collectionId, { name, description }) { export function deleteCollection(collectionId) { return (dispatch) => { - const url = `${ROOT_URL}/collections/${collectionId}`; - return axios.delete(url, { withCredentials: true }) + const url = `/collections/${collectionId}`; + return apiClient.delete(url) .then((response) => { dispatch({ type: ActionTypes.DELETE_COLLECTION, diff --git a/client/modules/IDE/actions/files.js b/client/modules/IDE/actions/files.js index 004600b2..e17e46c5 100644 --- a/client/modules/IDE/actions/files.js +++ b/client/modules/IDE/actions/files.js @@ -1,13 +1,11 @@ -import axios from 'axios'; import objectID from 'bson-objectid'; import blobUtil from 'blob-util'; import { reset } from 'redux-form'; +import apiClient from '../../../utils/apiClient'; import * as ActionTypes from '../../../constants'; import { setUnsavedChanges, closeNewFolderModal, closeNewFileModal } from './ide'; import { setProjectSavedTime } from './project'; -const __process = (typeof global !== 'undefined' ? global : window).process; -const ROOT_URL = __process.env.API_URL; function appendToFilename(filename, string) { const dotIndex = filename.lastIndexOf('.'); @@ -50,7 +48,7 @@ export function createFile(formProps) { parentId, children: [] }; - axios.post(`${ROOT_URL}/projects/${state.project.id}/files`, postParams, { withCredentials: true }) + apiClient.post(`/projects/${state.project.id}/files`, postParams) .then((response) => { dispatch({ type: ActionTypes.CREATE_FILE, @@ -106,7 +104,7 @@ export function createFolder(formProps) { parentId, fileType: 'folder' }; - axios.post(`${ROOT_URL}/projects/${state.project.id}/files`, postParams, { withCredentials: true }) + apiClient.post(`/projects/${state.project.id}/files`, postParams) .then((response) => { dispatch({ type: ActionTypes.CREATE_FILE, @@ -161,7 +159,7 @@ export function deleteFile(id, parentId) { parentId } }; - axios.delete(`${ROOT_URL}/projects/${state.project.id}/files/${id}`, deleteConfig, { withCredentials: true }) + apiClient.delete(`/projects/${state.project.id}/files/${id}`, deleteConfig) .then(() => { dispatch({ type: ActionTypes.DELETE_FILE, diff --git a/client/modules/IDE/actions/preferences.js b/client/modules/IDE/actions/preferences.js index 01ba077c..a182da76 100644 --- a/client/modules/IDE/actions/preferences.js +++ b/client/modules/IDE/actions/preferences.js @@ -1,11 +1,8 @@ -import axios from 'axios'; +import apiClient from '../../../utils/apiClient'; import * as ActionTypes from '../../../constants'; -const __process = (typeof global !== 'undefined' ? global : window).process; -const ROOT_URL = __process.env.API_URL; - function updatePreferences(formParams, dispatch) { - axios.put(`${ROOT_URL}/preferences`, formParams, { withCredentials: true }) + apiClient.put('/preferences', formParams) .then(() => { }) .catch((error) => { diff --git a/client/modules/IDE/actions/project.js b/client/modules/IDE/actions/project.js index d42c307d..2e2bca0b 100644 --- a/client/modules/IDE/actions/project.js +++ b/client/modules/IDE/actions/project.js @@ -1,8 +1,9 @@ import { browserHistory } from 'react-router'; -import axios from 'axios'; import objectID from 'bson-objectid'; import each from 'async/each'; import isEqual from 'lodash/isEqual'; +import apiClient from '../../../utils/apiClient'; +import getConfig from '../../../utils/getConfig'; import * as ActionTypes from '../../../constants'; import { showToast, setToastText } from './toast'; import { @@ -14,8 +15,7 @@ import { } from './ide'; import { clearState, saveState } from '../../../persistState'; -const __process = (typeof global !== 'undefined' ? global : window).process; -const ROOT_URL = __process.env.API_URL; +const ROOT_URL = getConfig('API_URL'); export function setProject(project) { return { @@ -52,7 +52,7 @@ export function setNewProject(project) { export function getProject(id, username) { return (dispatch, getState) => { dispatch(justOpenedProject()); - axios.get(`${ROOT_URL}/${username}/projects/${id}`, { withCredentials: true }) + apiClient.get(`/${username}/projects/${id}`) .then((response) => { dispatch(setProject(response.data)); dispatch(setUnsavedChanges(false)); @@ -142,7 +142,7 @@ export function saveProject(selectedFile = null, autosave = false) { fileToUpdate.content = selectedFile.content; } if (state.project.id) { - return axios.put(`${ROOT_URL}/projects/${state.project.id}`, formParams, { withCredentials: true }) + return apiClient.put(`/projects/${state.project.id}`, formParams) .then((response) => { dispatch(endSavingProject()); dispatch(setUnsavedChanges(false)); @@ -177,7 +177,7 @@ export function saveProject(selectedFile = null, autosave = false) { }); } - return axios.post(`${ROOT_URL}/projects`, formParams, { withCredentials: true }) + return apiClient.post('/projects', formParams) .then((response) => { dispatch(endSavingProject()); const { hasChanges, synchedProject } = getSynchedProject(getState(), response.data); @@ -260,7 +260,7 @@ export function cloneProject(id) { if (!id) { resolve(getState()); } else { - fetch(`${ROOT_URL}/projects/${id}`) + apiClient.get(`/projects/${id}`) .then(res => res.json()) .then(data => resolve({ files: data.files, @@ -287,7 +287,7 @@ export function cloneProject(id) { const formParams = { url: file.url }; - axios.post(`${ROOT_URL}/S3/copy`, formParams, { withCredentials: true }) + apiClient.post('/S3/copy', formParams) .then((response) => { file.url = response.data.url; callback(null); @@ -298,7 +298,7 @@ export function cloneProject(id) { }, (err) => { // if not errors in duplicating the files on S3, then duplicate it const formParams = Object.assign({}, { name: `${state.project.name} copy` }, { files: newFiles }); - axios.post(`${ROOT_URL}/projects`, formParams, { withCredentials: true }) + apiClient.post('/projects', formParams) .then((response) => { browserHistory.push(`/${response.data.user.username}/sketches/${response.data.id}`); dispatch(setNewProject(response.data)); @@ -337,7 +337,7 @@ export function setProjectSavedTime(updatedAt) { export function changeProjectName(id, newName) { return (dispatch, getState) => { const state = getState(); - axios.put(`${ROOT_URL}/projects/${id}`, { name: newName }, { withCredentials: true }) + apiClient.put(`/projects/${id}`, { name: newName }) .then((response) => { if (response.status === 200) { dispatch({ @@ -364,7 +364,7 @@ export function changeProjectName(id, newName) { export function deleteProject(id) { return (dispatch, getState) => { - axios.delete(`${ROOT_URL}/projects/${id}`, { withCredentials: true }) + apiClient.delete(`/projects/${id}`) .then(() => { const state = getState(); if (id === state.project.id) { diff --git a/client/modules/IDE/actions/projects.js b/client/modules/IDE/actions/projects.js index eb653ec8..d41747b5 100644 --- a/client/modules/IDE/actions/projects.js +++ b/client/modules/IDE/actions/projects.js @@ -1,21 +1,18 @@ -import axios from 'axios'; +import apiClient from '../../../utils/apiClient'; import * as ActionTypes from '../../../constants'; import { startLoader, stopLoader } from './loader'; -const __process = (typeof global !== 'undefined' ? global : window).process; -const ROOT_URL = __process.env.API_URL; - // eslint-disable-next-line export function getProjects(username) { return (dispatch) => { dispatch(startLoader()); let url; if (username) { - url = `${ROOT_URL}/${username}/projects`; + url = `/${username}/projects`; } else { - url = `${ROOT_URL}/projects`; + url = '/projects'; } - axios.get(url, { withCredentials: true }) + apiClient.get(url) .then((response) => { dispatch({ type: ActionTypes.SET_PROJECTS, diff --git a/client/modules/IDE/actions/uploader.js b/client/modules/IDE/actions/uploader.js index c3518630..eca1ffe6 100644 --- a/client/modules/IDE/actions/uploader.js +++ b/client/modules/IDE/actions/uploader.js @@ -1,12 +1,10 @@ -import axios from 'axios'; +import apiClient from '../../../utils/apiClient'; import getConfig from '../../../utils/getConfig'; import { createFile } from './files'; import { TEXT_FILE_REGEX } from '../../../../server/utils/fileUtils'; -const __process = (typeof global !== 'undefined' ? global : window).process; const s3BucketHttps = getConfig('S3_BUCKET_URL_BASE') || `https://s3-${getConfig('AWS_REGION')}.amazonaws.com/${getConfig('S3_BUCKET')}/`; -const ROOT_URL = __process.env.API_URL; const MAX_LOCAL_FILE_SIZE = 80000; // bytes, aka 80 KB function localIntercept(file, options = {}) { @@ -47,18 +45,13 @@ export function dropzoneAcceptCallback(userId, file, done) { }); } else { file.postData = []; // eslint-disable-line - axios.post( - `${ROOT_URL}/S3/sign`, { - name: file.name, - type: file.type, - size: file.size, - userId + apiClient.post('/S3/sign', { + name: file.name, + type: file.type, + size: file.size, + userId // _csrf: document.getElementById('__createPostToken').value - }, - { - withCredentials: true - } - ) + }) .then((response) => { file.custom_status = 'ready'; // eslint-disable-line file.postData = response.data; // eslint-disable-line diff --git a/client/modules/User/actions.js b/client/modules/User/actions.js index 19928549..dd54224d 100644 --- a/client/modules/User/actions.js +++ b/client/modules/User/actions.js @@ -1,13 +1,9 @@ import { browserHistory } from 'react-router'; -import axios from 'axios'; import * as ActionTypes from '../../constants'; +import apiClient from '../../utils/apiClient'; import { showErrorModal, justOpenedProject } from '../IDE/actions/ide'; import { showToast, setToastText } from '../IDE/actions/toast'; - -const __process = (typeof global !== 'undefined' ? global : window).process; -const ROOT_URL = __process.env.API_URL; - export function authError(error) { return { type: ActionTypes.AUTH_ERROR, @@ -17,7 +13,7 @@ export function authError(error) { export function signUpUser(previousPath, formValues) { return (dispatch) => { - axios.post(`${ROOT_URL}/signup`, formValues, { withCredentials: true }) + apiClient.post('/signup', formValues) .then((response) => { dispatch({ type: ActionTypes.AUTH_USER, @@ -34,7 +30,7 @@ export function signUpUser(previousPath, formValues) { } export function loginUser(formValues) { - return axios.post(`${ROOT_URL}/login`, formValues, { withCredentials: true }); + return apiClient.post('/login', formValues); } export function loginUserSuccess(user) { @@ -74,7 +70,7 @@ export function validateAndLoginUser(previousPath, formProps, dispatch) { export function getUser() { return (dispatch) => { - axios.get(`${ROOT_URL}/session`, { withCredentials: true }) + apiClient.get('/session') .then((response) => { dispatch({ type: ActionTypes.AUTH_USER, @@ -95,7 +91,7 @@ export function getUser() { export function validateSession() { return (dispatch, getState) => { - axios.get(`${ROOT_URL}/session`, { withCredentials: true }) + apiClient.get('/session') .then((response) => { const state = getState(); if (state.user.username !== response.data.username) { @@ -113,7 +109,7 @@ export function validateSession() { export function logoutUser() { return (dispatch) => { - axios.get(`${ROOT_URL}/logout`, { withCredentials: true }) + apiClient.get('/logout') .then(() => { dispatch({ type: ActionTypes.UNAUTH_USER @@ -131,7 +127,7 @@ export function initiateResetPassword(formValues) { dispatch({ type: ActionTypes.RESET_PASSWORD_INITIATE }); - axios.post(`${ROOT_URL}/reset-password`, formValues, { withCredentials: true }) + apiClient.post('/reset-password', formValues) .then(() => { // do nothing }) @@ -150,7 +146,7 @@ export function initiateVerification() { dispatch({ type: ActionTypes.EMAIL_VERIFICATION_INITIATE }); - axios.post(`${ROOT_URL}/verify/send`, {}, { withCredentials: true }) + apiClient.post('/verify/send', {}) .then(() => { // do nothing }) @@ -170,7 +166,7 @@ export function verifyEmailConfirmation(token) { type: ActionTypes.EMAIL_VERIFICATION_VERIFY, state: 'checking', }); - return axios.get(`${ROOT_URL}/verify?t=${token}`, {}, { withCredentials: true }) + return apiClient.get(`/verify?t=${token}`, {}) .then(response => dispatch({ type: ActionTypes.EMAIL_VERIFICATION_VERIFIED, message: response.data, @@ -194,7 +190,7 @@ export function resetPasswordReset() { export function validateResetPasswordToken(token) { return (dispatch) => { - axios.get(`${ROOT_URL}/reset-password/${token}`) + apiClient.get(`/reset-password/${token}`) .then(() => { // do nothing if the token is valid }) @@ -206,7 +202,7 @@ export function validateResetPasswordToken(token) { export function updatePassword(token, formValues) { return (dispatch) => { - axios.post(`${ROOT_URL}/reset-password/${token}`, formValues) + apiClient.post(`/reset-password/${token}`, formValues) .then((response) => { dispatch(loginUserSuccess(response.data)); browserHistory.push('/'); @@ -226,7 +222,7 @@ export function updateSettingsSuccess(user) { export function updateSettings(formValues) { return dispatch => - axios.put(`${ROOT_URL}/account`, formValues, { withCredentials: true }) + apiClient.put('/account', formValues) .then((response) => { dispatch(updateSettingsSuccess(response.data)); browserHistory.push('/'); @@ -248,7 +244,7 @@ export function createApiKeySuccess(user) { export function createApiKey(label) { return dispatch => - axios.post(`${ROOT_URL}/account/api-keys`, { label }, { withCredentials: true }) + apiClient.post('/account/api-keys', { label }) .then((response) => { dispatch(createApiKeySuccess(response.data)); }) @@ -260,7 +256,7 @@ export function createApiKey(label) { export function removeApiKey(keyId) { return dispatch => - axios.delete(`${ROOT_URL}/account/api-keys/${keyId}`, { withCredentials: true }) + apiClient.delete(`/account/api-keys/${keyId}`) .then((response) => { dispatch({ type: ActionTypes.API_KEY_REMOVED, diff --git a/client/modules/User/pages/AccountView.jsx b/client/modules/User/pages/AccountView.jsx index 23c126bc..fff8d331 100644 --- a/client/modules/User/pages/AccountView.jsx +++ b/client/modules/User/pages/AccountView.jsx @@ -3,18 +3,15 @@ import React from 'react'; import { reduxForm } from 'redux-form'; import { bindActionCreators } from 'redux'; import { Tab, Tabs, TabList, TabPanel } from 'react-tabs'; -import axios from 'axios'; import { Helmet } from 'react-helmet'; import { updateSettings, initiateVerification, createApiKey, removeApiKey } from '../actions'; import AccountForm from '../components/AccountForm'; +import apiClient from '../../../utils/apiClient'; import { validateSettings } from '../../../utils/reduxFormUtils'; import SocialAuthButton from '../components/SocialAuthButton'; import APIKeyForm from '../components/APIKeyForm'; import Nav from '../../../components/Nav'; -const __process = (typeof global !== 'undefined' ? global : window).process; -const ROOT_URL = __process.env.API_URL; - function SocialLoginPanel(props) { return ( @@ -96,7 +93,7 @@ function asyncValidate(formProps, dispatch, props) { const queryParams = {}; queryParams[fieldToValidate] = formProps[fieldToValidate]; queryParams.check_type = fieldToValidate; - return axios.get(`${ROOT_URL}/signup/duplicate_check`, { params: queryParams }) + return apiClient.get('/signup/duplicate_check', { params: queryParams }) .then((response) => { if (response.data.exists) { const error = {}; diff --git a/client/modules/User/pages/SignupView.jsx b/client/modules/User/pages/SignupView.jsx index b51c6ac6..225bb8e4 100644 --- a/client/modules/User/pages/SignupView.jsx +++ b/client/modules/User/pages/SignupView.jsx @@ -1,19 +1,16 @@ import PropTypes from 'prop-types'; import React from 'react'; import { bindActionCreators } from 'redux'; -import axios from 'axios'; import { Link, browserHistory } from 'react-router'; import { Helmet } from 'react-helmet'; import { reduxForm } from 'redux-form'; import * as UserActions from '../actions'; import SignupForm from '../components/SignupForm'; +import apiClient from '../../../utils/apiClient'; import { validateSignup } from '../../../utils/reduxFormUtils'; import SocialAuthButton from '../components/SocialAuthButton'; import Nav from '../../../components/Nav'; -const __process = (typeof global !== 'undefined' ? global : window).process; -const ROOT_URL = __process.env.API_URL; - class SignupView extends React.Component { gotoHomePage = () => { browserHistory.push('/'); @@ -86,7 +83,7 @@ function asyncValidate(formProps, dispatch, props) { const queryParams = {}; queryParams[fieldToValidate] = formProps[fieldToValidate]; queryParams.check_type = fieldToValidate; - return axios.get(`${ROOT_URL}/signup/duplicate_check`, { params: queryParams }) + return apiClient.get('/signup/duplicate_check', { params: queryParams }) .then((response) => { if (response.data.exists) { errors[fieldToValidate] = response.data.message; diff --git a/client/utils/apiClient.js b/client/utils/apiClient.js new file mode 100644 index 00000000..a8347674 --- /dev/null +++ b/client/utils/apiClient.js @@ -0,0 +1,17 @@ +import axios from 'axios'; + +import getConfig from './getConfig'; + +const ROOT_URL = getConfig('API_URL'); + +/** + * Configures an Axios instance with the correct API URL + */ +function createClientInstance() { + return axios.create({ + baseURL: ROOT_URL, + withCredentials: true + }); +} + +export default createClientInstance(); From cb2f42dc505a576728cae3603e66857d4bf5da10 Mon Sep 17 00:00:00 2001 From: ghalestrilo Date: Tue, 9 Jun 2020 16:29:20 -0300 Subject: [PATCH 04/97] =?UTF-8?q?=E2=9B=8F=20switch=20to=20mobile=20screen?= =?UTF-8?q?=20by=20window=20width?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/modules/IDE/pages/IDEViewMobile.jsx | 5 +++++ client/routes.jsx | 6 +++++- 2 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 client/modules/IDE/pages/IDEViewMobile.jsx diff --git a/client/modules/IDE/pages/IDEViewMobile.jsx b/client/modules/IDE/pages/IDEViewMobile.jsx new file mode 100644 index 00000000..d8bb3e33 --- /dev/null +++ b/client/modules/IDE/pages/IDEViewMobile.jsx @@ -0,0 +1,5 @@ +import React from 'react'; + +export default () => (
    +

    Test

    +
    ); //eslint-disable-line diff --git a/client/routes.jsx b/client/routes.jsx index 18ff9b20..061e537c 100644 --- a/client/routes.jsx +++ b/client/routes.jsx @@ -2,6 +2,7 @@ import { Route, IndexRoute } from 'react-router'; import React from 'react'; import App from './modules/App/App'; import IDEView from './modules/IDE/pages/IDEView'; +import IDEViewMobile from './modules/IDE/pages/IDEViewMobile'; import FullView from './modules/IDE/pages/FullView'; import LoginView from './modules/User/pages/LoginView'; import SignupView from './modules/User/pages/SignupView'; @@ -24,9 +25,11 @@ const onRouteChange = (store) => { store.dispatch(stopSketch()); }; +const isMobile = () => window.innerWidth <= 760; + const routes = store => ( { onRouteChange(store); }}> - + @@ -49,6 +52,7 @@ const routes = store => ( + ); From ca0d953b808a65c358cd51830bf3039d78185584 Mon Sep 17 00:00:00 2001 From: ghalestrilo Date: Tue, 9 Jun 2020 16:51:57 -0300 Subject: [PATCH 05/97] =?UTF-8?q?=E2=9B=8F=20=20create=20basic=20header=20?= =?UTF-8?q?and=20footer=20components?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/modules/IDE/pages/IDEViewMobile.jsx | 34 ++++++++++++++++++++-- client/routes.jsx | 7 +++-- 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/client/modules/IDE/pages/IDEViewMobile.jsx b/client/modules/IDE/pages/IDEViewMobile.jsx index d8bb3e33..b603e758 100644 --- a/client/modules/IDE/pages/IDEViewMobile.jsx +++ b/client/modules/IDE/pages/IDEViewMobile.jsx @@ -1,5 +1,33 @@ import React from 'react'; +import PropTypes from 'prop-types'; +import styled from 'styled-components'; -export default () => (
    -

    Test

    -
    ); //eslint-disable-line +const Header = styled.div` + width: 100%; + color: orange; + background: red; +`; + +const Footer = styled.div` + width: 100%; + color: orange; + background: blue; + position: absolute; + bottom: 0; +`; + +const Screen = ({ children }) => ( +
    + {children} +
    +); +Screen.propTypes = { + children: PropTypes.element.isRequired +}; + +export default () => ( + +

    Test

    +

    Actionbar

    +
    +); diff --git a/client/routes.jsx b/client/routes.jsx index 061e537c..02cdaa07 100644 --- a/client/routes.jsx +++ b/client/routes.jsx @@ -1,8 +1,8 @@ import { Route, IndexRoute } from 'react-router'; import React from 'react'; import App from './modules/App/App'; -import IDEView from './modules/IDE/pages/IDEView'; -import IDEViewMobile from './modules/IDE/pages/IDEViewMobile'; +import IDEViewScreen from './modules/IDE/pages/IDEView'; +import IDEViewMobileScreen from './modules/IDE/pages/IDEViewMobile'; import FullView from './modules/IDE/pages/FullView'; import LoginView from './modules/User/pages/LoginView'; import SignupView from './modules/User/pages/SignupView'; @@ -26,10 +26,11 @@ const onRouteChange = (store) => { }; const isMobile = () => window.innerWidth <= 760; +const IDEView = isMobile() ? IDEViewMobileScreen : IDEViewScreen; const routes = store => ( { onRouteChange(store); }}> - + From 95986a2df6491fcb8f6c13aa356a190284a0f3de Mon Sep 17 00:00:00 2001 From: ghalestrilo Date: Tue, 9 Jun 2020 17:08:14 -0300 Subject: [PATCH 06/97] =?UTF-8?q?=E2=9B=8F=20=20create=20uri=20method=20fo?= =?UTF-8?q?r=20bypassing=20the=20mobile=20screen?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/modules/IDE/pages/IDEViewMobile.jsx | 2 +- client/routes.jsx | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/client/modules/IDE/pages/IDEViewMobile.jsx b/client/modules/IDE/pages/IDEViewMobile.jsx index b603e758..edc7c4bb 100644 --- a/client/modules/IDE/pages/IDEViewMobile.jsx +++ b/client/modules/IDE/pages/IDEViewMobile.jsx @@ -22,7 +22,7 @@ const Screen = ({ children }) => ( ); Screen.propTypes = { - children: PropTypes.element.isRequired + children: PropTypes.node.isRequired }; export default () => ( diff --git a/client/routes.jsx b/client/routes.jsx index 02cdaa07..9502857f 100644 --- a/client/routes.jsx +++ b/client/routes.jsx @@ -25,8 +25,10 @@ const onRouteChange = (store) => { store.dispatch(stopSketch()); }; +const ignoreMobile = () => window.location.search.substring(1).includes('ignoremobile'); + const isMobile = () => window.innerWidth <= 760; -const IDEView = isMobile() ? IDEViewMobileScreen : IDEViewScreen; +const IDEView = isMobile() && !ignoreMobile() ? IDEViewMobileScreen : IDEViewScreen; const routes = store => ( { onRouteChange(store); }}> From 327406ea31dd9f9099da9e287ef425e1011e322d Mon Sep 17 00:00:00 2001 From: ghalestrilo Date: Tue, 9 Jun 2020 17:35:06 -0300 Subject: [PATCH 07/97] =?UTF-8?q?=E2=9B=8F=20=20create=20mobile=20screen?= =?UTF-8?q?=20stub?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/modules/IDE/pages/IDEViewMobile.jsx | 22 ++++++++++++++++------ client/theme.js | 3 +++ 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/client/modules/IDE/pages/IDEViewMobile.jsx b/client/modules/IDE/pages/IDEViewMobile.jsx index edc7c4bb..fcb39f99 100644 --- a/client/modules/IDE/pages/IDEViewMobile.jsx +++ b/client/modules/IDE/pages/IDEViewMobile.jsx @@ -1,19 +1,25 @@ import React from 'react'; import PropTypes from 'prop-types'; import styled from 'styled-components'; +import { prop, remSize } from '../../../theme'; + +const background = prop('Button.default.background'); +const textColor = prop('primaryTextColor'); const Header = styled.div` width: 100%; - color: orange; - background: red; + background-color: ${background} !important; + color: ${textColor}; + padding-left: ${remSize(32)}; `; const Footer = styled.div` width: 100%; - color: orange; - background: blue; position: absolute; bottom: 0; + background: ${background}; + color: ${textColor}; + padding-left: ${remSize(32)}; `; const Screen = ({ children }) => ( @@ -27,7 +33,11 @@ Screen.propTypes = { export default () => ( -

    Test

    -

    Actionbar

    +

    Mobile View

    +

    +
    This page is under construction. +
    Click here to return to the regular editor +

    +

    Bottom Bar

    ); diff --git a/client/theme.js b/client/theme.js index 5215dd99..4b61d77c 100644 --- a/client/theme.js +++ b/client/theme.js @@ -111,8 +111,11 @@ export default { foreground: grays.light, background: grays.dark, border: grays.middleDark, + }, }, + + }, [Theme.contrast]: { colors, From d64d93c82812c725560bf312a5a41d46657b3d15 Mon Sep 17 00:00:00 2001 From: Cassie Tarakajian Date: Wed, 10 Jun 2020 14:43:13 -0400 Subject: [PATCH 08/97] [#1421] Correct order of release steps --- developer_docs/release.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/developer_docs/release.md b/developer_docs/release.md index 302c8019..aa06293a 100644 --- a/developer_docs/release.md +++ b/developer_docs/release.md @@ -13,19 +13,19 @@ This project release guide is based on 1. `$ git checkout develop` 2. `$ git checkout -b release-` 3. Do all of the release branch testing necessary. This could be as simple as running `npm test:ci`, or it could take user testing over a few days. -4. `$ git checkout release` -5. `$ git merge --no-ff release-` -6. `$ npm version ` (see [npm-version](https://docs.npmjs.com/cli/version) for valid values of ). +4. `$ npm version ` (see [npm-version](https://docs.npmjs.com/cli/version) for valid values of ). +5. `$ git checkout release` +6. `$ git merge --no-ff release-` 7. `$ git push && git push --tags` 8. `$ git checkout develop` -9. `$ git merge --no-ff release-` -10. Create a release on GitHub. You can do this in one of two ways: +9. Create a release on GitHub. Make sure that you release from the `release` branch! You can do this in one of two ways: 1. (Preferred) Use the [`hub` command line tool](https://hub.github.com/). You can automate adding all commit messages since the last release with the following command: ```sh $ hub release create -d -m "" -m "$(git log `git describe --tags --abbrev=0 HEAD^`..HEAD --oneline)" ` ``` Note that this creates a draft release, which you can then edit on GitHub. This allows you to create release notes from the list of commit messages, but then edit these notes as you wish. 2. [Draft a new release on Github](https://github.com/processing/p5.js-web-editor/releases/new). +10. `$ git merge --no-ff release-` Travis CI will automatically deploy the release to production, as well as push a production tagged Docker image to DockerHub. From 7dc10ab682468f894a4c3a571bc158b38b680237 Mon Sep 17 00:00:00 2001 From: ghalestrilo Date: Fri, 12 Jun 2020 16:09:30 -0300 Subject: [PATCH 09/97] =?UTF-8?q?=E2=9C=A8make=20environment=20variable-ba?= =?UTF-8?q?sed=20switch=20for=20/mobile=20route?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/modules/IDE/pages/IDEViewMobile.jsx | 8 +++++++- client/routes.jsx | 14 ++++++++------ client/theme.js | 3 --- server/routes/server.routes.js | 8 +++++--- server/views/index.js | 1 + 5 files changed, 21 insertions(+), 13 deletions(-) diff --git a/client/modules/IDE/pages/IDEViewMobile.jsx b/client/modules/IDE/pages/IDEViewMobile.jsx index fcb39f99..1e3fb820 100644 --- a/client/modules/IDE/pages/IDEViewMobile.jsx +++ b/client/modules/IDE/pages/IDEViewMobile.jsx @@ -1,6 +1,9 @@ import React from 'react'; import PropTypes from 'prop-types'; import styled from 'styled-components'; +import { Link } from 'react-router'; + +import Editor from '../components/Editor'; import { prop, remSize } from '../../../theme'; const background = prop('Button.default.background'); @@ -34,10 +37,13 @@ Screen.propTypes = { export default () => (

    Mobile View

    + +


    This page is under construction. -
    Click here to return to the regular editor +
    Click here to return to the regular editor

    +

    Bottom Bar

    ); diff --git a/client/routes.jsx b/client/routes.jsx index 9502857f..5f4f9b8b 100644 --- a/client/routes.jsx +++ b/client/routes.jsx @@ -1,8 +1,8 @@ import { Route, IndexRoute } from 'react-router'; import React from 'react'; import App from './modules/App/App'; -import IDEViewScreen from './modules/IDE/pages/IDEView'; -import IDEViewMobileScreen from './modules/IDE/pages/IDEViewMobile'; +import IDEView from './modules/IDE/pages/IDEView'; +import IDEViewMobile from './modules/IDE/pages/IDEViewMobile'; import FullView from './modules/IDE/pages/FullView'; import LoginView from './modules/User/pages/LoginView'; import SignupView from './modules/User/pages/SignupView'; @@ -25,11 +25,12 @@ const onRouteChange = (store) => { store.dispatch(stopSketch()); }; -const ignoreMobile = () => window.location.search.substring(1).includes('ignoremobile'); - -const isMobile = () => window.innerWidth <= 760; -const IDEView = isMobile() && !ignoreMobile() ? IDEViewMobileScreen : IDEViewScreen; +// TODO: Investigate using react-router for this switch +// const ignoreMobile = () => window.location.search.substring(1).includes('ignoremobile'); +// const isMobile = () => window.innerWidth <= 760; +// const IDEView = isMobile() && !ignoreMobile() ? IDEViewMobileScreen : IDEViewScreen; +// How to use URL as a prop? const routes = store => ( { onRouteChange(store); }}> @@ -55,6 +56,7 @@ const routes = store => ( + ); diff --git a/client/theme.js b/client/theme.js index 4b61d77c..5215dd99 100644 --- a/client/theme.js +++ b/client/theme.js @@ -111,11 +111,8 @@ export default { foreground: grays.light, background: grays.dark, border: grays.middleDark, - }, }, - - }, [Theme.contrast]: { colors, diff --git a/server/routes/server.routes.js b/server/routes/server.routes.js index ddec0c22..bec91b17 100644 --- a/server/routes/server.routes.js +++ b/server/routes/server.routes.js @@ -114,9 +114,11 @@ router.get('/about', (req, res) => { res.send(renderIndex()); }); -router.get('/feedback', (req, res) => { - res.send(renderIndex()); -}); +if (process.env.MOBILE_ENABLED) { + router.get('/mobile', (req, res) => { + res.send(renderIndex()); + }); +} router.get('/:username/collections/create', (req, res) => { userExists(req.params.username, (exists) => { diff --git a/server/views/index.js b/server/views/index.js index 52b98985..ef88aec6 100644 --- a/server/views/index.js +++ b/server/views/index.js @@ -32,6 +32,7 @@ export function renderIndex() { window.process.env.UI_ACCESS_TOKEN_ENABLED = ${process.env.UI_ACCESS_TOKEN_ENABLED === 'false' ? false : true}; window.process.env.UI_COLLECTIONS_ENABLED = ${process.env.UI_COLLECTIONS_ENABLED === 'false' ? false : true}; window.process.env.UPLOAD_LIMIT = ${process.env.UPLOAD_LIMIT ? `${process.env.UPLOAD_LIMIT}` : undefined}; + window.process.env.MOBILE_ENABLED = ${process.env.MOBILE_ENABLED ? `${process.env.MOBILE_ENABLED}` : undefined}; From dc3f14083b1cbf0cd53a3a9cf3e83dc6e1e1d47e Mon Sep 17 00:00:00 2001 From: Casey Reas Date: Fri, 12 Jun 2020 12:09:58 -0700 Subject: [PATCH 10/97] Adding GitHub Sponsor to sponsorship options --- .github/FUNDING.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 82c461c7..2cf28e94 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1 +1,2 @@ +github: processing custom: https://processingfoundation.org/support From cdf11971c4dabd43e646b8cdef529f24c20acf1d Mon Sep 17 00:00:00 2001 From: ghalestrilo Date: Fri, 12 Jun 2020 16:36:31 -0300 Subject: [PATCH 11/97] =?UTF-8?q?=F0=9F=A7=B9use=20=20instead=20?= =?UTF-8?q?of=20?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/modules/IDE/pages/IDEViewMobile.jsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/client/modules/IDE/pages/IDEViewMobile.jsx b/client/modules/IDE/pages/IDEViewMobile.jsx index 1e3fb820..b8b50e92 100644 --- a/client/modules/IDE/pages/IDEViewMobile.jsx +++ b/client/modules/IDE/pages/IDEViewMobile.jsx @@ -3,7 +3,6 @@ import PropTypes from 'prop-types'; import styled from 'styled-components'; import { Link } from 'react-router'; -import Editor from '../components/Editor'; import { prop, remSize } from '../../../theme'; const background = prop('Button.default.background'); @@ -41,7 +40,7 @@ export default () => (


    This page is under construction. -
    Click here to return to the regular editor +
    Click here to return to the regular editor

    Bottom Bar

    From 8c33c6ebcadfd825745ef98b0192d6d10d0b1bb9 Mon Sep 17 00:00:00 2001 From: Casey Reas Date: Fri, 12 Jun 2020 15:06:24 -0700 Subject: [PATCH 12/97] Update FUNDING.yml --- .github/FUNDING.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 2cf28e94..93c4b27b 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,2 +1,2 @@ github: processing -custom: https://processingfoundation.org/support +custom: https://processingfoundation.org/ From 6d90dd2071f81eb2f6e8f78bc3791988ce8d193d Mon Sep 17 00:00:00 2001 From: Andrew Nicolaou Date: Sat, 13 Jun 2020 12:51:26 +0200 Subject: [PATCH 13/97] Do not log in test environment --- client/utils/getConfig.js | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/client/utils/getConfig.js b/client/utils/getConfig.js index 3d8331a2..43c050ae 100644 --- a/client/utils/getConfig.js +++ b/client/utils/getConfig.js @@ -1,17 +1,24 @@ +function isTestEnvironment() { + // eslint-disable-next-line no-use-before-define + return getConfig('NODE_ENV', { warn: false }) === 'test'; +} + /** * Returns config item from environment */ -export default function getConfig(key) { +function getConfig(key, options = { warn: !isTestEnvironment() }) { if (key == null) { throw new Error('"key" must be provided to getConfig()'); } - const __process = (typeof global !== 'undefined' ? global : window).process; - const value = __process.env[key]; + const env = (typeof global !== 'undefined' ? global : window)?.process?.env || {}; + const value = env[key]; - if (value == null) { + if (value == null && options?.warn !== false) { console.warn(`getConfig("${key}") returned null`); } return value; } + +export default getConfig; From fb7b3eaef48885206930b6fe7bdb5c847bd5b0d9 Mon Sep 17 00:00:00 2001 From: Cassie Tarakajian Date: Mon, 15 Jun 2020 13:14:48 -0400 Subject: [PATCH 14/97] [#1443] Add 'allow-downloads' to iframe sandbox --- client/modules/IDE/components/PreviewFrame.jsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/client/modules/IDE/components/PreviewFrame.jsx b/client/modules/IDE/components/PreviewFrame.jsx index ef19c96d..76be806f 100644 --- a/client/modules/IDE/components/PreviewFrame.jsx +++ b/client/modules/IDE/components/PreviewFrame.jsx @@ -353,6 +353,8 @@ class PreviewFrame extends React.Component { 'preview-frame': true, 'preview-frame--full-view': this.props.fullView }); + const sandboxAttributes = + 'allow-scripts allow-pointer-lock allow-same-origin allow-popups allow-forms allow-modals allow-downloads'; return (