@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.
- package/dist/index.esm.js +11212 -10483
- package/dist/index.esm.js.map +1 -1
- package/dist/jbrowse-plugin-apollo.cjs.development.js +11251 -10509
- package/dist/jbrowse-plugin-apollo.cjs.development.js.map +1 -1
- package/dist/jbrowse-plugin-apollo.cjs.production.min.js +1 -1
- package/dist/jbrowse-plugin-apollo.cjs.production.min.js.map +1 -1
- package/dist/jbrowse-plugin-apollo.umd.development.js +7726 -9014
- package/dist/jbrowse-plugin-apollo.umd.development.js.map +1 -1
- package/dist/jbrowse-plugin-apollo.umd.production.min.js +1 -1
- package/dist/jbrowse-plugin-apollo.umd.production.min.js.map +1 -1
- package/package.json +18 -18
- package/src/ApolloInternetAccount/model.ts +123 -70
- package/src/ApolloRefNameAliasAdapter/ApolloRefNameAliasAdapter.ts +4 -4
- package/src/ApolloSequenceAdapter/ApolloSequenceAdapter.ts +9 -7
- package/src/BackendDrivers/CollaborationServerDriver.ts +72 -20
- package/src/BackendDrivers/DesktopFileDriver.ts +2 -2
- package/src/ChangeManager.ts +36 -14
- package/src/FeatureDetailsWidget/ApolloTranscriptDetailsWidget.tsx +64 -5
- package/src/FeatureDetailsWidget/BasicInformation.tsx +6 -4
- package/src/FeatureDetailsWidget/NumberTextField.tsx +5 -2
- package/src/FeatureDetailsWidget/TranscriptSequence.tsx +70 -73
- package/src/FeatureDetailsWidget/TranscriptWidgetEditLocation.tsx +72 -234
- package/src/LinearApolloDisplay/components/CheckResultWarnings.tsx +92 -0
- package/src/LinearApolloDisplay/components/LinearApolloDisplay.tsx +23 -131
- package/src/LinearApolloDisplay/glyphs/BoxGlyph.ts +50 -194
- package/src/LinearApolloDisplay/glyphs/GeneGlyph.ts +279 -217
- package/src/LinearApolloDisplay/glyphs/GenericChildGlyph.ts +53 -34
- package/src/LinearApolloDisplay/glyphs/Glyph.ts +7 -9
- package/src/LinearApolloDisplay/glyphs/util.ts +19 -0
- package/src/LinearApolloDisplay/stateModel/base.ts +34 -43
- package/src/LinearApolloDisplay/stateModel/layouts.ts +3 -2
- package/src/LinearApolloDisplay/stateModel/mouseEvents.ts +32 -261
- package/src/LinearApolloDisplay/stateModel/rendering.ts +43 -343
- package/src/LinearApolloReferenceSequenceDisplay/components/LinearApolloReferenceSequenceDisplay.tsx +87 -0
- package/src/LinearApolloReferenceSequenceDisplay/components/index.ts +1 -0
- package/src/LinearApolloReferenceSequenceDisplay/configSchema.ts +7 -0
- package/src/LinearApolloReferenceSequenceDisplay/drawSequenceOverlay.ts +181 -0
- package/src/LinearApolloReferenceSequenceDisplay/drawSequenceTrack.ts +218 -0
- package/src/LinearApolloReferenceSequenceDisplay/index.ts +3 -0
- package/src/LinearApolloReferenceSequenceDisplay/stateModel/base.ts +227 -0
- package/src/LinearApolloReferenceSequenceDisplay/stateModel/index.ts +25 -0
- package/src/LinearApolloReferenceSequenceDisplay/stateModel/rendering.ts +157 -0
- package/src/LinearApolloSixFrameDisplay/components/LinearApolloSixFrameDisplay.tsx +101 -38
- package/src/LinearApolloSixFrameDisplay/glyphs/GeneGlyph.ts +334 -262
- package/src/LinearApolloSixFrameDisplay/glyphs/Glyph.ts +12 -8
- package/src/LinearApolloSixFrameDisplay/stateModel/base.ts +42 -4
- package/src/LinearApolloSixFrameDisplay/stateModel/layouts.ts +4 -8
- package/src/LinearApolloSixFrameDisplay/stateModel/mouseEvents.ts +73 -97
- package/src/LinearApolloSixFrameDisplay/stateModel/rendering.ts +49 -61
- package/src/TabularEditor/HybridGrid/Feature.tsx +16 -14
- package/src/TabularEditor/HybridGrid/HybridGrid.tsx +7 -5
- package/src/components/AddAssembly.tsx +34 -38
- package/src/components/AddAssemblyAliases.tsx +1 -1
- package/src/components/AddChildFeature.tsx +5 -2
- package/src/components/AddFeature.tsx +30 -21
- package/src/components/AddRefSeqAliases.tsx +64 -50
- package/src/components/CopyFeature.tsx +4 -2
- package/src/components/CreateApolloAnnotation.tsx +22 -9
- package/src/components/DeleteAssembly.tsx +3 -10
- package/src/components/DownloadGFF3.tsx +2 -2
- package/src/components/EditZoomThresholdDialog.tsx +69 -0
- package/src/components/FilterFeatures.tsx +7 -7
- package/src/components/FilterTranscripts.tsx +6 -6
- package/src/components/ImportFeatures.tsx +1 -1
- package/src/components/ManageChecks.tsx +3 -10
- package/src/components/ManageUsers.tsx +23 -22
- package/src/components/MergeTranscripts.tsx +12 -15
- package/src/components/OntologyTermAutocomplete.tsx +1 -8
- package/src/components/OntologyTermMultiSelect.tsx +11 -11
- package/src/components/OpenLocalFile.tsx +11 -7
- package/src/components/ViewChangeLog.tsx +25 -50
- package/src/components/ViewCheckResults.tsx +2 -8
- package/src/components/index.ts +1 -0
- package/src/config.ts +6 -0
- package/src/index.ts +53 -115
- package/src/makeDisplayComponent.tsx +9 -14
- package/src/menus/index.ts +1 -0
- package/src/{ApolloInternetAccount/addMenuItems.ts → menus/topLevelMenu.ts} +56 -47
- package/src/menus/topLevelMenuAdmin.ts +154 -0
- package/src/session/ClientDataStore.ts +32 -14
- package/src/session/session.ts +159 -121
- package/src/util/annotationFeatureUtils.ts +15 -21
- package/src/util/displayUtils.ts +149 -0
- package/src/util/glyphUtils.ts +329 -0
- package/src/util/loadAssemblyIntoClient.ts +3 -2
- package/src/util/mouseEventsUtils.ts +32 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@apollo-annotation/jbrowse-plugin-apollo",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.9",
|
|
4
4
|
"description": "Apollo plugin for JBrowse 2",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"jbrowse",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"start:watch": "JB_NPM=false NODE_ENV=development rollup --config --watch",
|
|
28
28
|
"start:server": "serve --no-request-logging --cors --listen 9000 --no-port-switching .",
|
|
29
29
|
"build": "yarn build:shared && yarn clean && rollup --config",
|
|
30
|
-
"browse": "serve --no-request-logging --listen 8999 --no-port-switching .jbrowse",
|
|
30
|
+
"browse": "serve --no-request-logging --listen 8999 --no-port-switching --symlinks .jbrowse",
|
|
31
31
|
"test": "jest",
|
|
32
32
|
"test:ci": "jest --coverage",
|
|
33
33
|
"start:collab-cypress": "yarn workspace @apollo-annotation/collaboration-server run cypress:start",
|
|
@@ -48,15 +48,15 @@
|
|
|
48
48
|
}
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
|
-
"@apollo-annotation/common": "^0.3.
|
|
52
|
-
"@apollo-annotation/mst": "^0.3.
|
|
53
|
-
"@apollo-annotation/shared": "^0.3.
|
|
51
|
+
"@apollo-annotation/common": "^0.3.9",
|
|
52
|
+
"@apollo-annotation/mst": "^0.3.9",
|
|
53
|
+
"@apollo-annotation/shared": "^0.3.9",
|
|
54
54
|
"@emotion/react": "^11.10.6",
|
|
55
55
|
"@emotion/styled": "^11.10.6",
|
|
56
|
-
"@gmod/gff": "
|
|
57
|
-
"@jbrowse/plugin-authentication": "^3.
|
|
58
|
-
"@jbrowse/plugin-linear-genome-view": "^3.
|
|
59
|
-
"@mui/icons-material": "^5.
|
|
56
|
+
"@gmod/gff": "^2.0.0",
|
|
57
|
+
"@jbrowse/plugin-authentication": "^3.6.5",
|
|
58
|
+
"@jbrowse/plugin-linear-genome-view": "^3.6.5",
|
|
59
|
+
"@mui/icons-material": "^6.5.0",
|
|
60
60
|
"@types/jsonpath": "^0.2.0",
|
|
61
61
|
"autosuggest-highlight": "^3.3.4",
|
|
62
62
|
"bson-objectid": "^2.0.4",
|
|
@@ -71,15 +71,15 @@
|
|
|
71
71
|
"tslib": "^2.3.1"
|
|
72
72
|
},
|
|
73
73
|
"devDependencies": {
|
|
74
|
-
"@jbrowse/cli": "^3.
|
|
75
|
-
"@jbrowse/core": "^3.
|
|
76
|
-
"@jbrowse/development-tools": "^2.
|
|
74
|
+
"@jbrowse/cli": "^3.6.5",
|
|
75
|
+
"@jbrowse/core": "^3.6.5",
|
|
76
|
+
"@jbrowse/development-tools": "^2.2.1",
|
|
77
77
|
"@jest/globals": "^29.0.3",
|
|
78
|
-
"@mui/material": "^
|
|
79
|
-
"@mui/x-data-grid": "^
|
|
78
|
+
"@mui/material": "^7.0.0",
|
|
79
|
+
"@mui/x-data-grid": "^8.0.0",
|
|
80
80
|
"@types/autosuggest-highlight": "^3",
|
|
81
81
|
"@types/file-saver": "^2",
|
|
82
|
-
"@types/node": "^
|
|
82
|
+
"@types/node": "^20.19.15",
|
|
83
83
|
"@types/prop-types": "^15",
|
|
84
84
|
"@types/react": "^18.3.4",
|
|
85
85
|
"@types/react-dom": "^18",
|
|
@@ -99,7 +99,7 @@
|
|
|
99
99
|
"react": "^18.2.0",
|
|
100
100
|
"react-dom": "^18.2.0",
|
|
101
101
|
"rimraf": "^3.0.2",
|
|
102
|
-
"rollup": "^2.
|
|
102
|
+
"rollup": "^2.79.2",
|
|
103
103
|
"rxjs": "^7.4.0",
|
|
104
104
|
"serve": "^14.0.1",
|
|
105
105
|
"shx": "^0.3.3",
|
|
@@ -111,9 +111,9 @@
|
|
|
111
111
|
},
|
|
112
112
|
"peerDependencies": {
|
|
113
113
|
"@jbrowse/core": "^3.0.1",
|
|
114
|
-
"@mui/material": "^
|
|
114
|
+
"@mui/material": "^7.0.0",
|
|
115
115
|
"mobx": "^6.6.1",
|
|
116
|
-
"mobx-react": "^
|
|
116
|
+
"mobx-react": "^9.0.0",
|
|
117
117
|
"mobx-state-tree": "^5.4.0",
|
|
118
118
|
"prop-types": "^15.8.1",
|
|
119
119
|
"react": "^18.2.0",
|
|
@@ -23,14 +23,14 @@ import {
|
|
|
23
23
|
isElectron,
|
|
24
24
|
} from '@jbrowse/core/util'
|
|
25
25
|
import { autorun } from 'mobx'
|
|
26
|
-
import { type Instance, flow, getRoot, types } from 'mobx-state-tree'
|
|
26
|
+
import { type Instance, flow, getRoot, isAlive, types } from 'mobx-state-tree'
|
|
27
27
|
import { io } from 'socket.io-client'
|
|
28
28
|
|
|
29
|
+
import { addTopLevelAdminMenus } from '../menus/topLevelMenuAdmin'
|
|
29
30
|
import { type Collaborator } from '../session'
|
|
30
31
|
import { type ApolloRootModel } from '../types'
|
|
31
32
|
import { createFetchErrorMessage } from '../util'
|
|
32
33
|
|
|
33
|
-
import { addMenuItems } from './addMenuItems'
|
|
34
34
|
import { AuthTypeSelector } from './components/AuthTypeSelector'
|
|
35
35
|
import { type ApolloInternetAccountConfigModel } from './configSchema'
|
|
36
36
|
|
|
@@ -61,33 +61,23 @@ const stateModelFactory = (configSchema: ApolloInternetAccountConfigModel) => {
|
|
|
61
61
|
}))
|
|
62
62
|
.volatile(() => ({
|
|
63
63
|
role: undefined as Role | undefined,
|
|
64
|
+
controller: new AbortController(),
|
|
65
|
+
}))
|
|
66
|
+
|
|
67
|
+
.actions((self) => ({
|
|
68
|
+
setRole() {
|
|
69
|
+
const token = self.retrieveToken()
|
|
70
|
+
if (!token) {
|
|
71
|
+
self.role = undefined
|
|
72
|
+
return
|
|
73
|
+
}
|
|
74
|
+
const dec = getDecodedToken(token)
|
|
75
|
+
const { role } = dec
|
|
76
|
+
if (self.role !== role) {
|
|
77
|
+
self.role = role
|
|
78
|
+
}
|
|
79
|
+
},
|
|
64
80
|
}))
|
|
65
|
-
.actions((self) => {
|
|
66
|
-
let roleNotificationSent = false
|
|
67
|
-
return {
|
|
68
|
-
setRole() {
|
|
69
|
-
const token = self.retrieveToken()
|
|
70
|
-
if (!token) {
|
|
71
|
-
self.role = undefined
|
|
72
|
-
return
|
|
73
|
-
}
|
|
74
|
-
const dec = getDecodedToken(token)
|
|
75
|
-
const { role } = dec
|
|
76
|
-
if (!role && !roleNotificationSent) {
|
|
77
|
-
const { session } = getRoot<ApolloRootModel>(self)
|
|
78
|
-
;(session as unknown as AbstractSessionModel).notify(
|
|
79
|
-
'You have registered as a user but have not been given access. Ask your administrator to enable access for your account.',
|
|
80
|
-
'warning',
|
|
81
|
-
)
|
|
82
|
-
// notify
|
|
83
|
-
roleNotificationSent = true
|
|
84
|
-
}
|
|
85
|
-
if (self.role !== role) {
|
|
86
|
-
self.role = role
|
|
87
|
-
}
|
|
88
|
-
},
|
|
89
|
-
}
|
|
90
|
-
})
|
|
91
81
|
.actions((self) => {
|
|
92
82
|
let listener: (event: MessageEvent) => void
|
|
93
83
|
return {
|
|
@@ -205,7 +195,7 @@ const stateModelFactory = (configSchema: ApolloInternetAccountConfigModel) => {
|
|
|
205
195
|
const searchParams = new URLSearchParams({ type: authType })
|
|
206
196
|
url.search = searchParams.toString()
|
|
207
197
|
const uri = url.toString()
|
|
208
|
-
const response = await fetch(uri)
|
|
198
|
+
const response = await fetch(uri, { signal: self.controller.signal })
|
|
209
199
|
if (!response.ok) {
|
|
210
200
|
const errorMessage = await createFetchErrorMessage(
|
|
211
201
|
response,
|
|
@@ -239,7 +229,18 @@ const stateModelFactory = (configSchema: ApolloInternetAccountConfigModel) => {
|
|
|
239
229
|
uri,
|
|
240
230
|
})
|
|
241
231
|
|
|
242
|
-
|
|
232
|
+
let response: Response
|
|
233
|
+
try {
|
|
234
|
+
response = yield apolloFetch(uri, {
|
|
235
|
+
method: 'GET',
|
|
236
|
+
signal: self.controller.signal,
|
|
237
|
+
})
|
|
238
|
+
} catch (error) {
|
|
239
|
+
if (!self.controller.signal.aborted) {
|
|
240
|
+
console.error(error)
|
|
241
|
+
}
|
|
242
|
+
return
|
|
243
|
+
}
|
|
243
244
|
if (!response.ok) {
|
|
244
245
|
const errorMessage = yield createFetchErrorMessage(
|
|
245
246
|
response,
|
|
@@ -274,7 +275,18 @@ const stateModelFactory = (configSchema: ApolloInternetAccountConfigModel) => {
|
|
|
274
275
|
uri,
|
|
275
276
|
})
|
|
276
277
|
|
|
277
|
-
|
|
278
|
+
let response: Response
|
|
279
|
+
try {
|
|
280
|
+
response = yield apolloFetch(uri, {
|
|
281
|
+
method: 'GET',
|
|
282
|
+
signal: self.controller.signal,
|
|
283
|
+
})
|
|
284
|
+
} catch (error) {
|
|
285
|
+
if (!self.controller.signal.aborted) {
|
|
286
|
+
console.error(error)
|
|
287
|
+
}
|
|
288
|
+
return
|
|
289
|
+
}
|
|
278
290
|
if (!response.ok) {
|
|
279
291
|
console.error(
|
|
280
292
|
`Error when fetching the last updates to recover socket connection — ${response.status}`,
|
|
@@ -300,11 +312,13 @@ const stateModelFactory = (configSchema: ApolloInternetAccountConfigModel) => {
|
|
|
300
312
|
if (!token) {
|
|
301
313
|
throw new Error('No Token found')
|
|
302
314
|
}
|
|
315
|
+
const user = getDecodedToken(token)
|
|
316
|
+
const localSessionId = makeUserSessionId(user)
|
|
303
317
|
const { socket } = self
|
|
304
318
|
const { addCheckResult, changeManager, deleteCheckResult } =
|
|
305
319
|
session.apolloDataStore
|
|
306
|
-
socket.on('connect',
|
|
307
|
-
|
|
320
|
+
socket.on('connect', () => {
|
|
321
|
+
void self.getMissingChanges()
|
|
308
322
|
})
|
|
309
323
|
socket.on('connect_error', (error) => {
|
|
310
324
|
console.error(error)
|
|
@@ -324,7 +338,7 @@ const stateModelFactory = (configSchema: ApolloInternetAccountConfigModel) => {
|
|
|
324
338
|
'LastChangeSequence',
|
|
325
339
|
String(message.changeSequence),
|
|
326
340
|
)
|
|
327
|
-
if (message.userSessionId ===
|
|
341
|
+
if (message.userSessionId === localSessionId) {
|
|
328
342
|
return // we did this change, no need to apply it again
|
|
329
343
|
}
|
|
330
344
|
const change = Change.fromJSON(message.changeInfo)
|
|
@@ -332,8 +346,6 @@ const stateModelFactory = (configSchema: ApolloInternetAccountConfigModel) => {
|
|
|
332
346
|
})
|
|
333
347
|
socket.on('USER_LOCATION', (message: UserLocationMessage) => {
|
|
334
348
|
const { channel, locations, userName, userSessionId } = message
|
|
335
|
-
const user = getDecodedToken(token)
|
|
336
|
-
const localSessionId = makeUserSessionId(user)
|
|
337
349
|
if (channel === 'USER_LOCATION' && userSessionId !== localSessionId) {
|
|
338
350
|
const collaborator: Collaborator = {
|
|
339
351
|
name: userName,
|
|
@@ -356,7 +368,10 @@ const stateModelFactory = (configSchema: ApolloInternetAccountConfigModel) => {
|
|
|
356
368
|
}))
|
|
357
369
|
.actions((self) => {
|
|
358
370
|
async function postUserLocation(userLoc: UserLocation[]) {
|
|
359
|
-
|
|
371
|
+
if (!isAlive(self) || self.role === 'none') {
|
|
372
|
+
return
|
|
373
|
+
}
|
|
374
|
+
const { baseURL, controller } = self
|
|
360
375
|
const url = new URL('users/userLocation', baseURL).href
|
|
361
376
|
const userLocation = new URLSearchParams(JSON.stringify(userLoc))
|
|
362
377
|
|
|
@@ -368,6 +383,7 @@ const stateModelFactory = (configSchema: ApolloInternetAccountConfigModel) => {
|
|
|
368
383
|
const response = await apolloFetch(url, {
|
|
369
384
|
method: 'POST',
|
|
370
385
|
body: userLocation,
|
|
386
|
+
signal: controller.signal,
|
|
371
387
|
})
|
|
372
388
|
if (!response.ok) {
|
|
373
389
|
throw new Error('ignore') // ignore message, will get caught by "catch"
|
|
@@ -390,42 +406,73 @@ const stateModelFactory = (configSchema: ApolloInternetAccountConfigModel) => {
|
|
|
390
406
|
}
|
|
391
407
|
return { postUserLocation: debouncePostUserLocation(postUserLocation) }
|
|
392
408
|
})
|
|
393
|
-
.
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
// Get and set server last change sequence into session storage
|
|
402
|
-
yield self.updateLastChangeSequenceNumber()
|
|
403
|
-
// Open socket listeners
|
|
404
|
-
self.addSocketListeners()
|
|
405
|
-
// request user locations
|
|
406
|
-
const { baseURL } = self
|
|
407
|
-
const uri = new URL('users/locations', baseURL).href
|
|
408
|
-
const apolloFetch = self.getFetcher({
|
|
409
|
-
locationType: 'UriLocation',
|
|
410
|
-
uri,
|
|
411
|
-
})
|
|
412
|
-
yield apolloFetch(uri, { method: 'GET' })
|
|
413
|
-
window.addEventListener('beforeunload', () => {
|
|
409
|
+
.volatile(() => ({ roleNotificationSent: false }))
|
|
410
|
+
.actions((self) => {
|
|
411
|
+
function beforeUnloadListener() {
|
|
412
|
+
self.postUserLocation([])
|
|
413
|
+
}
|
|
414
|
+
function visibilityChangeListener() {
|
|
415
|
+
// fires when user switches tabs, apps, goes to homescreen, etc.
|
|
416
|
+
if (document.visibilityState === 'hidden') {
|
|
414
417
|
self.postUserLocation([])
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
418
|
+
}
|
|
419
|
+
// fires when app transitions from prerender, user returns to the app / tab.
|
|
420
|
+
if (document.visibilityState === 'visible') {
|
|
421
|
+
const { session } = getRoot<ApolloRootModel>(self)
|
|
422
|
+
session.broadcastLocations()
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
return {
|
|
426
|
+
initialize: flow(function* initialize(role: Role) {
|
|
427
|
+
if (role === 'none') {
|
|
428
|
+
if (!self.roleNotificationSent) {
|
|
429
|
+
const { session } = getRoot<ApolloRootModel>(self)
|
|
430
|
+
;(session as unknown as AbstractSessionModel).notify(
|
|
431
|
+
'You have registered as an Apollo user but have not been given access. Ask your administrator to enable access for your account.',
|
|
432
|
+
'warning',
|
|
433
|
+
)
|
|
434
|
+
self.roleNotificationSent = true
|
|
435
|
+
}
|
|
436
|
+
return
|
|
420
437
|
}
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
438
|
+
if (role === 'admin') {
|
|
439
|
+
const rootModel = getRoot(self)
|
|
440
|
+
if (isAbstractMenuManager(rootModel)) {
|
|
441
|
+
addTopLevelAdminMenus(rootModel)
|
|
442
|
+
}
|
|
425
443
|
}
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
444
|
+
// Get and set server last change sequence into session storage
|
|
445
|
+
yield self.updateLastChangeSequenceNumber()
|
|
446
|
+
// Open socket listeners
|
|
447
|
+
self.addSocketListeners()
|
|
448
|
+
// request user locations
|
|
449
|
+
const { baseURL } = self
|
|
450
|
+
const uri = new URL('users/locations', baseURL).href
|
|
451
|
+
const apolloFetch = self.getFetcher({
|
|
452
|
+
locationType: 'UriLocation',
|
|
453
|
+
uri,
|
|
454
|
+
})
|
|
455
|
+
yield apolloFetch(uri, {
|
|
456
|
+
method: 'GET',
|
|
457
|
+
signal: self.controller.signal,
|
|
458
|
+
})
|
|
459
|
+
window.addEventListener('beforeunload', beforeUnloadListener)
|
|
460
|
+
document.addEventListener(
|
|
461
|
+
'visibilitychange',
|
|
462
|
+
visibilityChangeListener,
|
|
463
|
+
)
|
|
464
|
+
}),
|
|
465
|
+
removeBeforeUnloadListener() {
|
|
466
|
+
window.removeEventListener('beforeunload', beforeUnloadListener)
|
|
467
|
+
},
|
|
468
|
+
removeVisibilityChangeListener() {
|
|
469
|
+
document.removeEventListener(
|
|
470
|
+
'visibilitychange',
|
|
471
|
+
visibilityChangeListener,
|
|
472
|
+
)
|
|
473
|
+
},
|
|
474
|
+
}
|
|
475
|
+
})
|
|
429
476
|
.actions((self) => ({
|
|
430
477
|
afterAttach() {
|
|
431
478
|
self.setRole()
|
|
@@ -449,6 +496,12 @@ const stateModelFactory = (configSchema: ApolloInternetAccountConfigModel) => {
|
|
|
449
496
|
{ name: 'ApolloInternetAccount' },
|
|
450
497
|
)
|
|
451
498
|
},
|
|
499
|
+
beforeDestroy() {
|
|
500
|
+
self.removeBeforeUnloadListener()
|
|
501
|
+
self.removeVisibilityChangeListener()
|
|
502
|
+
self.controller.abort('internet account beforeDestroy')
|
|
503
|
+
self.socket.close()
|
|
504
|
+
},
|
|
452
505
|
}))
|
|
453
506
|
}
|
|
454
507
|
|
|
@@ -6,7 +6,6 @@ import {
|
|
|
6
6
|
import type RpcServer from 'librpc-web-mod/dist/server'
|
|
7
7
|
import { nanoid } from 'nanoid'
|
|
8
8
|
|
|
9
|
-
import { type BackendDriver } from '../BackendDrivers'
|
|
10
9
|
import { type ApolloSessionModel } from '../session'
|
|
11
10
|
|
|
12
11
|
import { type RefNameAliases } from './../BackendDrivers/BackendDriver'
|
|
@@ -50,9 +49,10 @@ export default class RefNameAliasAdapter
|
|
|
50
49
|
if (!dataStore) {
|
|
51
50
|
throw new Error('No Apollo data store found')
|
|
52
51
|
}
|
|
53
|
-
const backendDriver = dataStore.getBackendDriver(
|
|
54
|
-
|
|
55
|
-
|
|
52
|
+
const backendDriver = dataStore.getBackendDriver(assemblyId)
|
|
53
|
+
if (!backendDriver) {
|
|
54
|
+
throw new Error('No backend driver found')
|
|
55
|
+
}
|
|
56
56
|
const refNameAliases = await backendDriver.getRefNameAliases(assemblyId)
|
|
57
57
|
return refNameAliases
|
|
58
58
|
}
|
|
@@ -10,7 +10,6 @@ import SimpleFeature, { type Feature } from '@jbrowse/core/util/simpleFeature'
|
|
|
10
10
|
import { type NoAssemblyRegion, type Region } from '@jbrowse/core/util/types'
|
|
11
11
|
import { nanoid } from 'nanoid'
|
|
12
12
|
|
|
13
|
-
import { type BackendDriver } from '../BackendDrivers'
|
|
14
13
|
import { type ApolloSessionModel } from '../session'
|
|
15
14
|
|
|
16
15
|
// declare global {
|
|
@@ -62,9 +61,10 @@ export class ApolloSequenceAdapter extends BaseSequenceAdapter {
|
|
|
62
61
|
if (!dataStore) {
|
|
63
62
|
throw new Error('No Apollo data store found')
|
|
64
63
|
}
|
|
65
|
-
const backendDriver = dataStore.getBackendDriver(
|
|
66
|
-
|
|
67
|
-
|
|
64
|
+
const backendDriver = dataStore.getBackendDriver(assemblyId)
|
|
65
|
+
if (!backendDriver) {
|
|
66
|
+
throw new Error('No backend driver found')
|
|
67
|
+
}
|
|
68
68
|
const regions = await backendDriver.getRegions(assemblyId)
|
|
69
69
|
this.regions = regions
|
|
70
70
|
return regions
|
|
@@ -124,9 +124,11 @@ export class ApolloSequenceAdapter extends BaseSequenceAdapter {
|
|
|
124
124
|
observer.error('No Apollo data store found')
|
|
125
125
|
return
|
|
126
126
|
}
|
|
127
|
-
const backendDriver = dataStore.getBackendDriver(
|
|
128
|
-
|
|
129
|
-
|
|
127
|
+
const backendDriver = dataStore.getBackendDriver(assemblyId)
|
|
128
|
+
if (!backendDriver) {
|
|
129
|
+
observer.error('No backend driver found')
|
|
130
|
+
return
|
|
131
|
+
}
|
|
130
132
|
const regions = await backendDriver.getRegions(
|
|
131
133
|
regionWithAssemblyName.assemblyName,
|
|
132
134
|
)
|
|
@@ -4,7 +4,12 @@
|
|
|
4
4
|
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
|
5
5
|
/* eslint-disable @typescript-eslint/no-unsafe-call */
|
|
6
6
|
/* eslint-disable @typescript-eslint/no-unnecessary-condition */
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
type AssemblySpecificChange,
|
|
9
|
+
Change,
|
|
10
|
+
type FeatureChange,
|
|
11
|
+
isFeatureChange,
|
|
12
|
+
} from '@apollo-annotation/common'
|
|
8
13
|
import {
|
|
9
14
|
type AnnotationFeatureSnapshot,
|
|
10
15
|
type ApolloRefSeqI,
|
|
@@ -33,6 +38,14 @@ export interface ApolloRefSeqResponse {
|
|
|
33
38
|
assembly: string
|
|
34
39
|
}
|
|
35
40
|
|
|
41
|
+
interface RefSeq {
|
|
42
|
+
refName: string
|
|
43
|
+
id: string
|
|
44
|
+
aliases: string[]
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
type RefSeqMap = Map<string, RefSeq>
|
|
48
|
+
|
|
36
49
|
export interface ApolloInternetAccount extends BaseInternetAccountModel {
|
|
37
50
|
baseURL: string
|
|
38
51
|
socket: Socket
|
|
@@ -43,6 +56,8 @@ export interface ApolloInternetAccount extends BaseInternetAccountModel {
|
|
|
43
56
|
export class CollaborationServerDriver extends BackendDriver {
|
|
44
57
|
private inFlight = new Map<string, Promise<string>>()
|
|
45
58
|
|
|
59
|
+
private refSeqMaps = new Map<string, RefSeqMap>()
|
|
60
|
+
|
|
46
61
|
private async fetch(
|
|
47
62
|
internetAccount: ApolloInternetAccount,
|
|
48
63
|
info: RequestInfo,
|
|
@@ -92,13 +107,12 @@ export class CollaborationServerDriver extends BackendDriver {
|
|
|
92
107
|
if (!assembly) {
|
|
93
108
|
throw new Error(`Could not find assembly with name "${assemblyName}"`)
|
|
94
109
|
}
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
const refSeq = ids[refName]
|
|
99
|
-
if (!refSeq) {
|
|
110
|
+
const refSeqMap = await this.getRefSeqMapping(assemblyName)
|
|
111
|
+
const refSeqEntry = refSeqMap.get(refName)
|
|
112
|
+
if (!refSeqEntry) {
|
|
100
113
|
throw new Error(`Could not find refSeq "${refName}"`)
|
|
101
114
|
}
|
|
115
|
+
const refSeq = refSeqEntry.id
|
|
102
116
|
const internetAccount = this.clientStore.getInternetAccount(
|
|
103
117
|
assemblyName,
|
|
104
118
|
) as ApolloInternetAccount
|
|
@@ -151,12 +165,28 @@ export class CollaborationServerDriver extends BackendDriver {
|
|
|
151
165
|
)
|
|
152
166
|
if (message.userSessionId !== token && message.channel === channel) {
|
|
153
167
|
const change = Change.fromJSON(message.changeInfo)
|
|
154
|
-
|
|
168
|
+
if (isFeatureChange(change) && this.haveDataForChange(change)) {
|
|
169
|
+
await changeManager.submit(change, { submitToBackend: false })
|
|
170
|
+
}
|
|
155
171
|
}
|
|
156
172
|
})
|
|
157
173
|
}
|
|
158
174
|
}
|
|
159
175
|
|
|
176
|
+
private haveDataForChange(change: FeatureChange): boolean {
|
|
177
|
+
const { assembly, changedIds } = change
|
|
178
|
+
const apolloAssembly = this.clientStore.assemblies.get(assembly)
|
|
179
|
+
if (!apolloAssembly) {
|
|
180
|
+
return false
|
|
181
|
+
}
|
|
182
|
+
for (const changedId of changedIds) {
|
|
183
|
+
if (this.clientStore.getFeature(changedId)) {
|
|
184
|
+
return true
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return false
|
|
188
|
+
}
|
|
189
|
+
|
|
160
190
|
/**
|
|
161
191
|
* Call backend endpoint to get sequence by criteria
|
|
162
192
|
* @param region - Searchable region containing refSeq, start and end
|
|
@@ -171,13 +201,12 @@ export class CollaborationServerDriver extends BackendDriver {
|
|
|
171
201
|
if (!assembly) {
|
|
172
202
|
throw new Error(`Could not find assembly with name "${assemblyName}"`)
|
|
173
203
|
}
|
|
174
|
-
const
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
const refSeq = ids[refName]
|
|
178
|
-
if (!refSeq) {
|
|
204
|
+
const refSeqMap = await this.getRefSeqMapping(assemblyName)
|
|
205
|
+
const refSeqEntry = refSeqMap.get(refName)
|
|
206
|
+
if (!refSeqEntry) {
|
|
179
207
|
throw new Error(`Could not find refSeq "${refName}"`)
|
|
180
208
|
}
|
|
209
|
+
const refSeq = refSeqEntry.id
|
|
181
210
|
if (inFlightPromise) {
|
|
182
211
|
const seq = await inFlightPromise
|
|
183
212
|
return { seq, refSeq }
|
|
@@ -248,7 +277,11 @@ export class CollaborationServerDriver extends BackendDriver {
|
|
|
248
277
|
return seq
|
|
249
278
|
}
|
|
250
279
|
|
|
251
|
-
async
|
|
280
|
+
async getRefSeqMapping(assemblyName: string): Promise<RefSeqMap> {
|
|
281
|
+
const cachedRefSeqMap = this.refSeqMaps.get(assemblyName)
|
|
282
|
+
if (cachedRefSeqMap) {
|
|
283
|
+
return cachedRefSeqMap
|
|
284
|
+
}
|
|
252
285
|
const { assemblyManager } = getSession(this.clientStore)
|
|
253
286
|
const assembly = assemblyManager.get(assemblyName)
|
|
254
287
|
if (!assembly) {
|
|
@@ -278,13 +311,32 @@ export class CollaborationServerDriver extends BackendDriver {
|
|
|
278
311
|
)
|
|
279
312
|
}
|
|
280
313
|
const refSeqs = (await response.json()) as ApolloRefSeqResponse[]
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
314
|
+
const refSeqMap = new Map<string, RefSeq>(
|
|
315
|
+
refSeqs.map((refSeq) => [
|
|
316
|
+
refSeq.name,
|
|
317
|
+
{ refName: refSeq.name, id: refSeq._id, aliases: refSeq.aliases },
|
|
318
|
+
]),
|
|
319
|
+
)
|
|
320
|
+
this.refSeqMaps.set(assemblyName, refSeqMap)
|
|
321
|
+
return refSeqMap
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
async getRefNameAliases(assemblyName: string): Promise<RefNameAliases[]> {
|
|
325
|
+
const refSeqMap = await this.getRefSeqMapping(assemblyName)
|
|
326
|
+
return [...refSeqMap.values()].map((refSeq) => ({
|
|
327
|
+
refName: refSeq.refName,
|
|
328
|
+
aliases: [...new Set([refSeq.id, ...refSeq.aliases])],
|
|
329
|
+
uniqueId: `alias-${refSeq.id}`,
|
|
330
|
+
}))
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
async getRefSeqId(assemblyName: string, refName: string) {
|
|
334
|
+
const refSeqMap = await this.getRefSeqMapping(assemblyName)
|
|
335
|
+
if (!refSeqMap) {
|
|
336
|
+
return
|
|
337
|
+
}
|
|
338
|
+
const refSeq = refSeqMap.get(refName)
|
|
339
|
+
return refSeq?.id
|
|
288
340
|
}
|
|
289
341
|
|
|
290
342
|
async getRegions(assemblyName: string): Promise<Region[]> {
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
annotationFeatureToGFF3,
|
|
14
14
|
splitStringIntoChunks,
|
|
15
15
|
} from '@apollo-annotation/shared'
|
|
16
|
-
import
|
|
16
|
+
import { type GFF3Item, formatSync } from '@gmod/gff'
|
|
17
17
|
import { getConf } from '@jbrowse/core/configuration'
|
|
18
18
|
import { type Region, getSession } from '@jbrowse/core/util'
|
|
19
19
|
import { getSnapshot } from 'mobx-state-tree'
|
|
@@ -165,7 +165,7 @@ export class DesktopFileDriver extends BackendDriver {
|
|
|
165
165
|
})
|
|
166
166
|
}
|
|
167
167
|
|
|
168
|
-
const gff3Contents =
|
|
168
|
+
const gff3Contents = formatSync(gff3Items)
|
|
169
169
|
|
|
170
170
|
// eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/consistent-type-imports
|
|
171
171
|
const fs = require('node:fs') as typeof import('fs')
|