From 05e8b26a34d1a749b174660c6b9afef3cd5c6de6 Mon Sep 17 00:00:00 2001
From: Christopher Willis-Ford <7019101+cwillisf@users.noreply.github.com>
Date: Fri, 4 Sep 2020 13:26:34 -0700
Subject: [PATCH] WIP: actually load project from command line

Doing it this way works for the initial load but overrides later actions
like File -> New.
---
 src/main/index.js    | 27 ++++++++++++++++++++++++++-
 src/renderer/app.jsx | 31 ++++++++++++++++++++-----------
 2 files changed, 46 insertions(+), 12 deletions(-)

diff --git a/src/main/index.js b/src/main/index.js
index c806962..1545521 100644
--- a/src/main/index.js
+++ b/src/main/index.js
@@ -2,6 +2,7 @@ import {BrowserWindow, Menu, app, dialog, ipcMain, systemPreferences} from 'elec
 import fs from 'fs-extra';
 import path from 'path';
 import {URL} from 'url';
+import {promisify} from 'util';
 
 import argv from './argv';
 import {getFilterForExtension} from './FileFilters';
@@ -375,4 +376,28 @@ ipcMain.on('open-about-window', () => {
     _windows.about.show();
 });
 
-ipcMain.handle('get-argv', () => argv);
+// start loading initial project data before the GUI needs it so the load seems faster
+const initialProjectDataPromise = (async () => {
+    if (argv._.length === 0) {
+        // no command line argument means no initial project data
+        return;
+    }
+    if (argv._.length > 1) {
+        log.warn(`Expected 1 command line argument but received ${argv._.length}.`);
+    }
+    const projectPath = argv._[argv._.length - 1];
+    try {
+        const projectData = await promisify(fs.readFile)(projectPath, null);
+        return projectData;
+    } catch (e) {
+        dialog.showMessageBox(_windows.main, {
+            type: 'error',
+            title: 'Failed to load project',
+            message: `Could not load project from file:\n${projectPath}`,
+            detail: e.message
+        });
+    }
+    // load failed: initial project data undefined
+})(); // IIFE
+
+ipcMain.handle('get-initial-project-data', () => initialProjectDataPromise);
diff --git a/src/renderer/app.jsx b/src/renderer/app.jsx
index e3c1169..9ad78c0 100644
--- a/src/renderer/app.jsx
+++ b/src/renderer/app.jsx
@@ -6,7 +6,6 @@ import {compose} from 'redux';
 import GUI, {AppStateHOC} from 'scratch-gui';
 
 import ElectronStorageHelper from '../common/ElectronStorageHelper';
-import log from '../common/log';
 
 import styles from './app.css';
 
@@ -40,8 +39,16 @@ const ScratchDesktopHOC = function (WrappedComponent) {
                 'handleUpdateProjectTitle'
             ]);
             this.state = {
-                projectTitle: null
+                projectTitle: null,
+                projectLoading: true
             };
+
+            ipcRenderer.invoke('get-initial-project-data').then(projectData => {
+                this.setState({
+                    projectData,
+                    projectLoading: false
+                });
+            });
         }
         componentDidMount () {
             ipcRenderer.on('setTitleFromSave', this.handleSetTitleFromSave);
@@ -72,6 +79,11 @@ const ScratchDesktopHOC = function (WrappedComponent) {
         }
         render () {
             const shouldShowTelemetryModal = (typeof ipcRenderer.sendSync('getTelemetryDidOptIn') !== 'boolean');
+
+            if (this.state.projectLoading) {
+                return <p className="splash">Loading File...</p>;
+            }
+
             return (<WrappedComponent
                 canEditTitle
                 isScratchDesktop
@@ -84,6 +96,12 @@ const ScratchDesktopHOC = function (WrappedComponent) {
                 onTelemetryModalOptIn={this.handleTelemetryModalOptIn}
                 onTelemetryModalOptOut={this.handleTelemetryModalOptOut}
                 onUpdateProjectTitle={this.handleUpdateProjectTitle}
+
+                // completely omit the projectData prop if we have no project data
+                // passing an empty projectData causes a GUI error
+                {...(this.state.projectData ? {projectData: this.state.projectData} : {})}
+
+                // allow passed-in props to override any of the above
                 {...this.props}
             />);
         }
@@ -100,13 +118,4 @@ const WrappedGui = compose(
     AppStateHOC
 )(GUI);
 
-ipcRenderer.invoke('get-argv').then(
-    argv => {
-        log.log(`argv._ = ${argv._}`);
-    },
-    err => {
-        log.warn('Failed to retrieve argv', err);
-    }
-);
-
 ReactDOM.render(<WrappedGui />, appTarget);