Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Electron build of JBrowse desktop #520

Merged
merged 45 commits into from
Oct 30, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
f5b36ac
Initial simple electron app shell
garrettjstevens Sep 23, 2019
82336f7
Initial CRA run
garrettjstevens Sep 23, 2019
a7c6c12
Use rescripts to un-eject CRA
garrettjstevens Sep 24, 2019
4ed9eda
Update eslint rules, fix a lot of lint errors
garrettjstevens Sep 24, 2019
a0fbab2
Use node 10 in CI
garrettjstevens Sep 24, 2019
29470e5
Add back accidentally delected action
garrettjstevens Sep 24, 2019
b9779df
Copy jbrowse-web to jb-sv-inspector-desktop
garrettjstevens Sep 24, 2019
b61b077
Add back webpack-cli
garrettjstevens Sep 25, 2019
0aeefd3
WIP
garrettjstevens Sep 29, 2019
33c7e2b
More electron WIP
garrettjstevens Oct 1, 2019
a0b619e
Initial working electron with local file via IPC
garrettjstevens Oct 2, 2019
60221a1
Delete a file and fix the lockfile
garrettjstevens Oct 2, 2019
b8adc2b
Make ElectronLocalFile not crash jbrowse-web
garrettjstevens Oct 2, 2019
8e324be
Remove redundant tests and update snapshots
garrettjstevens Oct 2, 2019
0c25d44
Use app.getAppPath() instead of __dirName
garrettjstevens Oct 2, 2019
b629c82
More strictly emulate fs.promises.read() behavior
garrettjstevens Oct 3, 2019
75f1b0d
Make jbrowse-web production build work again
garrettjstevens Oct 6, 2019
447c2bb
Update deps
garrettjstevens Oct 6, 2019
f0242c5
Get rid of worker stuff in jb-desktop
garrettjstevens Oct 6, 2019
0b53659
Log the block error so you can see a stack trace
garrettjstevens Oct 9, 2019
23e873e
Properly pass timeout to rpc's call
garrettjstevens Oct 9, 2019
d72089e
Move where block error is logged from
garrettjstevens Oct 9, 2019
8c62a02
Missed file from 23e873e
garrettjstevens Oct 9, 2019
6ba4712
Electron window worker working with svg/div renderers
garrettjstevens Oct 9, 2019
ac572de
Send canvas data over electron IPC as dataURL
garrettjstevens Oct 11, 2019
58fedfe
Remove duplicated error log
garrettjstevens Oct 11, 2019
30979be
Terminate window workers on page reload
garrettjstevens Oct 11, 2019
55c6ee2
Factor out BaseRpcDriver class
garrettjstevens Oct 11, 2019
2f6d315
Add ability to desktop app to view non-cors-enabled files
garrettjstevens Oct 25, 2019
ea98564
Update rangeFetcher TypeScript
garrettjstevens Oct 25, 2019
f3d5155
Add basic start screen with recent sessions
garrettjstevens Oct 26, 2019
4273713
accept a DEV_SERVER_URL environment variable for electron.js for deve…
rbuels Oct 28, 2019
7f31bb9
Outline for a new session area in start screen
garrettjstevens Oct 28, 2019
d8df1c6
Add new linear genome view of dataset option to start screen
garrettjstevens Oct 28, 2019
065d8a2
Make window bigger, don't show menu bar, fix worker path name
garrettjstevens Oct 28, 2019
54648d0
Use config template if no config found on startup
garrettjstevens Oct 29, 2019
0e0704c
Update to new gnomAD SV VCF
garrettjstevens Oct 29, 2019
7ff8b45
Factor our new session cards
garrettjstevens Oct 29, 2019
7eb935c
Make a "back to welcome screen" entry in file menu
garrettjstevens Oct 30, 2019
9f56fe4
Add "factory reset" to advanced settings
garrettjstevens Oct 30, 2019
f569ab7
jbrowse-sv-inspector-desktop -> jbrowse-desktop
garrettjstevens Oct 30, 2019
4def34d
Merge branch 'master' into cra_rescripts
garrettjstevens Oct 30, 2019
e053747
Fix title and description in index.html
garrettjstevens Oct 30, 2019
593588a
Simplify with fs rename and path extname, basename
garrettjstevens Oct 30, 2019
8e64e7b
Add attribution to QuickLRU
garrettjstevens Oct 30, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Add basic start screen with recent sessions
  • Loading branch information
