@apollo-annotation/jbrowse-plugin-apollo 0.3.7 → 0.3.9

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.
Files changed (86) hide show
  1. package/dist/index.esm.js +11212 -10483
  2. package/dist/index.esm.js.map +1 -1
  3. package/dist/jbrowse-plugin-apollo.cjs.development.js +11251 -10509
  4. package/dist/jbrowse-plugin-apollo.cjs.development.js.map +1 -1
  5. package/dist/jbrowse-plugin-apollo.cjs.production.min.js +1 -1
  6. package/dist/jbrowse-plugin-apollo.cjs.production.min.js.map +1 -1
  7. package/dist/jbrowse-plugin-apollo.umd.development.js +7726 -9014
  8. package/dist/jbrowse-plugin-apollo.umd.development.js.map +1 -1
  9. package/dist/jbrowse-plugin-apollo.umd.production.min.js +1 -1
  10. package/dist/jbrowse-plugin-apollo.umd.production.min.js.map +1 -1
  11. package/package.json +18 -18
  12. package/src/ApolloInternetAccount/model.ts +123 -70
  13. package/src/ApolloRefNameAliasAdapter/ApolloRefNameAliasAdapter.ts +4 -4
  14. package/src/ApolloSequenceAdapter/ApolloSequenceAdapter.ts +9 -7
  15. package/src/BackendDrivers/CollaborationServerDriver.ts +72 -20
  16. package/src/BackendDrivers/DesktopFileDriver.ts +2 -2
  17. package/src/ChangeManager.ts +36 -14
  18. package/src/FeatureDetailsWidget/ApolloTranscriptDetailsWidget.tsx +64 -5
  19. package/src/FeatureDetailsWidget/BasicInformation.tsx +6 -4
  20. package/src/FeatureDetailsWidget/NumberTextField.tsx +5 -2
  21. package/src/FeatureDetailsWidget/TranscriptSequence.tsx +70 -73
  22. package/src/FeatureDetailsWidget/TranscriptWidgetEditLocation.tsx +72 -234
  23. package/src/LinearApolloDisplay/components/CheckResultWarnings.tsx +92 -0
  24. package/src/LinearApolloDisplay/components/LinearApolloDisplay.tsx +23 -131
  25. package/src/LinearApolloDisplay/glyphs/BoxGlyph.ts +50 -194
  26. package/src/LinearApolloDisplay/glyphs/GeneGlyph.ts +279 -217
  27. package/src/LinearApolloDisplay/glyphs/GenericChildGlyph.ts +53 -34
  28. package/src/LinearApolloDisplay/glyphs/Glyph.ts +7 -9
  29. package/src/LinearApolloDisplay/glyphs/util.ts +19 -0
  30. package/src/LinearApolloDisplay/stateModel/base.ts +34 -43
  31. package/src/LinearApolloDisplay/stateModel/layouts.ts +3 -2
  32. package/src/LinearApolloDisplay/stateModel/mouseEvents.ts +32 -261
  33. package/src/LinearApolloDisplay/stateModel/rendering.ts +43 -343
  34. package/src/LinearApolloReferenceSequenceDisplay/components/LinearApolloReferenceSequenceDisplay.tsx +87 -0
  35. package/src/LinearApolloReferenceSequenceDisplay/components/index.ts +1 -0
  36. package/src/LinearApolloReferenceSequenceDisplay/configSchema.ts +7 -0
  37. package/src/LinearApolloReferenceSequenceDisplay/drawSequenceOverlay.ts +181 -0
  38. package/src/LinearApolloReferenceSequenceDisplay/drawSequenceTrack.ts +218 -0
  39. package/src/LinearApolloReferenceSequenceDisplay/index.ts +3 -0
  40. package/src/LinearApolloReferenceSequenceDisplay/stateModel/base.ts +227 -0
  41. package/src/LinearApolloReferenceSequenceDisplay/stateModel/index.ts +25 -0
  42. package/src/LinearApolloReferenceSequenceDisplay/stateModel/rendering.ts +157 -0
  43. package/src/LinearApolloSixFrameDisplay/components/LinearApolloSixFrameDisplay.tsx +101 -38
  44. package/src/LinearApolloSixFrameDisplay/glyphs/GeneGlyph.ts +334 -262
  45. package/src/LinearApolloSixFrameDisplay/glyphs/Glyph.ts +12 -8
  46. package/src/LinearApolloSixFrameDisplay/stateModel/base.ts +42 -4
  47. package/src/LinearApolloSixFrameDisplay/stateModel/layouts.ts +4 -8
  48. package/src/LinearApolloSixFrameDisplay/stateModel/mouseEvents.ts +73 -97
  49. package/src/LinearApolloSixFrameDisplay/stateModel/rendering.ts +49 -61
  50. package/src/TabularEditor/HybridGrid/Feature.tsx +16 -14
  51. package/src/TabularEditor/HybridGrid/HybridGrid.tsx +7 -5
  52. package/src/components/AddAssembly.tsx +34 -38
  53. package/src/components/AddAssemblyAliases.tsx +1 -1
  54. package/src/components/AddChildFeature.tsx +5 -2
  55. package/src/components/AddFeature.tsx +30 -21
  56. package/src/components/AddRefSeqAliases.tsx +64 -50
  57. package/src/components/CopyFeature.tsx +4 -2
  58. package/src/components/CreateApolloAnnotation.tsx +22 -9
  59. package/src/components/DeleteAssembly.tsx +3 -10
  60. package/src/components/DownloadGFF3.tsx +2 -2
  61. package/src/components/EditZoomThresholdDialog.tsx +69 -0
  62. package/src/components/FilterFeatures.tsx +7 -7
  63. package/src/components/FilterTranscripts.tsx +6 -6
  64. package/src/components/ImportFeatures.tsx +1 -1
  65. package/src/components/ManageChecks.tsx +3 -10
  66. package/src/components/ManageUsers.tsx +23 -22
  67. package/src/components/MergeTranscripts.tsx +12 -15
  68. package/src/components/OntologyTermAutocomplete.tsx +1 -8
  69. package/src/components/OntologyTermMultiSelect.tsx +11 -11
  70. package/src/components/OpenLocalFile.tsx +11 -7
  71. package/src/components/ViewChangeLog.tsx +25 -50
  72. package/src/components/ViewCheckResults.tsx +2 -8
  73. package/src/components/index.ts +1 -0
  74. package/src/config.ts +6 -0
  75. package/src/index.ts +53 -115
  76. package/src/makeDisplayComponent.tsx +9 -14
  77. package/src/menus/index.ts +1 -0
  78. package/src/{ApolloInternetAccount/addMenuItems.ts → menus/topLevelMenu.ts} +56 -47
  79. package/src/menus/topLevelMenuAdmin.ts +154 -0
  80. package/src/session/ClientDataStore.ts +32 -14
  81. package/src/session/session.ts +159 -121
  82. package/src/util/annotationFeatureUtils.ts +15 -21
  83. package/src/util/displayUtils.ts +149 -0
  84. package/src/util/glyphUtils.ts +329 -0
  85. package/src/util/loadAssemblyIntoClient.ts +3 -2
  86. package/src/util/mouseEventsUtils.ts +32 -0
