@@ -35,11 +35,17 @@ export type Project = {
3535 opModes : storageModule . OpMode [ ] ,
3636} ;
3737
38+ const CURRENT_VERSION = '0.0.1' ;
39+
40+ type ProjectInfo = {
41+ version : string ,
42+ } ;
43+
3844/**
3945 * Returns the list of project names.
4046 */
4147export async function listProjectNames ( storage : commonStorage . Storage ) : Promise < string [ ] > {
42- const filePathRegexPattern = '.*/Robot\.robot\.json$' ;
48+ const filePathRegexPattern = storageNames . REGEX_ROBOT_MODULE_PATH ;
4349 const robotModulePaths : string [ ] = await storage . listFilePaths ( filePathRegexPattern ) ;
4450
4551 const projectNames : string [ ] = [ ] ;
@@ -119,6 +125,7 @@ export async function createProject(
119125 const opmodeContent = storageModuleContent . newOpModeContent (
120126 newProjectName , storageNames . CLASS_NAME_TELEOP ) ;
121127 await storage . saveFile ( opmodePath , opmodeContent ) ;
128+ await saveProjectInfo ( storage , newProjectName ) ;
122129}
123130
124131/**
@@ -161,6 +168,10 @@ async function renameOrCopyProject(
161168 await storage . deleteFile ( modulePath ) ;
162169 }
163170 }
171+ await saveProjectInfo ( storage , newProjectName ) ;
172+ if ( rename ) {
173+ await deleteProjectInfo ( storage , projectName ) ;
174+ }
164175}
165176
166177/**
@@ -177,6 +188,7 @@ export async function deleteProject(
177188 for ( const modulePath of modulePaths ) {
178189 await storage . deleteFile ( modulePath ) ;
179190 }
191+ await deleteProjectInfo ( storage , projectName ) ;
180192}
181193
182194/**
@@ -215,6 +227,7 @@ export async function addModuleToProject(
215227 } as storageModule . OpMode ) ;
216228 break ;
217229 }
230+ await saveProjectInfo ( storage , project . projectName ) ;
218231}
219232
220233/**
@@ -238,6 +251,7 @@ export async function removeModuleFromProject(
238251 break ;
239252 }
240253 await storage . deleteFile ( modulePath ) ;
254+ await saveProjectInfo ( storage , project . projectName ) ;
241255 }
242256}
243257
@@ -331,6 +345,7 @@ async function renameOrCopyModule(
331345 break ;
332346 }
333347 }
348+ await saveProjectInfo ( storage , project . projectName ) ;
334349
335350 return newModulePath ;
336351}
@@ -404,20 +419,20 @@ export function findModuleByModulePath(project: Project, modulePath: string): st
404419 */
405420export async function downloadProject (
406421 storage : commonStorage . Storage , projectName : string ) : Promise < string > {
407- const modulePaths : string [ ] = await storage . listFilePaths (
408- storageNames . makeModulePathRegexPattern ( projectName ) ) ;
409-
410- const fileNameToModuleContentText : { [ fileName : string ] : string } = { } ; // value is module content text
411- for ( const modulePath of modulePaths ) {
412- const fileName = storageNames . getFileName ( modulePath ) ;
413- const moduleContentText = await storage . fetchFileContentText ( modulePath ) ;
414- fileNameToModuleContentText [ fileName ] = moduleContentText ;
422+ const filePaths : string [ ] = await storage . listFilePaths (
423+ storageNames . makeFilePathRegexPattern ( projectName ) ) ;
424+
425+ const fileNameToFileContentText : { [ fileName : string ] : string } = { } ; // value is file content text
426+ for ( const filePath of filePaths ) {
427+ const fileName = storageNames . getFileName ( filePath ) ;
428+ const fileContentText = await storage . fetchFileContentText ( filePath ) ;
429+ fileNameToFileContentText [ fileName ] = fileContentText ;
415430 }
416431
417432 const zip = new JSZip ( ) ;
418- for ( const fileName in fileNameToModuleContentText ) {
419- const moduleContentText = fileNameToModuleContentText [ fileName ] ;
420- zip . file ( fileName , moduleContentText ) ;
433+ for ( const fileName in fileNameToFileContentText ) {
434+ const fileContentText = fileNameToFileContentText [ fileName ] ;
435+ zip . file ( fileName , fileContentText ) ;
421436 }
422437 const content = await zip . generateAsync ( { type : "blob" } ) ;
423438 return URL . createObjectURL ( content ) ;
@@ -436,20 +451,19 @@ export function makeUploadProjectName(
436451export async function uploadProject (
437452 storage : commonStorage . Storage , projectName : string , blobUrl : string ) : Promise < void > {
438453 // Process the uploaded blob to get the file names and contents.
439- const fileNameToModuleContentText = await processUploadedBlob ( blobUrl ) ;
440-
441- // Save each module.
442- for ( const fileName in fileNameToModuleContentText ) {
443- const moduleContentText = fileNameToModuleContentText [ fileName ] ;
444- const className = storageNames . getClassName ( fileName ) ;
445- const moduleType = storageNames . getModuleType ( fileName ) ;
446- const modulePath = storageNames . makeModulePath ( projectName , className , moduleType ) ;
447- await storage . saveFile ( modulePath , moduleContentText ) ;
454+ const fileNameToFileContentText = await processUploadedBlob ( blobUrl ) ;
455+
456+ // Save each file.
457+ for ( const fileName in fileNameToFileContentText ) {
458+ const fileContentText = fileNameToFileContentText [ fileName ] ;
459+ const filePath = storageNames . makeFilePath ( projectName , fileName ) ;
460+ await storage . saveFile ( filePath , fileContentText ) ;
448461 }
462+ await saveProjectInfo ( storage , projectName ) ;
449463}
450464
451465/**
452- * Process the uploaded blob to get the module file names and file contents.
466+ * Process the uploaded blob to get the file names and file contents.
453467 */
454468async function processUploadedBlob ( blobUrl : string ) : Promise < { [ fileName : string ] : string } > {
455469
@@ -473,22 +487,56 @@ async function processUploadedBlob(blobUrl: string): Promise<{ [fileName: string
473487 } )
474488 ) ;
475489
476- // Process each module 's content.
490+ // Process each file 's content.
477491 let foundRobot = false ;
478- const fileNameToModuleContentText : { [ fileName : string ] : string } = { } ; // value is module content text
492+ const fileNameToFileContentText : { [ fileName : string ] : string } = { } ; // value is file content text
479493 for ( const fileName in files ) {
480- const moduleType = storageNames . getModuleType ( fileName ) ;
481- if ( moduleType === storageModule . ModuleType . ROBOT ) {
482- foundRobot = true ;
494+ if ( storageNames . isValidProjectInfoFileName ( fileName ) ) {
495+ // Make sure we can parse the content.
496+ parseProjectInfoContentText ( files [ fileName ] ) ;
497+ } else if ( storageNames . isValidModuleFileName ( fileName ) ) {
498+ const moduleType = storageNames . getModuleType ( fileName ) ;
499+ if ( moduleType === storageModule . ModuleType . ROBOT ) {
500+ foundRobot = true ;
501+ }
502+ // Make sure we can parse the content.
503+ storageModuleContent . parseModuleContentText ( files [ fileName ] ) ;
504+ } else {
505+ throw new Error ( 'Uploaded project file contains one or more unexpected files.' ) ;
483506 }
484- // Make sure we can parse the content.
485- const moduleContent = storageModuleContent . parseModuleContentText ( files [ fileName ] ) ;
486- fileNameToModuleContentText [ fileName ] = moduleContent . getModuleContentText ( ) ;
507+ fileNameToFileContentText [ fileName ] = files [ fileName ] ;
487508 }
488509
489510 if ( ! foundRobot ) {
490- throw new Error ( 'Uploaded file did not contain a Robot.' ) ;
511+ throw new Error ( 'Uploaded project file did not contain a Robot.' ) ;
512+ }
513+
514+ return fileNameToFileContentText ;
515+ }
516+
517+ export async function saveProjectInfo (
518+ storage : commonStorage . Storage , projectName : string ) : Promise < void > {
519+ const projectInfo : ProjectInfo = {
520+ version : CURRENT_VERSION ,
521+ } ;
522+ const projectInfoContentText = JSON . stringify ( projectInfo , null , 2 ) ;
523+ const projectInfoPath = storageNames . makeProjectInfoPath ( projectName ) ;
524+ await storage . saveFile ( projectInfoPath , projectInfoContentText ) ;
525+ }
526+
527+ function parseProjectInfoContentText ( projectInfoContentText : string ) : ProjectInfo {
528+ const parsedContent = JSON . parse ( projectInfoContentText ) ;
529+ if ( ! ( 'version' in parsedContent ) ) {
530+ throw new Error ( 'Project info content text is not valid.' ) ;
491531 }
532+ const projectInfo : ProjectInfo = {
533+ version : parsedContent . version ,
534+ } ;
535+ return projectInfo ;
536+ }
492537
493- return fileNameToModuleContentText ;
538+ async function deleteProjectInfo (
539+ storage : commonStorage . Storage , projectName : string ) : Promise < void > {
540+ const projectInfoPath = storageNames . makeProjectInfoPath ( projectName ) ;
541+ await storage . deleteFile ( projectInfoPath ) ;
494542}
0 commit comments