@@ -4,29 +4,57 @@ import {connect} from 'react-redux';
44import log from './log' ;
55
66// eslint-disable-next-line import/no-commonjs
7- const { API_HOST } = require ( './brand' ) ;
7+ const { API_HOST , ASSET_HOST } = require ( './brand' ) ;
88
99import { setProjectTitle } from '../reducers/project-title' ;
1010import { setAuthor , setDescription } from '../reducers/tw' ;
1111
1212import storage from './storage' ;
1313
14- export const fetchProjectMeta = async projectId => {
14+ /**
15+ * Shared promise cache to prevent double-loading metadata
16+ * when both HOCs trigger at the same time.
17+ * (this is primarily due to Scratch Project Loading)
18+ */
19+ let activeFetchMetadataPromise = null ;
20+
21+ export const fetchProjectMeta = async ( projectId , isScratch ) => {
1522 const authToken = await storage . getProjectToken ( ) ;
16- const urls = [
23+ let urls = [
1724 `${ API_HOST } /v1/projects/blocks/${ projectId } /meta` ,
1825 `${ API_HOST } /v1/projects/blocks/${ projectId } /meta`
1926 ] ;
27+ if ( isScratch ) {
28+ urls = [
29+ `${ ASSET_HOST } /scratch_project_meta/${ projectId } ` ,
30+ `${ ASSET_HOST } /scratch_project_meta/${ projectId } `
31+ ] ;
32+ }
2033 let firstError ;
2134 for ( const url of urls ) {
2235 try {
2336 const res = await fetch ( url , {
24- headers : {
37+ headers : isScratch ? { } : {
2538 Authorization : `Bearer ${ authToken } `
26- } } ) ;
39+ }
40+ } ) ;
2741
2842 const data = await res . json ( ) ;
2943 if ( res . ok ) {
44+ if ( isScratch ) {
45+ storage . setScratchProjectToken ( data . project_token ) ; // so we can load actual project JSON file
46+ return {
47+ title : data . title ,
48+ author : {
49+ username : data . author . username ,
50+ PFP : data . author . profile . images [ '90x90' ]
51+ } ,
52+ instructions : data . instructions ,
53+ description : data . description ,
54+ canSave : 'false' ,
55+ canRemix : 'false'
56+ } ;
57+ }
3058 return data ;
3159 }
3260 if ( res . status === 404 ) {
@@ -42,6 +70,20 @@ export const fetchProjectMeta = async projectId => {
4270 throw firstError ;
4371} ;
4472
73+ export const fetchProjectMetaWithCache = ( projectId , isScratch ) => {
74+ if ( activeFetchMetadataPromise && activeFetchMetadataPromise . id === projectId ) {
75+ return activeFetchMetadataPromise . promise ;
76+ }
77+
78+ const promise = fetchProjectMeta ( projectId , isScratch ) ;
79+ activeFetchMetadataPromise = {
80+ id : projectId ,
81+ promise : promise . finally ( ( ) => {
82+ } )
83+ } ;
84+ return promise ;
85+ } ;
86+
4587const getNoIndexTag = ( ) => document . querySelector ( 'meta[name="robots"][content="noindex"]' ) ;
4688const setIndexable = indexable => {
4789 if ( indexable ) {
@@ -68,21 +110,22 @@ const TWProjectMetaFetcherHOC = function (WrappedComponent) {
68110 canEditTitle : false
69111 } ;
70112 }
113+
71114 componentDidUpdate ( prevProps ) {
72- // project title resetting is handled in titled-hoc.jsx
73- if ( this . props . reduxProjectId !== prevProps . reduxProjectId ) {
115+ if (
116+ this . props . reduxProjectId !== prevProps . reduxProjectId ||
117+ this . props . isScratchProject !== prevProps . isScratchProject
118+ ) {
74119 this . props . onSetAuthor ( '' , '' ) ;
75120 this . props . onSetDescription ( '' , '' ) ;
76121 const projectId = this . props . reduxProjectId ;
122+ const isScratch = this . props . isScratchProject ;
77123
78124 if ( projectId === '0' ) {
79- // don't try to get metadata
125+ activeFetchMetadataPromise = null ; // Reset cache on new project
80126 } else {
81- fetchProjectMeta ( projectId ) . then ( data => {
82- // If project ID changed, ignore the results.
83- if ( this . props . reduxProjectId !== projectId ) {
84- return ;
85- }
127+ fetchProjectMetaWithCache ( projectId , isScratch ) . then ( data => {
128+ if ( this . props . reduxProjectId !== projectId ) return ;
86129
87130 const title = data . title ;
88131 if ( title ) {
@@ -92,6 +135,7 @@ const TWProjectMetaFetcherHOC = function (WrappedComponent) {
92135 const authorName = data . author . username ;
93136 const authorThumbnail = data . author . PFP ;
94137 this . props . onSetAuthor ( authorName , authorThumbnail ) ;
138+
95139 const instructions = data . instructions || '' ;
96140 const credits = data . description || '' ;
97141 if ( instructions || credits ) {
@@ -105,11 +149,15 @@ const TWProjectMetaFetcherHOC = function (WrappedComponent) {
105149 canEditTitle : canSave // Enable title editing if user has save permissions
106150 } ) ;
107151
108- storage . setCloudOTT ( data ?. cloudDataOTT ) ;
109- storage . setCustomAchievements ( data ?. customAchievements ) ;
110- window . CollaborationRoom = data ?. collaboratorRoom ;
111- window . CollaborationUsername = data ?. username ;
112- window . collaborationOTT = data ?. collaborationOTT ;
152+ if ( isScratch ) {
153+ window . CollaborationRoom = null ;
154+ } else {
155+ storage . setCloudOTT ( data ?. cloudDataOTT ) ;
156+ storage . setCustomAchievements ( data ?. customAchievements ) ;
157+ window . CollaborationRoom = data ?. collaboratorRoom ;
158+ window . CollaborationUsername = data ?. username ;
159+ window . collaborationOTT = data ?. collaborationOTT ;
160+ }
113161 setIndexable ( true ) ;
114162 } )
115163 . catch ( err => {
@@ -126,6 +174,7 @@ const TWProjectMetaFetcherHOC = function (WrappedComponent) {
126174 const {
127175 /* eslint-disable no-unused-vars */
128176 reduxProjectId,
177+ isScratchProject,
129178 onSetAuthor,
130179 onSetDescription,
131180 onSetProjectTitle,
@@ -145,12 +194,14 @@ const TWProjectMetaFetcherHOC = function (WrappedComponent) {
145194 }
146195 ProjectMetaFetcherComponent . propTypes = {
147196 reduxProjectId : PropTypes . string ,
197+ isScratchProject : PropTypes . bool ,
148198 onSetAuthor : PropTypes . func ,
149199 onSetDescription : PropTypes . func ,
150200 onSetProjectTitle : PropTypes . func
151201 } ;
152202 const mapStateToProps = state => ( {
153- reduxProjectId : state . scratchGui . projectState . projectId
203+ reduxProjectId : state . scratchGui . projectState . projectId ,
204+ isScratchProject : state . scratchGui . projectState . isScratchProject
154205 } ) ;
155206 const mapDispatchToProps = dispatch => ( {
156207 onSetAuthor : ( username , thumbnail ) => dispatch ( setAuthor ( {
0 commit comments