garrettjstevens committed Oct 26, 2019
commit f3d51555b02a81afb05be7b2efa6e1bb3684eac1
27 changes: 25 additions & 2 deletions packages/core/rpc/ElectronRpcDriver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ class WindowWorkerHandle {

private window: import('electron').BrowserWindow

private ready = false

constructor(
ipcRenderer: import('electron-better-ipc-extra').RendererProcessIpc,
window: import('electron').BrowserWindow,
Expand All @@ -23,12 +25,33 @@ class WindowWorkerHandle {
this.window = window
}

async wait(ms: number): Promise<void> {
return new Promise((resolve): void => {
setTimeout(resolve, ms)
})
}

destroy(): void {
this.window.destroy()
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
call(functionName: string, filteredArgs?: any, options = {}): any {
async call(
functionName: string,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
filteredArgs?: any,
options = {},
// eslint-disable-next-line @typescript-eslint/no-explicit-any
): Promise<any> {
// The window can have been created, but still not be ready, and any
// `callRenderer` call to that window just returns Promise<undefined>
// instead of an error, which makes failures hard to track down. For now
// we'll just wait until it's ready until we find a better option.
while (!this.ready) {
// eslint-disable-next-line no-await-in-loop
await this.wait(1000)
// eslint-disable-next-line no-await-in-loop
this.ready = !!(await this.ipcRenderer.callRenderer(this.window, 'ready'))
}
return this.ipcRenderer.callRenderer(
this.window,
'call',
Expand Down
111 changes: 109 additions & 2 deletions packages/jbrowse-sv-inspector-desktop/public/electron.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,32 @@ const fs = require('fs')
const { promisify } = require('util')
const fetch = require('node-fetch')

const fsCopyFile = promisify(fs.copyFile)
const fsFStat = promisify(fs.fstat)
const fsOpen = promisify(fs.open)
const fsRead = promisify(fs.read)
const fsFStat = promisify(fs.fstat)
const fsReaddir = promisify(fs.readdir)
const fsReadFile = promisify(fs.readFile)
const fsStat = promisify(fs.stat)
const fsUnlink = promisify(fs.unlink)
const fsWriteFile = promisify(fs.writeFile)

const { app } = electron
const { BrowserWindow } = electron

const path = require('path')
const isDev = require('electron-is-dev')

const configLocation = path.join(app.getPath('userData'), 'config.json')
const sessionDirectory = path.join(app.getPath('userData'), 'sessions')
try {
fs.statSync(sessionDirectory)
} catch (error) {
if (error.code === 'ENOENT' || error.code === 'ENOTDIR')
fs.mkdirSync(sessionDirectory)
else throw error
}

let mainWindow

function createWindow() {
Expand All @@ -36,7 +51,7 @@ function createWindow() {
if (isDev) {
// Open the DevTools.
// BrowserWindow.addDevToolsExtension('<location to your react chrome extension>');
mainWindow.webContents.openDevTools()
// mainWindow.webContents.openDevTools()
}
mainWindow.on('closed', () => {
BrowserWindow.getAllWindows().forEach(win => win.close())
Expand Down Expand Up @@ -76,6 +91,98 @@ ipcMain.on('createWindowWorker', event => {
event.returnValue = workerWindow.id
})

ipcMain.answerRenderer('loadConfig', async () => {
let configJSON
try {
configJSON = await fsReadFile(configLocation, { encoding: 'utf8' })
} catch (error) {
if (error.code === 'ENOENT') {
// make a config file since one does not exist yet
configJSON = '{}'
await fsWriteFile(configLocation, configJSON)
} else throw error
}
return JSON.parse(configJSON)
})

ipcMain.answerRenderer('saveConfig', async configSnapshot => {
return fsWriteFile(configLocation, JSON.stringify(configSnapshot))
})

ipcMain.answerRenderer('listSessions', async () => {
const sessionFiles = await fsReaddir(sessionDirectory)
const sessionFilesData = []
for (const sessionFile of sessionFiles) {
if (sessionFile.endsWith('.thumbnail'))
sessionFilesData.push(
fsReadFile(path.join(sessionDirectory, sessionFile), {
encoding: 'utf8',
}),
)
else sessionFilesData.push(fsStat(path.join(sessionDirectory, sessionFile)))
}
const data = await Promise.all(sessionFilesData)
const sessions = {}
sessionFiles.forEach((sessionFile, idx) => {
if (sessionFile.endsWith('.thumbnail')) {
const sessionName = sessionFile.slice(0, -10)
if (!sessions[sessionName]) sessions[sessionName] = {}
sessions[sessionName].screenshot = data[idx]
} else if (sessionFile.endsWith('.json')) {
const sessionName = sessionFile.slice(0, -5)
if (!sessions[sessionName]) sessions[sessionName] = {}
sessions[sessionName].stats = data[idx]
}
})
return sessions
})

ipcMain.answerRenderer('loadSession', async sessionName => {
return fsReadFile(path.join(sessionDirectory, `${sessionName}.json`), {
encoding: 'utf8',
})
})

ipcMain.answerRenderer(
'saveSession',
async (sessionSnapshot, sessionScreenshot) => {
await fsWriteFile(
path.join(sessionDirectory, `${sessionSnapshot.name}.thumbnail`),
sessionScreenshot,
)
return fsWriteFile(
path.join(sessionDirectory, `${sessionSnapshot.name}.json`),
JSON.stringify(sessionSnapshot),
)
},
)

ipcMain.answerRenderer('renameSession', async (oldName, newName) => {
try {
await fsCopyFile(
path.join(sessionDirectory, `${oldName}.thumbnail`),
path.join(sessionDirectory, `${newName}.thumbnail`),
)
await fsUnlink(path.join(sessionDirectory, `${oldName}.thumbnail`))
} catch {
// ignore
}
await fsCopyFile(
path.join(sessionDirectory, `${oldName}.json`),
path.join(sessionDirectory, `${newName}.json`),
)
await fsUnlink(path.join(sessionDirectory, `${oldName}.json`))
})

ipcMain.answerRenderer('deleteSession', async sessionName => {
try {
await fsUnlink(path.join(sessionDirectory, `${sessionName}.thumbnail`))
} catch {
// ignore
}
return fsUnlink(path.join(sessionDirectory, `${sessionName}.json`))
})

ipcMain.answerRenderer('fetch', async (...args) => {
const response = await fetch(...args)
const serializableResponse = {}
Expand Down
2 changes: 1 addition & 1 deletion packages/jbrowse-sv-inspector-desktop/public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
<title>JBrowse</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
Expand Down
Loading