Skip to content

Commit

Permalink
Fix install plugin workflow and error handling on desktop, update to …
Browse files Browse the repository at this point in the history
…electron 15 (#2322)

* Add text searching to desktop and allow it to reload after installing plugins

* Update to electron 15

* Rearrange some error handling

* Avoid the volatile for pluginsUpdated, and instead update directly in
setPluginsUpdated to avoid 1s delay
  • Loading branch information
cmdcolin committed Sep 22, 2021
1 parent 77f6c7a commit e07b309
Show file tree
Hide file tree
Showing 11 changed files with 238 additions and 67 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@
"cross-spawn": "^7.0.1",
"del": "^5.1.0",
"dependency-graph": "^0.9.0",
"electron": "13.1.2",
"electron": "15.0.0",
"electron-builder": "^22.1.0",
"electron-mock-ipc": "^0.3.8",
"electron-notarize": "^1.0.0",
Expand Down
2 changes: 1 addition & 1 deletion products/jbrowse-desktop/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@
"last 1 chrome version"
],
"build": {
"electronVersion": "13.1.2",
"electronVersion": "15.0.0",
"extraMetadata": {
"main": "build/electron.js"
},
Expand Down
20 changes: 11 additions & 9 deletions products/jbrowse-desktop/public/electron.ts
Original file line number Diff line number Diff line change
Expand Up @@ -221,19 +221,21 @@ ipcMain.handle('listSessions', async () => {
)
})

ipcMain.handle('loadExternalConfig', (_event: unknown, sessionPath) =>
readFile(sessionPath, 'utf8'),
)
ipcMain.handle('loadSession', (_event: unknown, sessionName: string) =>
readFile(getPath(sessionName), 'utf8'),
)
ipcMain.handle('loadExternalConfig', (_event: unknown, sessionPath) => {
return readFile(sessionPath, 'utf8')
})

ipcMain.handle('loadSession', (_event: unknown, sessionName: string) => {
return readFile(getPath(sessionName), 'utf8')
})

