Skip to content

Commit

Permalink
Store autosave sessions in IndexedDB database (#4074)
Browse files Browse the repository at this point in the history
  • Loading branch information
cmdcolin authored Dec 7, 2024
1 parent 55d3c6b commit 159a621
Show file tree
Hide file tree
Showing 101 changed files with 3,990 additions and 3,453 deletions.
1 change: 1 addition & 0 deletions .ignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,4 @@ test_data/config_demo.json
plugins/spreadsheet-view/src/SpreadsheetView/test_data/1801160099-N32519_26611_S51_56704.hard-filtered.vcf
website/docs/faq.md
test_data/sars-cov2/ncbi_original.gff3
plugins/data-management/src/UCSCTrackHub/ucscAssemblies.ts
1 change: 1 addition & 0 deletions config/jest/console.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ jest.spyOn(console, 'error').mockImplementation((...args) => {
if (
r.includes('volvox.2bit_404') ||
r.includes('popupState') ||
r.includes('indexedDB') ||
r.includes("Can't perform a React state update on an unmounted") ||
r.includes('Unexpected return value from a callback ref') ||
r.includes('A suspended resource finished loading inside a test') ||
Expand Down
22 changes: 22 additions & 0 deletions packages/app-core/src/Assemblies/SessionAssembliesMixin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,28 @@ export function SessionAssembliesMixin(
return self.sessionAssemblies[length - 1]
},

/**
* #action
*/
addAssembly(conf: AnyConfiguration) {
if (self.adminMode) {
self.jbrowse.addAssemblyConf(conf)
} else {
this.addSessionAssembly(conf)
}
},

/**
* #action
*/
removeAssembly(name: string) {
if (self.adminMode) {
self.jbrowse.removeAssemblyConf(name)
} else {
this.removeSessionAssembly(name)
}
},

/**
* #action
*/
Expand Down
5 changes: 5 additions & 0 deletions packages/app-core/src/JBrowseConfig/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,5 +150,10 @@ export function JBrowseConfigF({
defaultSession: types.optional(types.frozen(), {
name: 'New Session',
}),

/**
* #slot
*/
preConfiguredSessions: types.array(types.frozen()),
})
}
140 changes: 53 additions & 87 deletions packages/app-core/src/RootMenu/index.ts
Original file line number Diff line number Diff line change
@@ -1,35 +1,39 @@
import { types } from 'mobx-state-tree'

import type { Menu, MenuAction } from '../menus'
import type { MenuItem } from '@jbrowse/core/ui/Menu'

export interface Menu {
label: string
menuItems: MenuItem[]
}

/**
* #stateModel RootAppMenuMixin
*/
export function RootAppMenuMixin() {
return types.model({}).actions(s => {
const self = s as { menus: Menu[] }
return {
return types
.model({})
.volatile(() => ({
mutableMenuActions: [] as MenuAction[],
}))
.actions(self => ({
/**
* #action
*/
setMenus(newMenus: Menu[]) {
self.menus = newMenus
self.mutableMenuActions = [
...self.mutableMenuActions,
{ type: 'setMenus', newMenus },
]
},
/**
* #action
* Add a top-level menu
*
* @param menuName - Name of the menu to insert.
*
* @returns The new length of the top-level menus array
*/
appendMenu(menuName: string) {
return self.menus.push({ label: menuName, menuItems: [] })
self.mutableMenuActions = [
...self.mutableMenuActions,
{ type: 'appendMenu', menuName },
]
},
/**
* #action
Expand All @@ -41,18 +45,16 @@ export function RootAppMenuMixin() {
* end, e.g. `insertMenu('My Menu', -1)` will insert the menu as the
* second-to-last one.
*
* @returns The new length of the top-level menus array
*/
insertMenu(menuName: string, position: number) {
self.menus.splice(
(position < 0 ? self.menus.length : 0) + position,
0,
self.mutableMenuActions = [
...self.mutableMenuActions,
{
label: menuName,
menuItems: [],
type: 'insertMenu',
menuName,
position,
},
)
return self.menus.length
]
},
/**
* #action
Expand All @@ -61,16 +63,16 @@ export function RootAppMenuMixin() {
* @param menuName - Name of the top-level menu to append to.
*
* @param menuItem - Menu item to append.
*
* @returns The new length of the menu
*/
appendToMenu(menuName: string, menuItem: MenuItem) {
const menu = self.menus.find(m => m.label === menuName)
if (!menu) {
self.menus.push({ label: menuName, menuItems: [menuItem] })
return 1
}
return menu.menuItems.push(menuItem)
self.mutableMenuActions = [
...self.mutableMenuActions,
{
type: 'appendToMenu',
menuName,
menuItem,
},
]
},
/**
* #action
Expand All @@ -83,19 +85,14 @@ export function RootAppMenuMixin() {
* @param position - Position to insert menu item. If negative, counts
* from the end, e.g. `insertMenu('My Menu', -1)` will insert the menu as
* the second-to-last one.
*
* @returns The new length of the menu
*/
insertInMenu(menuName: string, menuItem: MenuItem, position: number) {
const menu = self.menus.find(m => m.label === menuName)
if (!menu) {
self.menus.push({ label: menuName, menuItems: [menuItem] })
return 1
}
const insertPosition =
position < 0 ? menu.menuItems.length + position : position
menu.menuItems.splice(insertPosition, 0, menuItem)
return menu.menuItems.length
self.mutableMenuActions.push({
type: 'insertInMenu',
menuName,
menuItem,
position,
})
},
/**
* #action
Expand All @@ -109,28 +106,14 @@ export function RootAppMenuMixin() {
* @returns The new length of the sub-menu
*/
appendToSubMenu(menuPath: string[], menuItem: MenuItem) {
let topMenu = self.menus.find(m => m.label === menuPath[0])
if (!topMenu) {
const idx = this.appendMenu(menuPath[0]!)
topMenu = self.menus[idx - 1]!
}
let { menuItems: subMenu } = topMenu
const pathSoFar = [menuPath[0]]
menuPath.slice(1).forEach(menuName => {
pathSoFar.push(menuName)
let sm = subMenu.find(mi => 'label' in mi && mi.label === menuName)
if (!sm) {
const idx = subMenu.push({ label: menuName, subMenu: [] })
sm = subMenu[idx - 1]!
}
if (!('subMenu' in sm)) {
throw new Error(
`"${menuName}" in path "${pathSoFar}" is not a subMenu`,
)
}
subMenu = sm.subMenu
})
return subMenu.push(menuItem)
self.mutableMenuActions = [
...self.mutableMenuActions,
{
type: 'appendToSubMenu',
menuPath,
menuItem,
},
]
},
/**
* #action
Expand All @@ -144,38 +127,21 @@ export function RootAppMenuMixin() {
* @param position - Position to insert menu item. If negative, counts
* from the end, e.g. `insertMenu('My Menu', -1)` will insert the menu as
* the second-to-last one.
*
* @returns The new length of the sub-menu
*/
insertInSubMenu(
menuPath: string[],
menuItem: MenuItem,
position: number,
) {
let topMenu = self.menus.find(m => m.label === menuPath[0])
if (!topMenu) {
const idx = this.appendMenu(menuPath[0]!)
topMenu = self.menus[idx - 1]!
}
let { menuItems: subMenu } = topMenu
const pathSoFar = [menuPath[0]]
menuPath.slice(1).forEach(menuName => {
pathSoFar.push(menuName)
let sm = subMenu.find(mi => 'label' in mi && mi.label === menuName)
if (!sm) {
const idx = subMenu.push({ label: menuName, subMenu: [] })
sm = subMenu[idx - 1]!
}
if (!('subMenu' in sm)) {
throw new Error(
`"${menuName}" in path "${pathSoFar}" is not a subMenu`,
)
}
subMenu = sm.subMenu
})
subMenu.splice(position, 0, menuItem)
return subMenu.length
self.mutableMenuActions = [
...self.mutableMenuActions,
{
type: 'insertInSubMenu',
menuPath,
menuItem,
position,
},
]
},
}
})
}))
}
1 change: 1 addition & 0 deletions packages/app-core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ export * from './JBrowseConfig'
export * from './JBrowseModel'
export * from './Assemblies'
export * from './RootMenu'
export * from './menus'
Loading

0 comments on commit 159a621

Please sign in to comment.