@@ -0,0 +1,154 @@
1
+ import {
2
+ type AbstractMenuManager,
3
+ type AbstractSessionModel,
4
+ } from '@jbrowse/core/util'
5
+ import AddIcon from '@mui/icons-material/Add'
6
+ import AdminPanelSettingsIcon from '@mui/icons-material/AdminPanelSettings'
7
+ import DeleteIcon from '@mui/icons-material/Delete'
8
+ import InputIcon from '@mui/icons-material/Input'
9
+ import PersonIcon from '@mui/icons-material/Person'
10
+ import RuleIcon from '@mui/icons-material/Rule'
11
+
12
+ import {
13
+ AddAssembly,
14
+ AddAssemblyAliases,
15
+ AddRefSeqAliases,
16
+ DeleteAssembly,
17
+ ImportFeatures,
18
+ ManageChecks,
19
+ ManageUsers,
20
+ } from '../components'
21
+ import { type ApolloSessionModel } from '../session'
22
+
23
+ export function addTopLevelAdminMenus(rootModel: AbstractMenuManager) {
24
+ rootModel.appendToMenu('Apollo', {
25
+ label: 'Admin',
26
+ type: 'subMenu',
27
+ icon: AdminPanelSettingsIcon,
28
+ subMenu: [
29
+ {
30
+ label: 'Add Assembly',
31
+ icon: AddIcon,
32
+ onClick: (session: ApolloSessionModel) => {
33
+ ;(session as unknown as AbstractSessionModel).queueDialog(
34
+ (doneCallback) => [
35
+ AddAssembly,
36
+ {
37
+ session,
38
+ handleClose: () => {
39
+ doneCallback()
40
+ },
41
+ changeManager: session.apolloDataStore.changeManager,
42
+ },
43
+ ],
44
+ )
45
+ },
46
+ },
47
+ {
48
+ label: 'Delete Assembly',
49
+ icon: DeleteIcon,
50
+ onClick: (session: ApolloSessionModel) => {
51
+ ;(session as unknown as AbstractSessionModel).queueDialog(
52
+ (doneCallback) => [
53
+ DeleteAssembly,
54
+ {
55
+ session,
56
+ handleClose: () => {
57
+ doneCallback()
58
+ },
59
+ changeManager: session.apolloDataStore.changeManager,
60
+ },
61
+ ],
62
+ )
63
+ },
64
+ },
65
+ {
66
+ label: 'Import Features',
67
+ icon: InputIcon,
68
+ onClick: (session: ApolloSessionModel) => {
69
+ ;(session as unknown as AbstractSessionModel).queueDialog(
70
+ (doneCallback) => [
71
+ ImportFeatures,
72
+ {
73
+ session,
74
+ handleClose: () => {
75
+ doneCallback()
76
+ },
77
+ changeManager: session.apolloDataStore.changeManager,
78
+ },
79
+ ],
80
+ )
81
+ },
82
+ },
83
+ {
84
+ label: 'Add reference sequence aliases',
85
+ onClick: (session: ApolloSessionModel) => {
86
+ ;(session as unknown as AbstractSessionModel).queueDialog(
87
+ (doneCallback) => [
88
+ AddRefSeqAliases,
89
+ {
90
+ session,
91
+ handleClose: () => {
92
+ doneCallback()
93
+ },
94
+ changeManager: session.apolloDataStore.changeManager,
95
+ },
96
+ ],
97
+ )
98
+ },
99
+ },
100
+ {
101
+ label: 'Add Assembly aliases',
102
+ onClick: (session: ApolloSessionModel) => {
103
+ ;(session as unknown as AbstractSessionModel).queueDialog(
104
+ (doneCallback) => [
105
+ AddAssemblyAliases,
106
+ {
107
+ session,
108
+ handleClose: () => {
109
+ doneCallback()
110
+ },
111
+ changeManager: session.apolloDataStore.changeManager,
112
+ },
113
+ ],
114
+ )
115
+ },
116
+ },
117
+ {
118
+ label: 'Manage Users',
119
+ icon: PersonIcon,
120
+ onClick: (session: ApolloSessionModel) => {
121
+ ;(session as unknown as AbstractSessionModel).queueDialog(
122
+ (doneCallback) => [
123
+ ManageUsers,
124
+ {
125
+ session,
126
+ handleClose: () => {
127
+ doneCallback()
128
+ },
129
+ changeManager: session.apolloDataStore.changeManager,
130
+ },
131
+ ],
132
+ )
133
+ },
134
+ },
135
+ {
136
+ label: 'Manage Checks',
137
+ icon: RuleIcon,
138
+ onClick: (session: ApolloSessionModel) => {
139
+ ;(session as unknown as AbstractSessionModel).queueDialog(
140
+ (doneCallback) => [
141
+ ManageChecks,
142
+ {
143
+ session,
144
+ handleClose: () => {
145
+ doneCallback()
146
+ },
147
+ },
148
+ ],
149
+ )
150
+ },
151
+ },
152
+ ],
153
+ })
154
+ }
@@ -33,6 +33,7 @@ import {
33
33
 
34
34
  import {
35
35
  type ApolloInternetAccount,
36
+ type BackendDriver,
36
37
  CollaborationServerDriver,
37
38
  DesktopFileDriver,
38
39
  InMemoryFileDriver,
@@ -86,18 +87,38 @@ export function clientDataStoreFactory(
86
87
  }
87
88
  return self.assemblies.put(assemblySnapshot)
88
89
  },
90
+ }))
91
+ .actions((self) => ({
89
92
  addFeature(assemblyId: string, feature: AnnotationFeatureSnapshot) {
90
- const assembly = self.assemblies.get(assemblyId)
91
- if (!assembly) {
92
- throw new Error(
93
- `Could not find assembly "${assemblyId}" to add feature "${feature._id}"`,
94
- )
93
+ const session = getSession(self)
94
+ const { assemblyManager } = session
95
+ let apolloAssembly = self.assemblies.get(assemblyId)
96
+ if (!apolloAssembly) {
97
+ // maybe it's a valid assembly that we haven't loaded yet
98
+ const assembly = assemblyManager.get(assemblyId)
99
+ if (!assembly) {
100
+ throw new Error(
101
+ `Could not find assembly "${assemblyId}" to add feature "${feature._id}"`,
102
+ )
103
+ }
104
+ apolloAssembly = self.addAssembly(assemblyId)
95
105
  }
96
- const ref = assembly.refSeqs.get(feature.refSeq)
106
+ let ref = apolloAssembly.refSeqs.get(feature.refSeq)
97
107
  if (!ref) {
98
- throw new Error(
99
- `Could not find refSeq "${feature.refSeq}" to add feature "${feature._id}"`,
100
- )
108
+ // maybe it's a valid refName that we haven't loaded yet
109
+ const assembly = assemblyManager.get(assemblyId)
110
+ if (!assembly) {
111
+ throw new Error(
112
+ `Could not find assembly "${assemblyId}" to add feature "${feature._id}"`,
113
+ )
114
+ }
115
+ const canonicalRefName = assembly.getCanonicalRefName(feature.refSeq)
116
+ if (!canonicalRefName) {
117
+ throw new Error(
118
+ `Could not find refSeq "${feature.refSeq}" to add feature "${feature._id}"`,
119
+ )
120
+ }
121
+ ref = apolloAssembly.addRefSeq(feature.refSeq, canonicalRefName)
101
122
  }
102
123
  ref.features.put(feature)
103
124
  },
@@ -212,15 +233,12 @@ export function clientDataStoreFactory(
212
233
  },
213
234
  }))
214
235
  .views((self) => ({
215
- getBackendDriver(assemblyId: string) {
216
- if (!assemblyId) {
217
- return self.collaborationServerDriver
218
- }
236
+ getBackendDriver(assemblyId: string): BackendDriver | undefined {
219
237
  const session = getSession(self)
220
238
  const { assemblyManager } = session
221
239
  const assembly = assemblyManager.get(assemblyId)
222
240
  if (!assembly) {
223
- return self.collaborationServerDriver
241
+ return
224
242
  }
225
243
  const { file, internetAccountConfigId } = getConf(assembly, [
226
244
  'sequence',
@@ -26,8 +26,8 @@ import { autorun, observable } from 'mobx'
26
26
  import {
27
27
  type Instance,
28
28
  type SnapshotOut,
29
+ addDisposer,
29
30
  applySnapshot,
30
- flow,
31
31
  getRoot,
32
32
  getSnapshot,
33
33
  types,
@@ -54,12 +54,15 @@ export interface Collaborator {
54
54
  locations: UserLocation[]
55
55
  }
56
56
 
57
+ export interface HoveredFeature {
58
+ feature: AnnotationFeature
59
+ bp: number
60
+ }
61
+
57
62
  export function extendSession(
58
63
  pluginManager: PluginManager,
59
64
  sessionModel: ReturnType<typeof types.model>,
60
65
  ) {
61
- const aborter = new AbortController()
62
- const { signal } = aborter
63
66
  const AnnotationFeatureExtended = pluginManager.evaluateExtensionPoint(
64
67
  'Apollo-extendAnnotationFeature',
65
68
  AnnotationFeatureModel,
@@ -70,7 +73,12 @@ export function extendSession(
70
73
  apolloDataStore: types.optional(ClientDataStore, { typeName: 'Client' }),
71
74
  apolloSelectedFeature: types.safeReference(AnnotationFeatureExtended),
72
75
  jobsManager: types.optional(ApolloJobModel, {}),
76
+ isLocked: types.optional(types.boolean, false),
73
77
  })
78
+ .volatile(() => ({
79
+ apolloHoveredFeature: undefined as HoveredFeature | undefined,
80
+ abortController: new AbortController(),
81
+ }))
74
82
  .extend(() => {
75
83
  const collabs = observable.array<Collaborator>([])
76
84
 
@@ -95,10 +103,13 @@ export function extendSession(
95
103
  }
96
104
  })
97
105
  .actions((self) => ({
98
- apolloSetSelectedFeature(feature?: AnnotationFeature) {
106
+ apolloSetSelectedFeature(feature?: AnnotationFeature | string) {
99
107
  // @ts-expect-error Not sure why TS thinks these MST types don't match
100
108
  self.apolloSelectedFeature = feature
101
109
  },
110
+ apolloSetHoveredFeature(feature?: HoveredFeature) {
111
+ self.apolloHoveredFeature = feature
112
+ },
102
113
  addApolloTrackConfig(assembly: AssemblyModel, baseURL?: string) {
103
114
  const trackId = `apollo_track_${assembly.name}`
104
115
  const hasTrack = (self as unknown as AbstractSessionModel).tracks.some(
@@ -127,6 +138,18 @@ export function extendSession(
127
138
  })
128
139
  }
129
140
  },
141
+ toggleLocked() {
142
+ self.isLocked = !self.isLocked
143
+ },
144
+ getPluginConfiguration() {
145
+ const { jbrowse } = getRoot<ApolloRootModel>(self)
146
+ const pluginConfiguration =
147
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
148
+ jbrowse.configuration.ApolloPlugin as Instance<
149
+ typeof ApolloPluginConfigurationSchema
150
+ >
151
+ return pluginConfiguration
152
+ },
130
153
  broadcastLocations() {
131
154
  const { internetAccounts } = getRoot<ApolloRootModel>(self)
132
155
  const locations: {
@@ -142,20 +165,16 @@ export function extendSession(
142
165
  const lgv = view as unknown as LinearGenomeViewModel
143
166
  if (lgv.initialized) {
144
167
  const { dynamicBlocks } = lgv
145
- // eslint-disable-next-line unicorn/no-array-for-each
146
- dynamicBlocks.forEach((block) => {
147
- if (block.regionNumber !== undefined) {
148
- const { assemblyName, end, refName, start } = block
149
- const assembly =
150
- self.apolloDataStore.assemblies.get(assemblyName)
151
- if (
152
- assembly &&
153
- assembly.backendDriverType === 'CollaborationServerDriver'
154
- ) {
155
- locations.push({ assemblyName, refName, start, end })
156
- }
168
+ for (const block of dynamicBlocks.contentBlocks) {
169
+ const { assemblyName, end, refName, start } = block
170
+ const assembly = self.apolloDataStore.assemblies.get(assemblyName)
171
+ if (
172
+ assembly &&
173
+ assembly.backendDriverType === 'CollaborationServerDriver'
174
+ ) {
175
+ locations.push({ assemblyName, refName, start, end })
157
176
  }
158
- })
177
+ }
159
178
  }
160
179
  }
161
180
  if (locations.length === 0) {
@@ -184,28 +203,35 @@ export function extendSession(
184
203
  }
185
204
  },
186
205
  }))
206
+ .volatile((self) => ({
207
+ previousSnapshot: getSnapshot(self),
208
+ }))
187
209
  .actions((self) => ({
188
- afterCreate: flow(function* afterCreate() {
189
- autorun(
190
- () => {
191
- // broadcastLocations() // **** This is not working and therefore we need to duplicate broadcastLocations() -method code here because autorun() does not observe changes otherwise
192
- const locations: {
193
- assemblyName: string
194
- refName: string
195
- start: number
196
- end: number
197
- }[] = []
198
- for (const view of (self as unknown as AbstractSessionModel)
199
- .views) {
200
- if (view.type !== 'LinearGenomeView') {
201
- return
202
- }
203
- const lgv = view as unknown as LinearGenomeViewModel
204
- if (lgv.initialized) {
205
- const { dynamicBlocks } = lgv
206
- // eslint-disable-next-line unicorn/no-array-for-each
207
- dynamicBlocks.forEach((block) => {
208
- if (block.regionNumber !== undefined) {
210
+ afterCreate() {
211
+ applySnapshot(self, { name: self.name, id: self.id })
212
+ // @ts-expect-error type is missing on ApolloRootModel
213
+ const { internetAccounts, jbrowse, reloadPluginManagerCallback } =
214
+ getRoot<ApolloRootModel>(self)
215
+ addDisposer(
216
+ self,
217
+ autorun(
218
+ () => {
219
+ // broadcastLocations() // **** This is not working and therefore we need to duplicate broadcastLocations() -method code here because autorun() does not observe changes otherwise
220
+ const locations: {
221
+ assemblyName: string
222
+ refName: string
223
+ start: number
224
+ end: number
225
+ }[] = []
226
+ for (const view of (self as unknown as AbstractSessionModel)
227
+ .views) {
228
+ if (view.type !== 'LinearGenomeView') {
229
+ return
230
+ }
231
+ const lgv = view as unknown as LinearGenomeViewModel
232
+ if (lgv.initialized) {
233
+ const { dynamicBlocks } = lgv
234
+ for (const block of dynamicBlocks.contentBlocks) {
209
235
  const { assemblyName, end, refName, start } = block
210
236
  const assembly =
211
237
  self.apolloDataStore.assemblies.get(assemblyName)
@@ -216,100 +242,112 @@ export function extendSession(
216
242
  locations.push({ assemblyName, refName, start, end })
217
243
  }
218
244
  }
219
- })
245
+ }
220
246
  }
221
- }
222
- if (locations.length === 0) {
223
- for (const internetAccount of internetAccounts) {
224
- if ('baseURL' in internetAccount) {
225
- internetAccount.postUserLocation([])
247
+ if (locations.length === 0) {
248
+ for (const internetAccount of internetAccounts) {
249
+ if ('baseURL' in internetAccount) {
250
+ internetAccount.postUserLocation([])
251
+ }
226
252
  }
253
+ return
227
254
  }
228
- return
229
- }
230
255
 
231
- const allLocations: UserLocation[] = []
232
- for (const internetAccount of internetAccounts) {
233
- if ('baseURL' in internetAccount) {
234
- for (const location of locations) {
235
- const tmpLoc: UserLocation = {
236
- assemblyId: location.assemblyName,
237
- refSeq: location.refName,
238
- start: location.start,
239
- end: location.end,
256
+ const allLocations: UserLocation[] = []
257
+ for (const internetAccount of internetAccounts) {
258
+ if ('baseURL' in internetAccount) {
259
+ for (const location of locations) {
260
+ const tmpLoc: UserLocation = {
261
+ assemblyId: location.assemblyName,
262
+ refSeq: location.refName,
263
+ start: location.start,
264
+ end: location.end,
265
+ }
266
+ allLocations.push(tmpLoc)
240
267
  }
241
- allLocations.push(tmpLoc)
268
+ internetAccount.postUserLocation(allLocations)
242
269
  }
243
- internetAccount.postUserLocation(allLocations)
244
270
  }
245
- }
246
- },
247
- { name: 'ApolloSession' },
271
+ },
272
+ { name: 'ApolloSessionBroadcastLocations' },
273
+ ),
248
274
  )
249
- // When the initial config.json loads, it doesn't include the Apollo
250
- // tracks, which would result in a potentially invalid session snapshot
251
- // if any tracks are open. Here we copy the session snapshot, apply an
252
- // empty session snapshot, and then restore the original session
253
- // snapshot after the updated config.json loads.
254
- // @ts-expect-error type is missing on ApolloRootModel
255
- const { internetAccounts, jbrowse, reloadPluginManagerCallback } =
256
- getRoot<ApolloRootModel>(self)
257
- const pluginConfiguration =
258
- // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
259
- jbrowse.configuration.ApolloPlugin as Instance<
260
- typeof ApolloPluginConfigurationSchema
261
- >
262
- const hasRole = readConfObject(
263
- pluginConfiguration,
264
- 'hasRole',
265
- ) as boolean
266
- if (hasRole) {
267
- return
268
- }
269
- const sessionSnapshot = getSnapshot(self)
270
- const { id, name } = sessionSnapshot
271
- applySnapshot(self, { name, id })
275
+ addDisposer(
276
+ self,
277
+ autorun(
278
+ async (reaction) => {
279
+ // When the initial config.json loads, it doesn't include the Apollo
280
+ // tracks, which would result in a potentially invalid session snapshot
281
+ // if any tracks are open. Here we copy the session snapshot, apply an
282
+ // empty session snapshot, and then restore the original session
283
+ // snapshot after the updated config.json loads.
284
+ const pluginConfiguration =
285
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
286
+ jbrowse.configuration.ApolloPlugin as Instance<
287
+ typeof ApolloPluginConfigurationSchema
288
+ >
289
+ const hasRole = readConfObject(
290
+ pluginConfiguration,
291
+ 'hasRole',
292
+ ) as boolean
293
+ if (hasRole) {
294
+ // @ts-expect-error not sure why snapshot type is wrong for snapshot
295
+ applySnapshot(self, self.previousSnapshot)
296
+ reaction.dispose()
297
+ return
298
+ }
272
299
 
273
- // fetch and initialize assemblies for each of our Apollo internet accounts
274
- for (const internetAccount of internetAccounts as ApolloInternetAccountModel[]) {
275
- if (internetAccount.type !== 'ApolloInternetAccount') {
276
- continue
277
- }
300
+ const { signal } = self.abortController
301
+ // fetch and initialize assemblies for each of our Apollo internet accounts
302
+ for (const internetAccount of internetAccounts as ApolloInternetAccountModel[]) {
303
+ if (internetAccount.type !== 'ApolloInternetAccount') {
304
+ continue
305
+ }
278
306
 
279
- const { baseURL } = internetAccount
280
- const uri = new URL('jbrowse/config.json', baseURL).href
281
- const fetch = internetAccount.getFetcher({
282
- locationType: 'UriLocation',
283
- uri,
284
- })
285
- let response: Response
286
- try {
287
- response = yield fetch(uri, { signal })
288
- } catch (error) {
289
- console.error(error)
290
- continue
291
- }
292
- if (!response.ok) {
293
- const errorMessage = yield createFetchErrorMessage(
294
- response,
295
- 'Failed to fetch assemblies',
296
- )
297
- console.error(errorMessage)
298
- continue
299
- }
300
- let jbrowseConfig
301
- try {
302
- jbrowseConfig = yield response.json()
303
- } catch (error) {
304
- console.error(error)
305
- continue
306
- }
307
- // eslint-disable-next-line @typescript-eslint/no-unsafe-call
308
- reloadPluginManagerCallback(jbrowseConfig, sessionSnapshot)
309
- }
310
- }),
307
+ const { baseURL } = internetAccount
308
+ const uri = new URL('jbrowse/config.json', baseURL).href
309
+ const fetch = internetAccount.getFetcher({
310
+ locationType: 'UriLocation',
311
+ uri,
312
+ })
313
+ let response: Response
314
+ try {
315
+ response = await fetch(uri, { signal })
316
+ } catch (error) {
317
+ if (!self.abortController.signal.aborted) {
318
+ console.error(error)
319
+ }
320
+ continue
321
+ }
322
+ if (!response.ok) {
323
+ const errorMessage = await createFetchErrorMessage(
324
+ response,
325
+ 'Failed to fetch assemblies',
326
+ )
327
+ console.error(errorMessage)
328
+ continue
329
+ }
330
+ let jbrowseConfig
331
+ try {
332
+ jbrowseConfig = await response.json()
333
+ } catch (error) {
334
+ console.error(error)
335
+ continue
336
+ }
337
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-call
338
+ reloadPluginManagerCallback(
339
+ jbrowseConfig,
340
+ self.previousSnapshot,
341
+ )
342
+ reaction.dispose()
343
+ }
344
+ },
345
+ { name: 'ApolloSessionLoadConfig' },
346
+ ),
347
+ )
348
+ },
311
349
  beforeDestroy() {
312
- aborter.abort('destroying session model')
350
+ self.abortController.abort('destroying session model')
313
351
  },
314
352
  }))
315
353
 
@@ -1,7 +1,5 @@
1
1
  import { type AnnotationFeature } from '@apollo-annotation/mst'
2
2
 
3
- import { type MousePosition } from '../LinearApolloDisplay/stateModel/mouseEvents'
4
-
5
3
  export function getFeatureName(feature: AnnotationFeature) {
6
4
  const { attributes } = feature
7
5
  const name = attributes.get('gff_name')
@@ -75,44 +73,40 @@ function getParents(feature: AnnotationFeature): AnnotationFeature[] {
75
73
  return parents
76
74
  }
77
75
 
78
- export function getFeaturesUnderClick(
79
- mousePosition: MousePosition,
76
+ export function getRelatedFeatures(
77
+ feature: AnnotationFeature,
78
+ bp: number,
80
79
  includeSiblings = false,
81
80
  ): AnnotationFeature[] {
82
- const clickedFeatures: AnnotationFeature[] = []
83
- if (!mousePosition.featureAndGlyphUnderMouse) {
84
- return clickedFeatures
85
- }
86
- clickedFeatures.push(mousePosition.featureAndGlyphUnderMouse.feature)
87
- for (const x of getParents(mousePosition.featureAndGlyphUnderMouse.feature)) {
88
- clickedFeatures.push(x)
81
+ const relatedFeatures: AnnotationFeature[] = []
82
+ relatedFeatures.push(feature)
83
+ for (const x of getParents(feature)) {
84
+ relatedFeatures.push(x)
89
85
  }
90
- const { bp } = mousePosition
91
- const children = getChildren(mousePosition.featureAndGlyphUnderMouse.feature)
86
+ const children = getChildren(feature)
92
87
  for (const child of children) {
93
88
  if (child.min < bp && child.max >= bp) {
94
- clickedFeatures.push(child)
89
+ relatedFeatures.push(child)
95
90
  }
96
91
  }
97
92
  if (!includeSiblings) {
98
- return clickedFeatures
93
+ return relatedFeatures
99
94
  }
100
95
 
101
96
  // Also add siblings , i.e. features having the same parent as the clicked
102
97
  // one and intersecting the click position
103
- if (mousePosition.featureAndGlyphUnderMouse.feature.parent) {
104
- const siblings =
105
- mousePosition.featureAndGlyphUnderMouse.feature.parent.children
98
+ if (feature.parent) {
99
+ const siblings = feature.parent.children
106
100
  if (siblings) {
107
101
  for (const [, sib] of siblings) {
108
- if (sib._id == mousePosition.featureAndGlyphUnderMouse.feature._id) {
102
+ if (sib._id == feature._id) {
109
103
  continue
110
104
  }
111
105
  if (sib.min < bp && sib.max >= bp) {
112
- clickedFeatures.push(sib)
106
+ relatedFeatures.push(sib)
113
107
  }
114
108
  }
115
109
  }
116
110
  }
117
- return clickedFeatures
111
+ return relatedFeatures
118
112
  }