import { lazy } from 'react' ; import { AbstractSessionModel , TrackViewModel , DialogComponentType , } from '@jbrowse/core/util/types' ; import { AnyConfigurationModel } from '@jbrowse/core/configuration/configurationSchema' ; import addSnackbarToModel from '@jbrowse/core/ui/SnackbarModel' ; import { getContainingView } from '@jbrowse/core/util' ; import { readConfObject } from '@jbrowse/core/configuration' ; import { getMembers , getParent , getSnapshot , getType , isAlive , isModelType , isReferenceType , types , walk , Instance , IAnyStateTreeNode , } from 'mobx-state-tree' ; import PluginManager from '@jbrowse/core/PluginManager' ; import TextSearchManager from '@jbrowse/core/TextSearch/TextSearchManager' ; import InfoIcon from '@material-ui/icons/Info' ; import { ReferringNode } from '../types' ; const AboutDialog = lazy ( ( ) => import ( '@jbrowse/core/ui/AboutDialog' ) ) ; export default function sessionModelFactory ( pluginManager ) { const model = types . model ( 'ReactLinearGenomeViewSession' , { name : types . identifier , margin : 0 , view : pluginManager . getViewType ( 'LinearGenomeView' ) . stateModel , widgets : types . map ( pluginManager . pluggableMstType ( 'widget' , 'stateModel' ) ) , activeWidgets : types . map ( types . safeReference ( pluginManager . pluggableMstType ( 'widget' , 'stateModel' ) ) ) , connectionInstances : types . array ( pluginManager . pluggableMstType ( 'connection' , 'stateModel' ) ) , } ) . volatile ( ( ) => ( { /**
         * this is the globally "selected" object. can be anything.
         * code that wants to deal with this should examine it to see what
         * kind of thing it is.
         */ selection : undefined , /**
         * this is the current "task" that is being performed in the UI.
         * this is usually an object of the form
         * `{ taskName: "configure", target: thing_being_configured }`
         */ task : undefined , queueOfDialogs : [ ] as [ DialogComponentType , any ] [ ] , } ) ) . views ( self => ( { get DialogComponent ( ) { if ( self . queueOfDialogs . length ) { const firstInQueue = self . queueOfDialogs [ 0 ] ; return firstInQueue && firstInQueue [ 0 ] ; } return undefined ; } , get DialogProps ( ) { if ( self . queueOfDialogs . length ) { const firstInQueue = self . queueOfDialogs [ 0 ] ; return firstInQueue && firstInQueue [ 1 ] ; } return undefined ; } , get textSearchManager ( ) : TextSearchManager { return getParent ( self ) . textSearchManager ; } , get rpcManager ( ) { return getParent ( self ) . rpcManager ; } , get configuration ( ) { return getParent ( self ) . config . configuration ; } , get assemblies ( ) { return [ getParent ( self ) . config . assembly ] ; } , get assemblyNames ( ) { return [ getParent ( self ) . config . assemblyName ] ; } , get tracks ( ) { return getParent ( self ) . config . tracks ; } , get aggregateTextSearchAdapters ( ) { return getParent ( self ) . config . aggregateTextSearchAdapters ; } , get connections ( ) { return getParent ( self ) . config . connections ; } , get adminMode ( ) { return false ; } , get assemblyManager ( ) { return getParent ( self ) . assemblyManager ; } , get version ( ) { return '' ; } , get views ( ) { return [ self . view ] ; } , renderProps ( ) { return { theme : readConfObject ( this . configuration , 'theme' ) } ; } , get visibleWidget ( ) { if ( isAlive ( self ) ) { return Array . from ( self . activeWidgets . values ( ) ) [ self . activeWidgets . size - 1 ] ; } return undefined ; } , param object returns getReferring ( object ) { const refs = [ ] ; walk ( getParent ( self ) , node => { if ( isModelType ( getType ( node ) ) ) { const members = getMembers ( node ) ; Object . entries ( members . properties ) . forEach ( ( [ key , value ] ) => { if ( isReferenceType ( value ) && node [ key ] === object ) { refs . push ( { node , key } ) ; } } ) ; } } ) ; return refs ; } , } ) ) . actions ( self => ( { queueDialog ( callback ) { const [ component , props ] = callback ( ( ) => { this . removeActiveDialog ( ) ; } ) ; self . queueOfDialogs = [ ... self . queueOfDialogs , [ component , props ] ] ; } , removeActiveDialog ( ) { self . queueOfDialogs = self . queueOfDialogs . slice ( 1 ) ; } , makeConnection ( configuration , initialSnapshot = { } ) { const { type } = configuration ; if ( ! type ) { throw new Error ( 'track configuration has no `type` listed' ) ; } const name = readConfObject ( configuration , 'name' ) ; const connectionType = pluginManager . getConnectionType ( type ) ; if ( ! connectionType ) { throw new Error ( `unknown connection type ${ type }` ) ; } const connectionData = { ... initialSnapshot , name , type , configuration , } ; const length = self . connectionInstances . push ( connectionData ) ; return self . connectionInstances [ length - 1 ] ; } , removeReferring ( referring , track , callbacks , dereferenceTypeCount ) { referring . forEach ( ( { node } ) => { let dereferenced = false ; try { const type = 'open track(s)' ; const view = getContainingView ( node ) as TrackViewModel ; callbacks . push ( ( ) => view . hideTrack ( track . trackId ) ) ; dereferenced = true ; if ( ! dereferenceTypeCount [ type ] ) { dereferenceTypeCount [ type ] = 0 ; } dereferenceTypeCount [ type ] += 1 ; } catch ( err1 ) { } if ( this . hasWidget ( node ) ) { const type = 'configuration editor widget(s)' ; callbacks . push ( ( ) => this . hideWidget ( node ) ) ; dereferenced = true ; if ( ! dereferenceTypeCount [ type ] ) { dereferenceTypeCount [ type ] = 0 ; } dereferenceTypeCount [ type ] += 1 ; } if ( ! dereferenced ) { throw new Error ( `Error when closing this connection, the following node is still referring to a track configuration: ${ JSON . stringify ( getSnapshot ( node ) ) }` ) ; } } ) ; } , prepareToBreakConnection ( configuration ) { const callbacksToDereferenceTrack = [ ] ; const dereferenceTypeCount = { } ; const name = readConfObject ( configuration , 'name' ) ; const connection = self . connectionInstances . find ( c => c . name === name ) ; connection . tracks . forEach ( ( track ) => { const referring = self . getReferring ( track ) ; this . removeReferring ( referring , track , callbacksToDereferenceTrack , dereferenceTypeCount ) ; } ) ; const safelyBreakConnection = ( ) => { callbacksToDereferenceTrack . forEach ( cb => cb ( ) ) ; this . breakConnection ( configuration ) ; } ; return [ safelyBreakConnection , dereferenceTypeCount ] ; } , breakConnection ( configuration ) { const name = readConfObject ( configuration , 'name' ) ; const connection = self . connectionInstances . find ( c => c . name === name ) ; self . connectionInstances . remove ( connection ) ; } , addView ( typeName , initialState = { } ) { const typeDefinition = pluginManager . getElementType ( 'view' , typeName ) ; if ( ! typeDefinition ) { throw new Error ( `unknown view type ${ typeName }` ) ; } self . view = { ... initialState , type : typeName , } ; } , removeView ( ) { } , addWidget ( typeName , id , initialState = { } , configuration = { type : typeName } ) { const typeDefinition = pluginManager . getElementType ( 'widget' , typeName ) ; if ( ! typeDefinition ) { throw new Error ( `unknown widget type ${ typeName }` ) ; } const data = { ... initialState , id , type : typeName , configuration , } ; self . widgets . set ( id , data ) ; return self . widgets . get ( id ) ; } , showWidget ( widget ) { if ( self . activeWidgets . has ( widget . id ) ) { self . activeWidgets . delete ( widget . id ) ; } self . activeWidgets . set ( widget . id , widget ) ; } , hasWidget ( widget ) { return self . activeWidgets . has ( widget . id ) ; } , hideWidget ( widget ) { self . activeWidgets . delete ( widget . id ) ; } , hideAllWidgets ( ) { self . activeWidgets . clear ( ) ; } , param thing setSelection ( thing ) { self . selection = thing ; } , /**
         * clears the global selection
         */ clearSelection ( ) { self . selection = undefined ; } , clearConnections ( ) { self . connectionInstances . length = 0 ; } , renameCurrentSession ( sessionName ) { return getParent ( self ) . renameCurrentSession ( sessionName ) ; } , } ) ) . views ( self => ( { getTrackActionMenuItems ( config ) { return [ { label : 'About track' , onClick : ( ) => { self . queueDialog ( ( doneCallback ) => [ AboutDialog , { config , handleClose : doneCallback } , ] ) ; } , icon : InfoIcon , } , ] ; } , } ) ) ; return addSnackbarToModel ( model ) ; } export type SessionStateModel = ReturnType < typeof sessionModelFactory > ; export type SessionModel = Instance < SessionStateModel > ; function z ( x ) { return x ; }