@jbrowse/plugin-data-management 1.5.0 → 1.5.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/AddTrackWidget/model.d.ts +1 -434
- package/dist/AssemblyManager/AssemblyTable.d.ts +8 -2
- package/dist/PluginStoreWidget/components/CustomPluginForm.d.ts +1 -1
- package/dist/PluginStoreWidget/components/PluginCard.d.ts +2 -2
- package/dist/SetDefaultSession/SetDefaultSession.d.ts +4 -6
- package/dist/index.d.ts +8 -12
- package/dist/plugin-data-management.cjs.development.js +387 -324
- package/dist/plugin-data-management.cjs.development.js.map +1 -1
- package/dist/plugin-data-management.cjs.production.min.js +1 -1
- package/dist/plugin-data-management.cjs.production.min.js.map +1 -1
- package/dist/plugin-data-management.esm.js +389 -326
- package/dist/plugin-data-management.esm.js.map +1 -1
- package/package.json +3 -2
- package/src/AddTrackWidget/components/AddTrackWidget.test.js +1 -1
- package/src/AddTrackWidget/components/AddTrackWidget.tsx +3 -1
- package/src/AddTrackWidget/components/ConfirmTrack.tsx +101 -32
- package/src/AddTrackWidget/components/TrackSourceSelect.tsx +2 -3
- package/src/AddTrackWidget/index.test.jsx +34 -15
- package/src/AddTrackWidget/model.ts +5 -14
- package/src/AssemblyManager/AssemblyManager.tsx +3 -1
- package/src/AssemblyManager/AssemblyTable.tsx +40 -39
- package/src/HierarchicalTrackSelectorWidget/model.js +3 -2
- package/src/PluginStoreWidget/components/CustomPluginForm.tsx +164 -56
- package/src/PluginStoreWidget/components/InstalledPlugin.tsx +16 -11
- package/src/PluginStoreWidget/components/PluginCard.tsx +7 -9
- package/src/PluginStoreWidget/components/PluginStoreWidget.test.js +8 -7
- package/src/PluginStoreWidget/components/PluginStoreWidget.tsx +34 -25
- package/src/PluginStoreWidget/components/__snapshots__/PluginStoreWidget.test.js.snap +69 -52
- package/src/SetDefaultSession/SetDefaultSession.test.tsx +6 -81
- package/src/SetDefaultSession/SetDefaultSession.tsx +51 -162
|
@@ -1,29 +1,50 @@
|
|
|
1
1
|
import React, { useState } from 'react'
|
|
2
2
|
import { observer } from 'mobx-react'
|
|
3
3
|
import { getRoot } from 'mobx-state-tree'
|
|
4
|
+
import clsx from 'clsx'
|
|
4
5
|
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
import {
|
|
7
|
+
Button,
|
|
8
|
+
Collapse,
|
|
9
|
+
Dialog,
|
|
10
|
+
DialogActions,
|
|
11
|
+
DialogTitle,
|
|
12
|
+
DialogContent,
|
|
13
|
+
DialogContentText,
|
|
14
|
+
TextField,
|
|
15
|
+
makeStyles,
|
|
16
|
+
} from '@material-ui/core'
|
|
10
17
|
|
|
18
|
+
// icons
|
|
11
19
|
import IconButton from '@material-ui/core/IconButton'
|
|
12
20
|
import CloseIcon from '@material-ui/icons/Close'
|
|
21
|
+
import ExpandMoreIcon from '@material-ui/icons/ExpandMore'
|
|
13
22
|
|
|
23
|
+
import { PluginDefinition } from '@jbrowse/core/PluginLoader'
|
|
24
|
+
|
|
25
|
+
// locals
|
|
14
26
|
import { PluginStoreModel } from '../model'
|
|
15
27
|
|
|
16
|
-
const useStyles = makeStyles(
|
|
17
|
-
|
|
28
|
+
const useStyles = makeStyles(theme => ({
|
|
29
|
+
closeButton: {
|
|
18
30
|
position: 'absolute',
|
|
19
|
-
right:
|
|
20
|
-
top:
|
|
31
|
+
right: theme.spacing(1),
|
|
32
|
+
top: theme.spacing(1),
|
|
21
33
|
},
|
|
22
|
-
|
|
23
|
-
margin: 15,
|
|
34
|
+
dialogContent: {
|
|
24
35
|
display: 'flex',
|
|
25
36
|
flexDirection: 'column',
|
|
26
37
|
},
|
|
38
|
+
expand: {
|
|
39
|
+
transform: 'rotate(0deg)',
|
|
40
|
+
marginLeft: 'auto',
|
|
41
|
+
transition: theme.transitions.create('transform', {
|
|
42
|
+
duration: theme.transitions.duration.shortest,
|
|
43
|
+
}),
|
|
44
|
+
},
|
|
45
|
+
expandOpen: {
|
|
46
|
+
transform: 'rotate(180deg)',
|
|
47
|
+
},
|
|
27
48
|
}))
|
|
28
49
|
|
|
29
50
|
function CustomPluginForm({
|
|
@@ -32,69 +53,156 @@ function CustomPluginForm({
|
|
|
32
53
|
model,
|
|
33
54
|
}: {
|
|
34
55
|
open: boolean
|
|
35
|
-
onClose:
|
|
56
|
+
onClose(): void
|
|
36
57
|
model: PluginStoreModel
|
|
37
58
|
}) {
|
|
38
59
|
const classes = useStyles()
|
|
39
|
-
const [
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
60
|
+
const [umdPluginName, setUMDPluginName] = useState('')
|
|
61
|
+
const [umdPluginUrl, setUMDPluginUrl] = useState('')
|
|
62
|
+
const [esmPluginUrl, setESMPluginUrl] = useState('')
|
|
63
|
+
const [cjsPluginUrl, setCJSPluginUrl] = useState('')
|
|
64
|
+
const [advancedOptionsOpen, setAdvancedOptionsOpen] = useState(false)
|
|
43
65
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
}
|
|
66
|
+
function handleChange(event: React.ChangeEvent<HTMLTextAreaElement>) {
|
|
67
|
+
const { name, value } = event.target
|
|
68
|
+
if (name === 'umdName') {
|
|
69
|
+
setUMDPluginName(value)
|
|
70
|
+
}
|
|
71
|
+
if (name === 'umdUrl') {
|
|
72
|
+
setUMDPluginUrl(value)
|
|
73
|
+
}
|
|
74
|
+
if (name === 'esmUrl') {
|
|
75
|
+
setESMPluginUrl(value)
|
|
76
|
+
}
|
|
77
|
+
if (name === 'cjsUrl') {
|
|
78
|
+
setCJSPluginUrl(value)
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function handleOpenAdvancedOptions() {
|
|
83
|
+
setAdvancedOptionsOpen(!advancedOptionsOpen)
|
|
49
84
|
}
|
|
50
85
|
|
|
51
86
|
const rootModel = getRoot(model)
|
|
52
87
|
const { jbrowse } = rootModel
|
|
53
88
|
|
|
54
|
-
const
|
|
55
|
-
|
|
89
|
+
const ready = Boolean(
|
|
90
|
+
(umdPluginName && umdPluginUrl) || esmPluginUrl || cjsPluginUrl,
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
function handleSubmit() {
|
|
94
|
+
if (!ready) {
|
|
95
|
+
return
|
|
96
|
+
}
|
|
97
|
+
const pluginDefinition: PluginDefinition = {}
|
|
98
|
+
if (umdPluginName && umdPluginUrl) {
|
|
99
|
+
pluginDefinition.name = umdPluginName
|
|
100
|
+
pluginDefinition.umdUrl = umdPluginUrl
|
|
101
|
+
}
|
|
102
|
+
if (esmPluginUrl) {
|
|
103
|
+
pluginDefinition.esmUrl = esmPluginUrl
|
|
104
|
+
}
|
|
105
|
+
if (cjsPluginUrl) {
|
|
106
|
+
pluginDefinition.cjsUrl = cjsPluginUrl
|
|
107
|
+
}
|
|
108
|
+
jbrowse.addPlugin(pluginDefinition)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function handleClose() {
|
|
112
|
+
setUMDPluginName('')
|
|
113
|
+
setUMDPluginUrl('')
|
|
114
|
+
setESMPluginUrl('')
|
|
115
|
+
setCJSPluginUrl('')
|
|
116
|
+
onClose()
|
|
56
117
|
}
|
|
57
118
|
|
|
58
119
|
return (
|
|
59
|
-
<Dialog open={open} onClose={
|
|
120
|
+
<Dialog open={open} onClose={handleClose}>
|
|
60
121
|
<DialogTitle>
|
|
122
|
+
Add custom plugin
|
|
61
123
|
<IconButton
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
onClick={() => onClose(
|
|
124
|
+
size="medium"
|
|
125
|
+
className={classes.closeButton}
|
|
126
|
+
onClick={() => onClose()}
|
|
65
127
|
>
|
|
66
128
|
<CloseIcon />
|
|
67
129
|
</IconButton>
|
|
68
130
|
</DialogTitle>
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
131
|
+
<form onSubmit={handleSubmit}>
|
|
132
|
+
<DialogContent className={classes.dialogContent}>
|
|
133
|
+
<DialogContentText>
|
|
134
|
+
Enter the name of the plugin and its URL. The name should match what
|
|
135
|
+
is defined in the plugin's build.
|
|
136
|
+
</DialogContentText>
|
|
137
|
+
<TextField
|
|
138
|
+
id="umd-name-input"
|
|
139
|
+
name="umdName"
|
|
140
|
+
label="Plugin name"
|
|
141
|
+
variant="outlined"
|
|
142
|
+
value={umdPluginName}
|
|
143
|
+
onChange={handleChange}
|
|
144
|
+
/>
|
|
145
|
+
<TextField
|
|
146
|
+
id="umd-url-input"
|
|
147
|
+
name="umdUrl"
|
|
148
|
+
label="Plugin URL"
|
|
149
|
+
variant="outlined"
|
|
150
|
+
value={umdPluginUrl}
|
|
151
|
+
onChange={handleChange}
|
|
152
|
+
/>
|
|
153
|
+
<DialogContentText onClick={handleOpenAdvancedOptions}>
|
|
154
|
+
<IconButton
|
|
155
|
+
className={clsx(classes.expand, {
|
|
156
|
+
[classes.expandOpen]: advancedOptionsOpen,
|
|
157
|
+
})}
|
|
158
|
+
aria-expanded={advancedOptionsOpen}
|
|
159
|
+
aria-label="show more"
|
|
160
|
+
>
|
|
161
|
+
<ExpandMoreIcon />
|
|
162
|
+
</IconButton>
|
|
163
|
+
Advanced options
|
|
164
|
+
</DialogContentText>
|
|
165
|
+
<Collapse in={advancedOptionsOpen}>
|
|
166
|
+
<div className={classes.dialogContent}>
|
|
167
|
+
<DialogContentText>
|
|
168
|
+
The above fields assume that the plugin is built in UMD format.
|
|
169
|
+
If your plugin is in another format, or you have additional
|
|
170
|
+
builds you want to add (such as a CJS build for using NodeJS
|
|
171
|
+
APIs in desktop), you can enter the URLs for those builds below.
|
|
172
|
+
</DialogContentText>
|
|
173
|
+
<TextField
|
|
174
|
+
id="esm-url-input"
|
|
175
|
+
name="esmUrl"
|
|
176
|
+
label="ESM build URL"
|
|
177
|
+
variant="outlined"
|
|
178
|
+
value={esmPluginUrl}
|
|
179
|
+
onChange={handleChange}
|
|
180
|
+
/>
|
|
181
|
+
<TextField
|
|
182
|
+
id="cjs-url-input"
|
|
183
|
+
name="cjsUrl"
|
|
184
|
+
label="CJS build URL"
|
|
185
|
+
variant="outlined"
|
|
186
|
+
value={cjsPluginUrl}
|
|
187
|
+
onChange={handleChange}
|
|
188
|
+
/>
|
|
189
|
+
</div>
|
|
190
|
+
</Collapse>
|
|
191
|
+
</DialogContent>
|
|
192
|
+
<DialogActions>
|
|
193
|
+
<Button variant="contained" onClick={handleClose}>
|
|
194
|
+
Cancel
|
|
195
|
+
</Button>
|
|
196
|
+
<Button
|
|
197
|
+
variant="contained"
|
|
198
|
+
color="primary"
|
|
199
|
+
onClick={handleSubmit}
|
|
200
|
+
disabled={!ready}
|
|
201
|
+
>
|
|
202
|
+
Submit
|
|
203
|
+
</Button>
|
|
204
|
+
</DialogActions>
|
|
205
|
+
</form>
|
|
98
206
|
</Dialog>
|
|
99
207
|
)
|
|
100
208
|
}
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import React, { useState } from 'react'
|
|
2
2
|
import { observer } from 'mobx-react'
|
|
3
3
|
import { getParent } from 'mobx-state-tree'
|
|
4
|
-
|
|
5
|
-
import { makeStyles } from '@material-ui/core/styles'
|
|
6
4
|
import {
|
|
7
5
|
Button,
|
|
8
6
|
Dialog,
|
|
@@ -13,6 +11,7 @@ import {
|
|
|
13
11
|
ListItem,
|
|
14
12
|
Tooltip,
|
|
15
13
|
Typography,
|
|
14
|
+
makeStyles,
|
|
16
15
|
} from '@material-ui/core'
|
|
17
16
|
|
|
18
17
|
import CloseIcon from '@material-ui/icons/Close'
|
|
@@ -33,12 +32,16 @@ const useStyles = makeStyles(() => ({
|
|
|
33
32
|
dialogContainer: {
|
|
34
33
|
margin: 15,
|
|
35
34
|
},
|
|
35
|
+
lockedPluginTooltip: {
|
|
36
|
+
marginRight: '0.5rem',
|
|
37
|
+
},
|
|
36
38
|
}))
|
|
37
39
|
|
|
38
40
|
function LockedPlugin() {
|
|
41
|
+
const classes = useStyles()
|
|
39
42
|
return (
|
|
40
43
|
<Tooltip
|
|
41
|
-
|
|
44
|
+
className={classes.lockedPluginTooltip}
|
|
42
45
|
title="This plugin was installed by an administrator, you cannot remove it."
|
|
43
46
|
>
|
|
44
47
|
<LockIcon />
|
|
@@ -67,7 +70,9 @@ function PluginDialog({
|
|
|
67
70
|
</DialogTitle>
|
|
68
71
|
<DialogContent>
|
|
69
72
|
<Typography>
|
|
70
|
-
Please confirm that you want to remove {plugin}:
|
|
73
|
+
Please confirm that you want to remove {plugin}. Note: if any
|
|
74
|
+
resources in this session still use this plugin, it may cause your
|
|
75
|
+
session to crash
|
|
71
76
|
</Typography>
|
|
72
77
|
<DialogActions>
|
|
73
78
|
<Button
|
|
@@ -109,11 +114,11 @@ function InstalledPlugin({
|
|
|
109
114
|
const [dialogPlugin, setDialogPlugin] = useState<string>()
|
|
110
115
|
|
|
111
116
|
const session = getSession(model)
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
117
|
+
const { sessionPlugins } = session as unknown as {
|
|
118
|
+
sessionPlugins: BasePlugin[]
|
|
119
|
+
}
|
|
115
120
|
const isSessionPlugin = sessionPlugins?.some(
|
|
116
|
-
|
|
121
|
+
p => pluginManager.pluginMetadata[plugin.name].url === p.url,
|
|
117
122
|
)
|
|
118
123
|
|
|
119
124
|
const rootModel = getParent(model, 3)
|
|
@@ -127,11 +132,11 @@ function InstalledPlugin({
|
|
|
127
132
|
onClose={name => {
|
|
128
133
|
if (name) {
|
|
129
134
|
const pluginMetadata = pluginManager.pluginMetadata[plugin.name]
|
|
130
|
-
|
|
135
|
+
|
|
131
136
|
if (adminMode) {
|
|
132
|
-
jbrowse.removePlugin(
|
|
137
|
+
jbrowse.removePlugin(pluginMetadata)
|
|
133
138
|
} else if (isSessionWithSessionPlugins(session)) {
|
|
134
|
-
session.removeSessionPlugin(
|
|
139
|
+
session.removeSessionPlugin(pluginMetadata)
|
|
135
140
|
}
|
|
136
141
|
}
|
|
137
142
|
setDialogPlugin(undefined)
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import React, { useState } from 'react'
|
|
2
|
+
import PluginManager from '@jbrowse/core/PluginManager'
|
|
2
3
|
import { observer } from 'mobx-react'
|
|
3
4
|
import { getEnv, getParent } from 'mobx-state-tree'
|
|
4
|
-
|
|
5
|
-
import {
|
|
5
|
+
import { getSession } from '@jbrowse/core/util'
|
|
6
|
+
import { JBrowsePlugin } from '@jbrowse/core/util/types'
|
|
7
|
+
import { isSessionWithSessionPlugins } from '@jbrowse/core/util/types'
|
|
6
8
|
import {
|
|
7
9
|
Card,
|
|
8
10
|
CardActions,
|
|
@@ -10,18 +12,14 @@ import {
|
|
|
10
12
|
Button,
|
|
11
13
|
Link,
|
|
12
14
|
Typography,
|
|
15
|
+
makeStyles,
|
|
13
16
|
} from '@material-ui/core'
|
|
14
17
|
|
|
18
|
+
// icons
|
|
15
19
|
import PersonIcon from '@material-ui/icons/Person'
|
|
16
20
|
import AddIcon from '@material-ui/icons/Add'
|
|
17
21
|
import CheckIcon from '@material-ui/icons/Check'
|
|
18
|
-
|
|
19
|
-
import PluginManager from '@jbrowse/core/PluginManager'
|
|
20
|
-
import { getSession } from '@jbrowse/core/util'
|
|
21
|
-
import type { JBrowsePlugin } from '@jbrowse/core/util/types'
|
|
22
|
-
import { isSessionWithSessionPlugins } from '@jbrowse/core/util/types'
|
|
23
|
-
|
|
24
|
-
import type { PluginStoreModel } from '../model'
|
|
22
|
+
import { PluginStoreModel } from '../model'
|
|
25
23
|
|
|
26
24
|
const useStyles = makeStyles(() => ({
|
|
27
25
|
card: {
|
|
@@ -65,18 +65,18 @@ describe('<PluginStoreWidget />', () => {
|
|
|
65
65
|
)
|
|
66
66
|
await findByText('multiple sequence alignment browser plugin for JBrowse 2')
|
|
67
67
|
fireEvent.click(getByText('Add custom plugin'))
|
|
68
|
-
fireEvent.change(getByLabelText('Plugin
|
|
68
|
+
fireEvent.change(getByLabelText('Plugin URL'), {
|
|
69
69
|
target: {
|
|
70
|
-
value:
|
|
70
|
+
value:
|
|
71
|
+
'https://unpkg.com/jbrowse-plugin-msaview/dist/jbrowse-plugin-msaview.esm.js',
|
|
71
72
|
},
|
|
72
73
|
})
|
|
73
|
-
fireEvent.change(getByLabelText('Plugin
|
|
74
|
+
fireEvent.change(getByLabelText('Plugin name'), {
|
|
74
75
|
target: {
|
|
75
|
-
value:
|
|
76
|
-
'https://unpkg.com/jbrowse-plugin-msaview/dist/jbrowse-plugin-msaview.umd.production.min.js',
|
|
76
|
+
value: 'MsaView',
|
|
77
77
|
},
|
|
78
78
|
})
|
|
79
|
-
fireEvent.click(getByText('
|
|
79
|
+
fireEvent.click(getByText('Submit'))
|
|
80
80
|
|
|
81
81
|
await waitFor(() => {
|
|
82
82
|
expect(window.location.reload).toHaveBeenCalled()
|
|
@@ -85,7 +85,8 @@ describe('<PluginStoreWidget />', () => {
|
|
|
85
85
|
expect(getSnapshot(getParent(session)).jbrowse.plugins).toEqual([
|
|
86
86
|
{
|
|
87
87
|
name: 'MsaView',
|
|
88
|
-
|
|
88
|
+
umdUrl:
|
|
89
|
+
'https://unpkg.com/jbrowse-plugin-msaview/dist/jbrowse-plugin-msaview.esm.js',
|
|
89
90
|
},
|
|
90
91
|
])
|
|
91
92
|
})
|
|
@@ -13,29 +13,27 @@ import {
|
|
|
13
13
|
makeStyles,
|
|
14
14
|
} from '@material-ui/core'
|
|
15
15
|
|
|
16
|
+
import { JBrowsePlugin } from '@jbrowse/core/util/types'
|
|
17
|
+
import { getSession, isElectron } from '@jbrowse/core/util'
|
|
18
|
+
|
|
19
|
+
// icons
|
|
16
20
|
import ExpandMoreIcon from '@material-ui/icons/ExpandMore'
|
|
17
21
|
import ClearIcon from '@material-ui/icons/Clear'
|
|
18
22
|
import InfoOutlinedIcon from '@material-ui/icons/InfoOutlined'
|
|
19
23
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
import { getSession, isElectron } from '@jbrowse/core/util'
|
|
24
|
+
// locals
|
|
23
25
|
import InstalledPluginsList from './InstalledPluginsList'
|
|
24
26
|
import PluginCard from './PluginCard'
|
|
25
27
|
import CustomPluginForm from './CustomPluginForm'
|
|
26
|
-
|
|
27
28
|
import { PluginStoreModel } from '../model'
|
|
28
29
|
|
|
29
30
|
const useStyles = makeStyles(theme => ({
|
|
30
|
-
|
|
31
|
-
|
|
31
|
+
root: {
|
|
32
|
+
margin: theme.spacing(1),
|
|
32
33
|
},
|
|
33
34
|
expandIcon: {
|
|
34
35
|
color: '#fff',
|
|
35
36
|
},
|
|
36
|
-
searchBox: {
|
|
37
|
-
marginBottom: theme.spacing(2),
|
|
38
|
-
},
|
|
39
37
|
adminBadge: {
|
|
40
38
|
margin: '0.5em',
|
|
41
39
|
borderRadius: 3,
|
|
@@ -54,25 +52,30 @@ const useStyles = makeStyles(theme => ({
|
|
|
54
52
|
|
|
55
53
|
function PluginStoreWidget({ model }: { model: PluginStoreModel }) {
|
|
56
54
|
const classes = useStyles()
|
|
57
|
-
const [pluginArray, setPluginArray] = useState<JBrowsePlugin[]>(
|
|
55
|
+
const [pluginArray, setPluginArray] = useState<JBrowsePlugin[]>()
|
|
58
56
|
const [error, setError] = useState<unknown>()
|
|
59
57
|
const [customPluginFormOpen, setCustomPluginFormOpen] = useState(false)
|
|
60
58
|
const { adminMode } = getSession(model)
|
|
61
59
|
const { pluginManager } = getEnv(model)
|
|
62
60
|
|
|
63
61
|
useEffect(() => {
|
|
64
|
-
|
|
62
|
+
const controller = new AbortController()
|
|
63
|
+
const { signal } = controller
|
|
65
64
|
|
|
66
65
|
;(async () => {
|
|
67
66
|
try {
|
|
68
|
-
const
|
|
67
|
+
const response = await fetch(
|
|
69
68
|
'https://jbrowse.org/plugin-store/plugins.json',
|
|
69
|
+
{ signal },
|
|
70
70
|
)
|
|
71
|
-
if (!
|
|
72
|
-
|
|
71
|
+
if (!response.ok) {
|
|
72
|
+
const err = await response.text()
|
|
73
|
+
throw new Error(
|
|
74
|
+
`Failed to fetch plugin data: ${response.status} ${response.statusText} ${err}`,
|
|
75
|
+
)
|
|
73
76
|
}
|
|
74
|
-
const array = await
|
|
75
|
-
if (!
|
|
77
|
+
const array = await response.json()
|
|
78
|
+
if (!signal.aborted) {
|
|
76
79
|
setPluginArray(array.plugins)
|
|
77
80
|
}
|
|
78
81
|
} catch (e) {
|
|
@@ -82,12 +85,12 @@ function PluginStoreWidget({ model }: { model: PluginStoreModel }) {
|
|
|
82
85
|
})()
|
|
83
86
|
|
|
84
87
|
return () => {
|
|
85
|
-
|
|
88
|
+
controller.abort()
|
|
86
89
|
}
|
|
87
90
|
}, [])
|
|
88
91
|
|
|
89
92
|
return (
|
|
90
|
-
<div>
|
|
93
|
+
<div className={classes.root}>
|
|
91
94
|
{adminMode && (
|
|
92
95
|
<>
|
|
93
96
|
{!isElectron && (
|
|
@@ -111,13 +114,12 @@ function PluginStoreWidget({ model }: { model: PluginStoreModel }) {
|
|
|
111
114
|
</div>
|
|
112
115
|
<CustomPluginForm
|
|
113
116
|
open={customPluginFormOpen}
|
|
114
|
-
onClose={setCustomPluginFormOpen}
|
|
117
|
+
onClose={() => setCustomPluginFormOpen(false)}
|
|
115
118
|
model={model}
|
|
116
119
|
/>
|
|
117
120
|
</>
|
|
118
121
|
)}
|
|
119
122
|
<TextField
|
|
120
|
-
className={classes.searchBox}
|
|
121
123
|
label="Filter plugins"
|
|
122
124
|
value={model.filterText}
|
|
123
125
|
onChange={event => model.setFilterText(event.target.value)}
|
|
@@ -153,13 +155,20 @@ function PluginStoreWidget({ model }: { model: PluginStoreModel }) {
|
|
|
153
155
|
</AccordionSummary>
|
|
154
156
|
{error ? (
|
|
155
157
|
<Typography color="error">{`${error}`}</Typography>
|
|
156
|
-
) : pluginArray
|
|
158
|
+
) : pluginArray ? (
|
|
157
159
|
pluginArray
|
|
158
|
-
.filter(plugin =>
|
|
159
|
-
|
|
160
|
+
.filter(plugin => {
|
|
161
|
+
// If pugin only has cjsUrl, don't display outside desktop
|
|
162
|
+
if (
|
|
163
|
+
!isElectron &&
|
|
164
|
+
!(plugin.esmUrl || plugin.url || plugin.umdUrl)
|
|
165
|
+
) {
|
|
166
|
+
return false
|
|
167
|
+
}
|
|
168
|
+
return plugin.name
|
|
160
169
|
.toLowerCase()
|
|
161
|
-
.includes(model.filterText.toLowerCase())
|
|
162
|
-
)
|
|
170
|
+
.includes(model.filterText.toLowerCase())
|
|
171
|
+
})
|
|
163
172
|
.map(plugin => (
|
|
164
173
|
<PluginCard
|
|
165
174
|
key={(plugin as JBrowsePlugin).name}
|