ipcMain.on('saveSession', async (_event: unknown, snap: SessionSnap) => {
ipcMain.handle('saveSession', async (_event: unknown, snap: SessionSnap) => {
const page = await mainWindow?.capturePage()
const name = snap.defaultSession.name
if (page) {
writeFile(getPath(snap.defaultSession.name, 'thumbnail'), page.toDataURL())
await writeFile(getPath(name, 'thumbnail'), page.toDataURL())
}
writeFile(getPath(snap.defaultSession.name), JSON.stringify(snap, null, 2))
await writeFile(getPath(name), JSON.stringify(snap, null, 2))
})

ipcMain.handle(
Expand Down
114 changes: 104 additions & 10 deletions products/jbrowse-desktop/src/Loader.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,117 @@
import React, { useState } from 'react'
import PluginManager from '@jbrowse/core/PluginManager'
import { CssBaseline, ThemeProvider } from '@material-ui/core'
import React, { useState, useCallback, useEffect } from 'react'
import { observer } from 'mobx-react'
import PluginManager from '@jbrowse/core/PluginManager'
import { CssBaseline, ThemeProvider, makeStyles } from '@material-ui/core'
import { createJBrowseTheme } from '@jbrowse/core/ui'
import { StringParam, useQueryParam } from 'use-query-params'
import { ipcRenderer } from 'electron'
import { createPluginManager } from './StartScreen/util'

import JBrowse from './JBrowse'
import StartScreen from './StartScreen'
import { createJBrowseTheme } from '@jbrowse/core/ui'

function Loader() {
const useStyles = makeStyles(theme => ({
message: {
border: '1px solid black',
overflow: 'auto',
maxHeight: 200,
margin: theme.spacing(1),
padding: theme.spacing(1),
},

errorBox: {
background: 'lightgrey',
border: '1px solid black',
margin: 20,
},
}))

const ErrorMessage = ({
error,
snapshotError,
}: {
error: Error
snapshotError?: string
}) => {
const classes = useStyles()
return (
<div className={classes.message} style={{ background: '#f88' }}>
{`${error}`}
{snapshotError ? (
<>
... Failed element had snapshot:
<pre className={classes.errorBox}>
{JSON.stringify(JSON.parse(snapshotError), null, 2)}
</pre>
</>
) : null}
</div>
)
}

const Loader = observer(() => {
const [pluginManager, setPluginManager] = useState<PluginManager>()
const [config, setConfig] = useQueryParam('config', StringParam)
const [error, setError] = useState<Error>()
const [snapshotError, setSnapshotError] = useState('')

function handleError(e: Error) {
const match = e.message.match(
/.*at path "(.*)" snapshot `(.*)` is not assignable/,
)

// best effort to make a better error message than the default
// mobx-state-tree
if (match) {
setError(new Error(`Failed to load element at ${match[1]}`))
setSnapshotError(match[2])
} else {
setError(new Error(e.message.slice(0, 10000)))
}
console.error(e)
}

const handleSetPluginManager = useCallback(
(pm: PluginManager) => {
setPluginManager(pm)
setError(undefined)
setSnapshotError('')
setConfig('')
},
[setConfig],
)

useEffect(() => {
;(async () => {
if (config) {
try {
const data = await ipcRenderer.invoke('loadSession', config)
const pm = await createPluginManager(JSON.parse(data))
handleSetPluginManager(pm)
} catch (e) {
handleError(e)
}
}
})()
}, [config, handleSetPluginManager])

return (
<ThemeProvider theme={createJBrowseTheme()}>
<CssBaseline />

{error ? (
<ErrorMessage error={error} snapshotError={snapshotError} />
) : null}
{pluginManager?.rootModel?.session ? (
<JBrowse pluginManager={pluginManager} />
) : (
<StartScreen setPluginManager={setPluginManager} />
)}
) : !config || error ? (
<StartScreen
setError={handleError}
setPluginManager={handleSetPluginManager}
/>
) : null}
</ThemeProvider>
)
}
})

export default observer(Loader)
export default Loader
77 changes: 77 additions & 0 deletions products/jbrowse-desktop/src/StartScreen/data/preloadedConfigs.js
Original file line number Diff line number Diff line change
Expand Up @@ -203,11 +203,47 @@ const preloadedConfigs = {
},
},
},
{
type: 'FeatureTrack',
trackId: 'ncbi_refseq_109_hg38_latest',
name: 'NCBI RefSeq',
assemblyNames: ['hg38'],
category: ['Annotation'],
adapter: {
type: 'Gff3TabixAdapter',
gffGzLocation: {
uri:
'https://s3.amazonaws.com/jbrowse.org/genomes/GRCh38/ncbi_refseq/GRCh38_latest_genomic.sort.gff.gz',
},
index: {
location: {
uri:
'https://s3.amazonaws.com/jbrowse.org/genomes/GRCh38/ncbi_refseq/GRCh38_latest_genomic.sort.gff.gz.tbi',
},
},
},
},
],

