Skip to content

Commit

Permalink
371 improve initialization (#427)
Browse files Browse the repository at this point in the history
* Improve function names and docs
* Rename settings
* Introduce new loading screen
* Simplify PORT logic
* Use new splash screen for loading dialogs
* Remove no longer needed Wait dialog (replaced by splash window)
* Small improvements to splash window
* Update dev dependencies
  • Loading branch information
pdelboca authored Jun 17, 2024
1 parent 65911b9 commit c0f4d26
Show file tree
Hide file tree
Showing 17 changed files with 171 additions and 172 deletions.
17 changes: 3 additions & 14 deletions client/client.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,18 @@
import omit from 'lodash/omit'
import * as settings from './settings'
import * as types from './types'

export class Client {
serverUrl?: string

constructor(props: { serverUrl?: string } = {}) {
this.serverUrl = props.serverUrl
}

async readServerUrl() {
return (
this.serverUrl ||
// @ts-ignore
(await window?.opendataeditor?.readServerUrl()) ||
settings.SERVER_URL
)
constructor() {
this.serverUrl = 'http://localhost:4040'
}

async request(
path: string,
props: { [key: string]: any; file?: File; isBytes?: boolean } = {}
) {
const serverUrl = await this.readServerUrl()
return makeRequest(serverUrl + path, props)
return makeRequest(this.serverUrl + path, props)
}

// Article
Expand Down
3 changes: 1 addition & 2 deletions client/components/Application/Dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ import DeleteFolderDialog from './Dialogs/DeleteFolder'
import IndexFilesDialog from './Dialogs/IndexFiles'
import MoveFileDialog from './Dialogs/MoveFile'
import MoveFolderDialog from './Dialogs/MoveFolder'
import StartDialog from './Dialogs/Start'
import { useStore } from './store'

export default function Dialog() {
const dialog = useStore((state) => state.dialog)
if (!dialog) return null
// @ts-ignore
const Dialog = DIALOGS[dialog]
return <Dialog />
}
Expand All @@ -34,5 +34,4 @@ const DIALOGS = {
indexFiles: IndexFilesDialog,
moveFile: MoveFileDialog,
moveFolder: MoveFolderDialog,
start: StartDialog,
}
20 changes: 0 additions & 20 deletions client/components/Application/Dialogs/Start.tsx

This file was deleted.

7 changes: 1 addition & 6 deletions client/components/Application/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,7 @@ export function makeStore(props: ApplicationProps) {

onStart: async () => {
const { client, loadConfig, loadFiles, updateState } = get()
updateState({ dialog: 'start' })

// Wait for the server
// @ts-ignore
const sendFatalError = window?.opendataeditor?.sendFatalError
let ready = false
Expand All @@ -101,21 +99,18 @@ export function makeStore(props: ApplicationProps) {
} catch (error) {
attempt += 1
if (attempt >= maxAttempts) {
const serverUrl = await client.readServerUrl()
const serverUrl = client.serverUrl
const message = `Client cannot connect to server on "${serverUrl}"`
sendFatalError ? sendFatalError(message) : alert(message)
}
await delay(delaySeconds * 1000)
}
}

// Setup project sync polling
setInterval(async () => {
const { files } = await client.projectSync({})
updateState({ files })
}, settings.PROJECT_SYNC_INTERVAL_MILLIS)

updateState({ dialog: undefined })
},
onFileCreate: async (paths) => {
const { loadFiles, selectFile } = get()
Expand Down
43 changes: 0 additions & 43 deletions client/components/Parts/Dialogs/Wait.tsx

This file was deleted.

71 changes: 71 additions & 0 deletions client/loading.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Open Data Editor</title>
<meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline';" />
<script>
window?.opendataeditor.ensureLogs((message) => {
if (message == "python") {
var element = document.getElementById("python")
var loader = document.getElementById("loader")
element.classList.toggle('pending')
element.removeChild(loader)
var next = document.getElementById("requirements")
next.appendChild(loader)
}
if (message == "requirements") {
var element = document.getElementById("requirements")
var loader = document.getElementById("loader")
element.classList.toggle('pending')
element.removeChild(loader)
var next = document.getElementById("server")
next.appendChild(loader)
}
if (message == "server") {
var element = document.getElementById("server")
var loader = document.getElementById("loader")
element.classList.toggle('pending')
element.removeChild(loader)
}
})
</script>
<style>
li {
color: black;
}
.pending {
color: grey;
}
.loader {
border: 5px solid grey;
border-bottom-color: transparent;
border-radius: 50%;
display: inline-block;
box-sizing: border-box;
animation: rotation 1s linear infinite;
}
@keyframes rotation {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
</style>
</head>
<body>
<section style="display:grid; place-items: center;">
<div>
<h1 style="text-align: center;">Open Data Editor is loading</h1>
<p style="text-align: center;">The first time launching Open Data Editor needs to ensure requirements are installed.</p>
<ul id="logs">
<li class="pending" id="python">Ensure python is installed <span id="loader" class="loader"></span></li>
<li class="pending" id="requirements">Ensure python dependencies are installed</li>
<li class="pending" id="server">Ensure server is running</li>
</ul>
</div>
</section>
</body>
</html>
5 changes: 1 addition & 4 deletions desktop/bridge.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
import { ipcMain, dialog, app } from 'electron'
import log from 'electron-log'

export function createBridge({ serverPort }: { serverPort: number }) {
ipcMain.handle('readServerUrl', async () => {
return `http://localhost:${serverPort}`
})
export function createBridge() {
ipcMain.handle('sendFatalError', async (_ev, message: string) => {
log.error(message)
await dialog.showMessageBox({
Expand Down
20 changes: 3 additions & 17 deletions desktop/index.ts
Original file line number Diff line number Diff line change
@@ -1,35 +1,21 @@
import { app, dialog, BrowserWindow } from 'electron'
import { electronApp, optimizer } from '@electron-toolkit/utils'
import { electronApp, optimizer } from '@electron-toolkit/utils'
import { createWindow } from './window'
import { createBridge } from './bridge'
import { is } from '@electron-toolkit/utils'
import { join } from 'path'
import log from 'electron-log'
import * as system from './system'
import * as server from './server'
import * as python from './python'
import * as settings from './settings'
import * as resources from './resources'

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.whenReady().then(async () => {
log.info('# Start application')
electronApp.setAppUserModelId(settings.APP_USER_MODEL_ID)
const serverPort = await system.findPort()

log.info('## Start client')
createBridge({ serverPort })
createWindow()

if (!is.dev) {
log.info('## Start server')
await resources.ensureRunner()
await python.ensurePython()
await python.ensureLibraries()
await server.runServer({ serverPort })
}
createBridge()
await createWindow()
})

// Default open or close DevTools by F12 in development
Expand Down
2 changes: 1 addition & 1 deletion desktop/preload/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { contextBridge, ipcRenderer } from 'electron'

contextBridge.exposeInMainWorld('opendataeditor', {
readServerUrl: () => ipcRenderer.invoke('readServerUrl'),
sendFatalError: (message: string) => ipcRenderer.invoke('sendFatalError', message),
openDirectoryDialog: () => ipcRenderer.invoke('openDirectoryDialog'),
ensureLogs: (callback: any) => ipcRenderer.on('ensureLogs', (_event, message: string) => callback(message))
})
18 changes: 10 additions & 8 deletions desktop/python.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,22 @@ import log from 'electron-log'
import toml from 'toml'
import * as system from './system'

export async function ensurePython() {
log.info('[ensurePython]', { path: settings.APP_PYTHON })
export async function ensurePythonVirtualEnvironment() {
// When running, ODE will ensure that a virtual environment for dependencies exists
log.info('[ensurePythonVirtualEnvironment]', { path: settings.APP_PYTHON_VENV })

let message = 'existed'
if (!fs.existsSync(settings.APP_PYTHON)) {
await system.execFile(settings.PYTHON_SOURCE, ['-m', 'venv', settings.APP_PYTHON])
if (!fs.existsSync(settings.APP_PYTHON_VENV)) {
await system.execFile(settings.PYTHON_SOURCE, ['-m', 'venv', settings.APP_PYTHON_VENV])
message = 'created'
}

log.info('[ensurePython]', { message })
log.info('[ensurePythonVirtualEnvironment]', { message })
}

export async function ensureLibraries() {
log.info('[ensureLibraries]')
export async function ensurePythonRequirements() {
// When running, ODE will ensure that all python dependencies are installed.
log.info('[ensurePythonRequirements]')

const required = await readRequiredLibraries()
const installed = await readInstalledLibraries()
Expand All @@ -35,7 +37,7 @@ export async function ensureLibraries() {
...missing,
])

log.info('[ensureLibraries]', { missing })
log.info('[ensurePythonRequirements]', { missing })
}

export async function readRequiredLibraries() {
Expand Down
14 changes: 8 additions & 6 deletions desktop/resources.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,20 @@ import fsp from 'fs/promises'
import * as settings from './settings'
import log from 'electron-log'

export async function ensureRunner() {
log.info('[ensureRunner]', { path: settings.APP_RUNNER })
export async function ensurePython() {
// ODE builds a Python 3.10 distribution and ships it with the app.
// It is generated when running make build
log.info('[ensurePython]', { path: settings.APP_PYTHON })

let message = 'existed'
if (!fs.existsSync(settings.APP_RUNNER)) {
await fsp.mkdir(settings.APP_RUNNER, { recursive: true })
await fsp.cp(settings.DIST_RUNNER, settings.APP_RUNNER, {
if (!fs.existsSync(settings.APP_PYTHON)) {
await fsp.mkdir(settings.APP_PYTHON, { recursive: true })
await fsp.cp(settings.DIST_PYTHON, settings.APP_PYTHON, {
recursive: true,
verbatimSymlinks: true,
})
message = 'created'
}

log.info('[ensureRunner]', { message })
log.info('[ensurePython]', { message })
}
8 changes: 4 additions & 4 deletions desktop/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ import { spawnFile } from './system'
import * as settings from './settings'
import log from 'electron-log'

export async function runServer({ serverPort }: { serverPort: number }) {
log.info('[runServer]', { serverPort })
export async function runServer() {
log.info(`[Run FastAPI server on port 4040`)

// Start server
// Start production server
const proc = spawnFile(
settings.PYTHON_TARGET,
['-m', 'server', settings.APP_TMP, '--port', serverPort.toString()],
['-m', 'server', settings.APP_TMP, '--port', `4040`],
process.resourcesPath
)

Expand Down
13 changes: 5 additions & 8 deletions desktop/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,17 @@ import { join } from 'path'
export const WIN = process.platform === 'win32'
export const HOME = os.homedir()

export const PORT_DEV = 4040
export const PORT_PROD = 4444

export const DIST = process.resourcesPath
export const DIST_RUNNER = join(DIST, 'runner')
export const DIST_PYTHON = join(DIST, 'runner')
export const DIST_SERVER = join(DIST, 'server')

export const APP_NAME = 'opendataeditor'
export const APP_USER_MODEL_ID = 'org.opendataeditor'
export const APP_HOME = join(HOME, `.${APP_NAME}`)
export const APP_RUNNER = join(APP_HOME, 'runner')
export const APP_PYTHON = join(APP_HOME, 'python')
export const APP_PYTHON = join(APP_HOME, 'runner')
export const APP_PYTHON_VENV = join(APP_HOME, 'python')
// APP_TMP will be the folder to upload files the first time the user opens the Application
export const APP_TMP = join(APP_HOME, 'tmp')

export const PYTHON_SOURCE = join(APP_RUNNER, WIN ? 'python.exe' : 'bin/python3')
export const PYTHON_TARGET = join(APP_PYTHON, WIN ? 'Scripts\\python.exe' : 'bin/python3')
export const PYTHON_SOURCE = join(APP_PYTHON, WIN ? 'python.exe' : 'bin/python3')
export const PYTHON_TARGET = join(APP_PYTHON_VENV, WIN ? 'Scripts\\python.exe' : 'bin/python3')
Loading

0 comments on commit c0f4d26

Please sign in to comment.