Skip to content

Commit

Permalink
Clean up code
Browse files Browse the repository at this point in the history
  • Loading branch information
SleepyLeslie committed Jun 20, 2024
1 parent fb23dff commit 5d9991a
Show file tree
Hide file tree
Showing 4 changed files with 42 additions and 67 deletions.
49 changes: 24 additions & 25 deletions ext/app/electron/GristApp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import * as log from "app/server/lib/log";
import * as path from "path";
import * as shutdown from "app/server/lib/shutdown";
import * as winston from "winston";
import { ElectronServerMethods, FlexServer } from "app/server/lib/FlexServer";
import { GristDesktopAuthMode, getMinimalElectronLoginSystem } from "app/electron/logins";
import AppMenu from "app/electron/AppMenu";
import { FlexServer } from "app/server/lib/FlexServer";
import RecentItems from "app/common/RecentItems";
import { UpdateManager } from "app/electron/UpdateManager";
import { makeId } from "app/server/lib/idUtils";
Expand All @@ -18,13 +18,13 @@ import webviewOptions from "app/electron/webviewOptions";
export class GristApp {
private flexServer: FlexServer;
private app = electron.app;
private appWindows = new Set(); // A set of all our window objects.
private appHost: any = null; // The hostname to connect to the local node server we start.
private pendingPathToOpen: any = null; // Path to open when app is started to open a document.
private appWindows: Set<electron.BrowserWindow> = new Set(); // A set of all our window objects.
private appHost: string; // The hostname to connect to the local node server we start.
private pendingPathToOpen: string | undefined = undefined; // Path to open when app is started to open a document.

// Function, set once the app is ready, that opens or focuses a window when Grist is started. It
// is called on 'ready' and by onInstanceStart (triggered when starting another Grist instance).
private onStartup: any = null;
private onStartup: (optPath?: string) => Promise<void>;
private credential: string = makeId();
private shouldQuit = false;
private authMode: GristDesktopAuthMode;
Expand Down Expand Up @@ -74,36 +74,36 @@ export class GristApp {
this.app.whenReady().then(() => this.onReady().catch(reportErrorAndStop));
}

private onInstanceStart(argv: any, workingDir: any) {
private onInstanceStart(argv: string[], workingDir: string) {
argv = this.cleanArgv(argv);
// Someone tried to run a second instance, we should either open a file or focus a window.
log.debug("onInstanceStart %s in %s", JSON.stringify(argv), workingDir);
if (this.onStartup) {
this.onStartup(argv[1] ? path.resolve(workingDir, argv[1]) : null);
this.onStartup(argv[1] ? path.resolve(workingDir, argv[1]) : undefined);
}
}

private cleanArgv(argv: any) {
private cleanArgv(argv: string[]) {
// Ignoring flags starting with '-' which might be added by electron on Mac (See
// https://phab.getgrist.com/T307).
return argv.filter((arg: any) => !arg.startsWith('-'));
return argv.filter((arg) => !arg.startsWith('-'));
}

private openWindowForPath(path: string, openWith?: {loadURL: (url: string) => Promise<void>}) {
private openWindowForDoc(docID: string, openWith?: {loadURL: (url: string) => Promise<void>}) {
// Create the browser window, and load the document.
(openWith || this.createWindow()).loadURL(this.getUrl({doc: path}));
(openWith || this.createWindow()).loadURL(this.getUrl(docID));
}

// Opens file at filepath for any accepted file type.
private async handleOpen(serverMethods: any, filepath: string) {
private async handleOpen(serverMethods: ElectronServerMethods, filepath: string) {
log.debug("handleOpen %s", filepath);
const ext = path.extname(filepath);
switch (ext) {
case '.csv':
case '.xlsx':
case '.xlsm': {
const docName = serverMethods.importDoc(filepath);
this.openWindowForPath(docName);
const doc = await serverMethods.importDoc(filepath);
this.openWindowForDoc(doc.id);
break;
}
default:
Expand All @@ -112,12 +112,10 @@ export class GristApp {
}
}

private getUrl(options: {
doc?: string,
} = {}) {
private getUrl(docID?: string) {
const url = new URL(this.appHost);
if (options.doc) {
url.pathname = 'doc/' + encodeURIComponent(options.doc);
if (docID) {
url.pathname = 'doc/' + encodeURIComponent(docID);
}
if (this.authMode !== 'none') {
url.searchParams.set('electron_key', this.credential);
Expand Down Expand Up @@ -171,7 +169,7 @@ export class GristApp {
if (!await fse.pathExists(link)) {
await fse.symlink(target, link, 'junction');
}
this.openWindowForPath(docId, openWith);
this.openWindowForDoc(docId, openWith);
}

// Returns the last Grist window that was created.
Expand Down Expand Up @@ -261,7 +259,7 @@ export class GristApp {

if (process.env.GRIST_LOG_PATH || fse.existsSync(debugLogPath)) {
const output = fse.createWriteStream(debugLogPath, { flags: "a" });
output.on('error', (err: any) => log.error("Failed to open %s: %s", debugLogPath, err));
output.on('error', (err) => log.error("Failed to open %s: %s", debugLogPath, err));
output.on('open', () => {
log.info('Logging also to %s', debugLogPath);
output.write('\n--- log starting by pid ' + process.pid + ' ---\n');
Expand All @@ -284,7 +282,8 @@ export class GristApp {
}

private async onReady() {
this.appHost = process.env.APP_HOME_URL;
// APP_HOME_URL is set by loadConfig
this.appHost = process.env.APP_HOME_URL as string;

await updateDb();

Expand All @@ -297,15 +296,15 @@ export class GristApp {
// This function is what we'll call now, and also in onInstanceStart. The latter is used on
// Windows thanks to makeSingleInstance, and triggered when user clicks another .grist file.
// We can only set this callback once we have serverMethods and appHost.
this.onStartup = async (optPath: any) => {
this.onStartup = async (optPath?: string) => {
log.debug("onStartup %s", optPath);
if (optPath) {
await this.handleOpen(serverMethods, optPath);
return;
}
const win = this.getLastWindow();
if (win) {
(win as any).show();
win.show();
return;
}
// We had no file to open, so open a window to the DocList.
Expand All @@ -314,7 +313,7 @@ export class GristApp {

// Call onStartup immediately.
this.onStartup(this.pendingPathToOpen);
this.pendingPathToOpen = null;
this.pendingPathToOpen = undefined;

const recentItems = new RecentItems({
maxCount: 10,
Expand Down
52 changes: 17 additions & 35 deletions ext/app/electron/config.ts
Original file line number Diff line number Diff line change
@@ -1,36 +1,17 @@
import * as dotenv from "dotenv";
import * as electron from "electron";
import * as fse from "fs-extra";
import * as log from "app/server/lib/log";
import * as net from 'net';
import * as packageJson from "desktop.package.json";
import * as path from "path";
import bluebird from 'bluebird';
import { commonUrls } from "app/common/gristUrls";
import { getAvailablePort } from "app/server/lib/serverUtils";

const NO_VALIDATION = () => true;

const NO_VALIDATION = () => true;

/**
* Copied from grist-core, since it is unsafe to import core code at this point.
*/
async function getAvailablePort(firstPort: number = 8000, optCount: number = 200): Promise<number> {
const lastPort = firstPort + optCount - 1;
async function checkNext(port: number): Promise<number> {
if (port > lastPort) {
throw new Error("No available ports between " + firstPort + " and " + lastPort);
}
return new bluebird((resolve: (p: number) => void, reject: (e: Error) => void) => {
const server = net.createServer();
server.on('error', reject);
server.on('close', () => resolve(port));
server.listen(port, 'localhost', () => server.close());
})
.catch(() => checkNext(port + 1));
}
return bluebird.try(() => checkNext(firstPort));
}

function check(envKey: string, validator: (value: string) => boolean, defaultValue: string,): void {
function validateOrFallback(envKey: string, validator: (value: string) => boolean, defaultValue: string,): void {
const envValue = process.env[envKey];
if (envValue === undefined) {
log.warn(`${envKey} is not set, using default value ${defaultValue}`);
Expand All @@ -45,6 +26,7 @@ function check(envKey: string, validator: (value: string) => boolean, defaultVal


export async function loadConfig() {
dotenv.config();
if (process.env.GRIST_ELECTRON_AUTH !== undefined) {
if (process.env.GRIST_DESKTOP_AUTH === undefined) {
process.env.GRIST_DESKTOP_AUTH = process.env.GRIST_ELECTRON_AUTH;
Expand All @@ -53,22 +35,22 @@ export async function loadConfig() {
log.warn("GRIST_DESKTOP_AUTH set, ignoring GRIST_ELECTRON_AUTH (deprecated).");
}
}
check(
validateOrFallback(
"GRIST_DEFAULT_USERNAME",
NO_VALIDATION,
"You"
);
check(
validateOrFallback(
"GRIST_DEFAULT_EMAIL",
NO_VALIDATION,
"you@example.com"
);
check(
validateOrFallback(
"GRIST_HOST",
NO_VALIDATION,
"localhost"
);
check(
validateOrFallback(
"GRIST_PORT",
(portstr) => {
if (! /^\d+$/.test(portstr)) {
Expand All @@ -79,43 +61,43 @@ export async function loadConfig() {
},
(await getAvailablePort(47478)).toString()
);
check(
validateOrFallback(
"GRIST_DESKTOP_AUTH",
(auth) => ["strict", "none", "mixed"].includes(auth),
"strict"
);
check(
validateOrFallback(
"GRIST_SANDBOX_FLAVOR",
(flavor) => ["pyodide", "unsandboxed", "gvisor", "macSandboxExec"].includes(flavor),
"pyodide"
);
check(
validateOrFallback(
"GRIST_INST_DIR",
NO_VALIDATION,
electron.app.getPath("userData")
);
check(
validateOrFallback(
"GRIST_DATA_DIR",
NO_VALIDATION,
electron.app.getPath("documents")
);
check(
validateOrFallback(
"GRIST_USER_ROOT",
NO_VALIDATION,
path.join(electron.app.getPath("home"), ".grist")
);
check(
validateOrFallback(
"TYPEORM_DATABASE",
NO_VALIDATION,
path.join(electron.app.getPath("appData"), "landing.db")
);
check(
validateOrFallback(
"GRIST_WIDGET_LIST_URL", // Related to plugins (Would have to be changed if local custom widgets are used?)
NO_VALIDATION,
commonUrls.gristLabsWidgetRepository
);

const homeDBLocation = path.parse((process.env.TYPEORM_DATABASE as string)).dir;
const homeDBLocation = path.parse(path.resolve(process.env.TYPEORM_DATABASE as string)).dir;
if (!fse.existsSync(homeDBLocation)) {
log.warn(`Directory to contain the home DB does not exist, creating ${homeDBLocation}`);
fse.mkdirSync(homeDBLocation);
Expand Down
8 changes: 1 addition & 7 deletions ext/app/electron/main.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import * as dotenv from "dotenv";
import * as electron from "electron";
import * as path from "path";
import { program } from "commander";
Expand All @@ -18,6 +17,7 @@ if (!electron.app.isPackaged) {
// eslint-disable-next-line sort-imports
import * as packageJson from "desktop.package.json";
import * as version from "app/common/version";
import { GristApp } from "app/electron/GristApp";
import { loadConfig } from "app/electron/config";

program.name(packageJson.name).version(`${packageJson.productName} ${packageJson.version} (with Grist Core ${version.version})`);
Expand All @@ -30,12 +30,6 @@ if (!electron.app.isPackaged) {
process.argv.splice(1, 1);
}

dotenv.config();

loadConfig().then(() => {
// Note: TYPEORM_DATABASE must be set before importing dbUtils, or else it won't take effect.
// As Grist code could pull dbUtils in implicitly, it is unsafe to import anything from Grist Core before this.
// eslint-disable-next-line @typescript-eslint/no-var-requires
const GristApp = require("app/electron/GristApp").GristApp;
new GristApp().main();
});
Binary file added test.db
Binary file not shown.

0 comments on commit 5d9991a

Please sign in to comment.