From e6843fb47aba9534e6ddf9a3944248919cf0fee7 Mon Sep 17 00:00:00 2001 From: Aquino Luane Date: Thu, 3 Oct 2024 17:24:46 +0900 Subject: [PATCH 1/9] poc: get project files and store in redux --- src/lib/project-fetcher-hoc.jsx | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/lib/project-fetcher-hoc.jsx b/src/lib/project-fetcher-hoc.jsx index 3f4613719..c39f339b1 100644 --- a/src/lib/project-fetcher-hoc.jsx +++ b/src/lib/project-fetcher-hoc.jsx @@ -72,7 +72,29 @@ const ProjectFetcherHOC = function (WrappedComponent) { .load(storage.AssetType.Project, projectId, storage.DataFormat.JSON) .then(projectAsset => { if (projectAsset) { - this.props.onFetchedProjectData(projectAsset.data, loadingState); + // Fetch the .sb3 file from the API + fetch("http://localhost:3000/download") + .then((response) => { + if (!response.ok) { + throw new Error( + "Network response was not ok" + ); + } + return response.arrayBuffer(); // Get the binary data + }) + .then((projectData) => { + // Load the .sb3 file into store + this.props.onFetchedProjectData( + projectData, + loadingState + ); + }) + .catch((error) => { + console.error( + "There was a problem with the fetch operation:", + error + ); + }); } else { // Treat failure to load as an error // Throw to be caught by catch later on From 07facdfc1ef95f3c2db9bd4a48ecc20006ba3410 Mon Sep 17 00:00:00 2001 From: Aquino Luane Date: Wed, 9 Oct 2024 12:14:33 +0900 Subject: [PATCH 2/9] refactor: improve way of fetching project files --- src/lib/project-fetcher-hoc.jsx | 26 ++------------------------ src/lib/storage.js | 2 +- src/reducers/project-state.js | 2 +- 3 files changed, 4 insertions(+), 26 deletions(-) diff --git a/src/lib/project-fetcher-hoc.jsx b/src/lib/project-fetcher-hoc.jsx index c39f339b1..b8f9ca651 100644 --- a/src/lib/project-fetcher-hoc.jsx +++ b/src/lib/project-fetcher-hoc.jsx @@ -72,29 +72,7 @@ const ProjectFetcherHOC = function (WrappedComponent) { .load(storage.AssetType.Project, projectId, storage.DataFormat.JSON) .then(projectAsset => { if (projectAsset) { - // Fetch the .sb3 file from the API - fetch("http://localhost:3000/download") - .then((response) => { - if (!response.ok) { - throw new Error( - "Network response was not ok" - ); - } - return response.arrayBuffer(); // Get the binary data - }) - .then((projectData) => { - // Load the .sb3 file into store - this.props.onFetchedProjectData( - projectData, - loadingState - ); - }) - .catch((error) => { - console.error( - "There was a problem with the fetch operation:", - error - ); - }); + this.props.onFetchedProjectData(projectAsset.data, loadingState); } else { // Treat failure to load as an error // Throw to be caught by catch later on @@ -153,7 +131,7 @@ const ProjectFetcherHOC = function (WrappedComponent) { }; ProjectFetcherComponent.defaultProps = { assetHost: 'https://assets.scratch.mit.edu', - projectHost: 'https://projects.scratch.mit.edu' + projectHost: 'http://localhost:3000/download' }; const mapStateToProps = state => ({ diff --git a/src/lib/storage.js b/src/lib/storage.js index a3f240685..5226c281a 100644 --- a/src/lib/storage.js +++ b/src/lib/storage.js @@ -36,7 +36,7 @@ class Storage extends ScratchStorage { this.projectHost = projectHost; } getProjectGetConfig (projectAsset) { - return `${this.projectHost}/${projectAsset.assetId}`; + return this.projectHost } getProjectCreateConfig () { return { diff --git a/src/reducers/project-state.js b/src/reducers/project-state.js index 76955d589..ec5fa5c2e 100644 --- a/src/reducers/project-state.js +++ b/src/reducers/project-state.js @@ -23,7 +23,7 @@ const START_REMIXING = 'scratch-gui/project-state/START_REMIXING'; const START_UPDATING_BEFORE_CREATING_COPY = 'scratch-gui/project-state/START_UPDATING_BEFORE_CREATING_COPY'; const START_UPDATING_BEFORE_CREATING_NEW = 'scratch-gui/project-state/START_UPDATING_BEFORE_CREATING_NEW'; -const defaultProjectId = '0'; // hardcoded id of default project +const defaultProjectId = '123'; // hardcoded id of default project const LoadingState = keyMirror({ NOT_LOADED: null, From 7ecf17c98a8715212d3901ba0bb3da1c13d8f4bc Mon Sep 17 00:00:00 2001 From: Aquino Luane Date: Fri, 18 Oct 2024 16:30:18 +0900 Subject: [PATCH 3/9] feat: consume project from mock api passing project id parameter --- src/lib/project-fetcher-hoc.jsx | 2 +- src/lib/storage.js | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/lib/project-fetcher-hoc.jsx b/src/lib/project-fetcher-hoc.jsx index b8f9ca651..a8a93b590 100644 --- a/src/lib/project-fetcher-hoc.jsx +++ b/src/lib/project-fetcher-hoc.jsx @@ -131,7 +131,7 @@ const ProjectFetcherHOC = function (WrappedComponent) { }; ProjectFetcherComponent.defaultProps = { assetHost: 'https://assets.scratch.mit.edu', - projectHost: 'http://localhost:3000/download' + projectHost: 'http://localhost:3000/ccm/scratch-api/projects' }; const mapStateToProps = state => ({ diff --git a/src/lib/storage.js b/src/lib/storage.js index 5226c281a..9f00c9911 100644 --- a/src/lib/storage.js +++ b/src/lib/storage.js @@ -36,7 +36,11 @@ class Storage extends ScratchStorage { this.projectHost = projectHost; } getProjectGetConfig (projectAsset) { - return this.projectHost + // FIXME: remove projectAsset mock when real data is returned + const projectAssetMock = { + assetId: 1 + } + return `${this.projectHost}/${projectAssetMock.assetId}`; } getProjectCreateConfig () { return { From 9061af9c3691ae30e20a45135ae83c4f73070a7d Mon Sep 17 00:00:00 2001 From: Aquino Luane Date: Tue, 22 Oct 2024 18:17:47 +0900 Subject: [PATCH 4/9] feat: set hash router to show project id in the address bar --- src/lib/hash-parser-hoc.jsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/lib/hash-parser-hoc.jsx b/src/lib/hash-parser-hoc.jsx index 26ceb22e8..bb139ddca 100644 --- a/src/lib/hash-parser-hoc.jsx +++ b/src/lib/hash-parser-hoc.jsx @@ -23,6 +23,10 @@ const HashParserHOC = function (WrappedComponent) { } componentDidMount () { window.addEventListener('hashchange', this.handleHashChange); + // FIXME: replace mock projectId with real one + window.addEventListener('load', () => { + window.location.hash = '/project/1' + }); this.handleHashChange(); } componentDidUpdate (prevProps) { From 7c771fd4faef28072f6e3152743483cde59fb05b Mon Sep 17 00:00:00 2001 From: Aquino Luane Date: Mon, 28 Oct 2024 14:20:05 +0900 Subject: [PATCH 5/9] feat: use projectId from redux --- src/lib/storage.js | 6 +----- src/reducers/project-state.js | 2 +- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/lib/storage.js b/src/lib/storage.js index 9f00c9911..a3f240685 100644 --- a/src/lib/storage.js +++ b/src/lib/storage.js @@ -36,11 +36,7 @@ class Storage extends ScratchStorage { this.projectHost = projectHost; } getProjectGetConfig (projectAsset) { - // FIXME: remove projectAsset mock when real data is returned - const projectAssetMock = { - assetId: 1 - } - return `${this.projectHost}/${projectAssetMock.assetId}`; + return `${this.projectHost}/${projectAsset.assetId}`; } getProjectCreateConfig () { return { diff --git a/src/reducers/project-state.js b/src/reducers/project-state.js index ec5fa5c2e..e5e413170 100644 --- a/src/reducers/project-state.js +++ b/src/reducers/project-state.js @@ -23,7 +23,7 @@ const START_REMIXING = 'scratch-gui/project-state/START_REMIXING'; const START_UPDATING_BEFORE_CREATING_COPY = 'scratch-gui/project-state/START_UPDATING_BEFORE_CREATING_COPY'; const START_UPDATING_BEFORE_CREATING_NEW = 'scratch-gui/project-state/START_UPDATING_BEFORE_CREATING_NEW'; -const defaultProjectId = '123'; // hardcoded id of default project +const defaultProjectId = '1'; // hardcoded id of default project const LoadingState = keyMirror({ NOT_LOADED: null, From dd9971591710903fa543a2382ebc454b9bd8059e Mon Sep 17 00:00:00 2001 From: Aquino Luane Date: Mon, 28 Oct 2024 15:04:53 +0900 Subject: [PATCH 6/9] feat: set projectId from url --- src/lib/hash-parser-hoc.jsx | 2 +- src/reducers/project-state.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/hash-parser-hoc.jsx b/src/lib/hash-parser-hoc.jsx index bb139ddca..dd6a6e227 100644 --- a/src/lib/hash-parser-hoc.jsx +++ b/src/lib/hash-parser-hoc.jsx @@ -41,7 +41,7 @@ const HashParserHOC = function (WrappedComponent) { window.removeEventListener('hashchange', this.handleHashChange); } handleHashChange () { - const hashMatch = window.location.hash.match(/#(\d+)/); + const hashMatch = window.location.hash.match(/\/(\d+)$/); const hashProjectId = hashMatch === null ? defaultProjectId : hashMatch[1]; this.props.setProjectId(hashProjectId.toString()); } diff --git a/src/reducers/project-state.js b/src/reducers/project-state.js index e5e413170..76955d589 100644 --- a/src/reducers/project-state.js +++ b/src/reducers/project-state.js @@ -23,7 +23,7 @@ const START_REMIXING = 'scratch-gui/project-state/START_REMIXING'; const START_UPDATING_BEFORE_CREATING_COPY = 'scratch-gui/project-state/START_UPDATING_BEFORE_CREATING_COPY'; const START_UPDATING_BEFORE_CREATING_NEW = 'scratch-gui/project-state/START_UPDATING_BEFORE_CREATING_NEW'; -const defaultProjectId = '1'; // hardcoded id of default project +const defaultProjectId = '0'; // hardcoded id of default project const LoadingState = keyMirror({ NOT_LOADED: null, From abbfbda9e66535ffa7d7a42471f8e347954e1bb7 Mon Sep 17 00:00:00 2001 From: Aquino Luane Date: Tue, 29 Oct 2024 14:52:07 +0900 Subject: [PATCH 7/9] feat: save project to server --- src/lib/project-saver-hoc.jsx | 20 +------------------- src/lib/save-project-to-server.js | 2 +- src/playground/render-gui.jsx | 2 +- 3 files changed, 3 insertions(+), 21 deletions(-) diff --git a/src/lib/project-saver-hoc.jsx b/src/lib/project-saver-hoc.jsx index d00dff948..06096adf8 100644 --- a/src/lib/project-saver-hoc.jsx +++ b/src/lib/project-saver-hoc.jsx @@ -226,25 +226,7 @@ const ProjectSaverHOC = function (WrappedComponent) { // serialized project refers to a newer asset than what // we just finished saving). const savedVMState = this.props.vm.toJSON(); - return Promise.all(this.props.vm.assets - .filter(asset => !asset.clean) - .map( - asset => storage.store( - asset.assetType, - asset.dataFormat, - asset.data, - asset.assetId - ).then(response => { - // Asset servers respond with {status: ok} for successful POSTs - if (response.status !== 'ok') { - // Errors include a `code` property, e.g. "Forbidden" - return Promise.reject(response.code); - } - asset.clean = true; - }) - ) - ) - .then(() => this.props.onUpdateProjectData(projectId, savedVMState, requestParams)) + return this.props.onUpdateProjectData(projectId, savedVMState, requestParams) .then(response => { this.props.onSetProjectUnchanged(); const id = response.id.toString(); diff --git a/src/lib/save-project-to-server.js b/src/lib/save-project-to-server.js index 2584923e6..b435d7234 100644 --- a/src/lib/save-project-to-server.js +++ b/src/lib/save-project-to-server.js @@ -21,7 +21,7 @@ export default function (projectId, vmState, params) { headers: { 'Content-Type': 'application/json' }, - withCredentials: true + withCredentials: false // FIXME: check if this should be true }; const creatingProject = projectId === null || typeof projectId === 'undefined'; const queryParams = {}; diff --git a/src/playground/render-gui.jsx b/src/playground/render-gui.jsx index 0f15fbfa7..9fc31e13d 100644 --- a/src/playground/render-gui.jsx +++ b/src/playground/render-gui.jsx @@ -78,7 +78,7 @@ export default appTarget => { backpackVisible showComingSoon backpackHost={backpackHost} - canSave={false} + canSave={true} onClickLogo={onClickLogo} />, appTarget); From 21305597124d89ba0eb0d9f1fc2512171b4a737b Mon Sep 17 00:00:00 2001 From: Aquino Luane Date: Fri, 10 Jan 2025 10:50:08 +0900 Subject: [PATCH 8/9] feat(autosave): send request to api --- src/components/menu-bar/menu-bar.jsx | 5 ++--- src/lib/project-saver-hoc.jsx | 2 +- src/lib/save-project-to-server.js | 15 ++++++++++----- src/utils/constants.js | 1 + 4 files changed, 14 insertions(+), 9 deletions(-) create mode 100644 src/utils/constants.js diff --git a/src/components/menu-bar/menu-bar.jsx b/src/components/menu-bar/menu-bar.jsx index fc00b7a41..53c7a21d7 100644 --- a/src/components/menu-bar/menu-bar.jsx +++ b/src/components/menu-bar/menu-bar.jsx @@ -547,8 +547,7 @@ class MenuBar extends React.Component { */} - {/* hide this code for first release */} - {/* {this.props.canEditTitle ? ( + {this.props.canEditTitle ? (
- ) : null)} */} + ) : null)} {/* hide share button for first release */} {/*
{this.props.canShare ? ( diff --git a/src/lib/project-saver-hoc.jsx b/src/lib/project-saver-hoc.jsx index 06096adf8..823c13e0f 100644 --- a/src/lib/project-saver-hoc.jsx +++ b/src/lib/project-saver-hoc.jsx @@ -226,7 +226,7 @@ const ProjectSaverHOC = function (WrappedComponent) { // serialized project refers to a newer asset than what // we just finished saving). const savedVMState = this.props.vm.toJSON(); - return this.props.onUpdateProjectData(projectId, savedVMState, requestParams) + return this.props.onUpdateProjectData(projectId, savedVMState, requestParams, this.props.reduxProjectTitle) .then(response => { this.props.onSetProjectUnchanged(); const id = response.id.toString(); diff --git a/src/lib/save-project-to-server.js b/src/lib/save-project-to-server.js index b435d7234..96b6211b7 100644 --- a/src/lib/save-project-to-server.js +++ b/src/lib/save-project-to-server.js @@ -1,6 +1,7 @@ import queryString from 'query-string'; import xhr from 'xhr'; import storage from '../lib/storage'; +import { BASE_API_URL } from '../utils/constants'; /** * Save a project JSON to the project server. @@ -14,14 +15,18 @@ import storage from '../lib/storage'; * @property {?string} params.title the title of the project. * @return {Promise} A promise that resolves when the network request resolves. */ -export default function (projectId, vmState, params) { +export default function (projectId, vmState, params, projectTitle) { const opts = { - body: vmState, + body: JSON.stringify({ + file: vmState, + name: projectTitle, + }), // If we set json:true then the body is double-stringified, so don't + // FIXME: pass token to authorization header headers: { - 'Content-Type': 'application/json' + 'Content-Type': 'application/json', + 'Authorization': `Bearer 123` }, - withCredentials: false // FIXME: check if this should be true }; const creatingProject = projectId === null || typeof projectId === 'undefined'; const queryParams = {}; @@ -39,7 +44,7 @@ export default function (projectId, vmState, params) { } else { Object.assign(opts, { method: 'put', - url: `${storage.projectHost}/${projectId}${qs}` + url: `${BASE_API_URL}/projects/${projectId}` }); } return new Promise((resolve, reject) => { diff --git a/src/utils/constants.js b/src/utils/constants.js new file mode 100644 index 000000000..dfb96300d --- /dev/null +++ b/src/utils/constants.js @@ -0,0 +1 @@ +export const BASE_API_URL = "http://adventure-lab-cms.docker.amazee.io/md/api"; From 9cbfde2d2d0e9925807e891a014d331b5191a922 Mon Sep 17 00:00:00 2001 From: Aquino Luane Date: Fri, 10 Jan 2025 15:52:16 +0900 Subject: [PATCH 9/9] feat(error handling): add a link to go back to project list when there is an error --- .../crash-message/crash-message.css | 4 ++++ .../crash-message/crash-message.jsx | 21 ++----------------- 2 files changed, 6 insertions(+), 19 deletions(-) diff --git a/src/components/crash-message/crash-message.css b/src/components/crash-message/crash-message.css index 605f7fdab..5d8b39df8 100644 --- a/src/components/crash-message/crash-message.css +++ b/src/components/crash-message/crash-message.css @@ -26,3 +26,7 @@ font-size: 0.875rem; cursor: pointer; } + +.text-link { + color: white; +} diff --git a/src/components/crash-message/crash-message.jsx b/src/components/crash-message/crash-message.jsx index c3732ddc0..6d0d1accb 100644 --- a/src/components/crash-message/crash-message.jsx +++ b/src/components/crash-message/crash-message.jsx @@ -5,6 +5,7 @@ import {FormattedMessage} from 'react-intl'; import styles from './crash-message.css'; import reloadIcon from './reload.svg'; +import { BASE_API_URL } from '../../utils/constants.js'; const CrashMessage = props => (
@@ -20,15 +21,6 @@ const CrashMessage = props => ( id="gui.crashMessage.label" /> -

- -

{props.eventId && (

( />

)} - + Go back to project list
);