defaultSession: {
name: 'New Session',
},
aggregateTextSearchAdapters: [
{
type: 'TrixTextSearchAdapter',
textSearchAdapterId: 'hg38-index',
ixFilePath: {
uri: 'https://jbrowse.org/genomes/GRCh38/trix/hg38.ix',
},
ixxFilePath: {
uri: 'https://jbrowse.org/genomes/GRCh38/trix/hg38.ixx',
},
metaFilePath: {
uri: 'https://jbrowse.org/genomes/GRCh38/trix/meta.json',
},
assemblyNames: ['hg38'],
},
],
},
hg19: {
assemblies: [
Expand Down Expand Up @@ -243,6 +279,30 @@ const preloadedConfigs = {
},
],
tracks: [
{
type: 'FeatureTrack',
trackId: 'ncbi_gff_hg19',
name: 'NCBI RefSeq',
assemblyNames: ['hg19'],
category: ['Annotation'],
metadata: {
source: 'https://www.ncbi.nlm.nih.gov/genome/guide/human/',
dateaccessed: '12/03/2020',
},
adapter: {
type: 'Gff3TabixAdapter',
gffGzLocation: {
uri:
'https://s3.amazonaws.com/jbrowse.org/genomes/hg19/ncbi_refseq/GRCh37_latest_genomic.sort.gff.gz',
},
index: {
location: {
uri:
'https://s3.amazonaws.com/jbrowse.org/genomes/hg19/ncbi_refseq/GRCh37_latest_genomic.sort.gff.gz.tbi',
},
},
},
},
{
type: 'FeatureTrack',
trackId: 'repeats_hg19',
Expand Down Expand Up @@ -397,6 +457,23 @@ const preloadedConfigs = {
defaultSession: {
name: 'New Session',
},

aggregateTextSearchAdapters: [
{
type: 'TrixTextSearchAdapter',
textSearchAdapterId: 'hg19-index',
ixFilePath: {
uri: 'https://jbrowse.org/genomes/hg19/trix/hg19.ix',
},
ixxFilePath: {
uri: 'https://jbrowse.org/genomes/hg19/trix/hg19.ixx',
},
metaFilePath: {
uri: 'https://jbrowse.org/genomes/hg19/trix/meta.json',
},
assemblyNames: ['hg19'],
},
],
},
mm10: {
assemblies: [
Expand Down
10 changes: 3 additions & 7 deletions products/jbrowse-desktop/src/StartScreen/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,10 @@ function LogoWithVersion() {
}
export default function StartScreen({
setPluginManager,
setError,
}: {
setPluginManager: (arg: PluginManager) => void
setError: (arg: Error) => void
}) {
const classes = useStyles()
const [sessions, setSessions] = useState<Map<string, SessionStats>>()
Expand All @@ -90,7 +92,6 @@ export default function StartScreen({
const [factoryResetDialogOpen, setFactoryResetDialogOpen] = useState(false)
const [updateSessionsList, setUpdateSessionsList] = useState(true)
const [menuAnchorEl, setMenuAnchorEl] = useState<HTMLElement | null>(null)
const [error, setError] = useState<Error>()

const sessionNames = useMemo(() => Object.keys(sessions || {}), [sessions])

Expand All @@ -110,10 +111,9 @@ export default function StartScreen({
}
} catch (e) {
setError(e)
console.error(e)
}
})()
}, [updateSessionsList])
}, [setError, updateSessionsList])

if (!sessions) {
return (
Expand Down Expand Up @@ -156,7 +156,6 @@ export default function StartScreen({
setUpdateSessionsList(true)
} catch (e) {
setError(e)
console.error(e)
} finally {
setFactoryResetDialogOpen(false)
}
Expand All @@ -175,9 +174,6 @@ export default function StartScreen({
</IconButton>

<LogoWithVersion />
{error ? (
<Typography color="error" variant="h6">{`${error}`}</Typography>
) : null}

<div className={classes.root}>
<Grid container spacing={3}>
Expand Down
2 changes: 2 additions & 0 deletions products/jbrowse-desktop/src/corePlugins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import Wiggle from '@jbrowse/plugin-wiggle'
import SpreadsheetViewPlugin from '@jbrowse/plugin-spreadsheet-view'
import SvInspectorPlugin from '@jbrowse/plugin-sv-inspector'
import HicPlugin from '@jbrowse/plugin-hic'
import TrixPlugin from '@jbrowse/plugin-trix'
import GridBookmarkPlugin from '@jbrowse/plugin-grid-bookmark'

const corePlugins = [
Expand All @@ -45,6 +46,7 @@ const corePlugins = [
SvInspectorPlugin,
BreakpointSplitView,
HicPlugin,
TrixPlugin,
GridBookmarkPlugin,
]

Expand Down
9 changes: 7 additions & 2 deletions products/jbrowse-desktop/src/index.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import { FatalErrorDialog } from '@jbrowse/core/ui'
import React from 'react'
import ReactDOM from 'react-dom'
import { FatalErrorDialog } from '@jbrowse/core/ui'
import { ErrorBoundary } from 'react-error-boundary'
import { QueryParamProvider } from 'use-query-params'

import 'fontsource-roboto'

import factoryReset from './factoryReset'
import Loader from './Loader'

Expand All @@ -14,7 +17,9 @@ const PlatformSpecificFatalErrorDialog = props => {

ReactDOM.render(
<ErrorBoundary FallbackComponent={PlatformSpecificFatalErrorDialog}>
<Loader initialTimestamp={initialTimestamp} />
<QueryParamProvider>
<Loader initialTimestamp={initialTimestamp} />
</QueryParamProvider>
</ErrorBoundary>,
document.getElementById('root'),
)
3 changes: 3 additions & 0 deletions products/jbrowse-desktop/src/jbrowseModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ export default function JBrowseDesktop(
// track configuration is an array of track config schemas. multiple
// instances of a track can exist that use the same configuration
tracks: types.array(pluginManager.pluggableConfigSchemaType('track')),
aggregateTextSearchAdapters: types.array(
pluginManager.pluggableConfigSchemaType('text search adapter'),
),
connections: types.array(
pluginManager.pluggableConfigSchemaType('connection'),
),
Expand Down
Loading

0 comments on commit e07b309

Please sign in to comment.