Skip to content

Commit

Permalink
Convert to use mobx-state-tree autoruns instead of useEffect, with po…
Browse files Browse the repository at this point in the history
…tential for displaying multiple structures (#13)
  • Loading branch information
cmdcolin committed Aug 23, 2024
1 parent a8ded30 commit 39c30bd
Show file tree
Hide file tree
Showing 35 changed files with 1,781 additions and 2,705 deletions.
8 changes: 2 additions & 6 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,7 @@ import tseslint from 'typescript-eslint'

export default tseslint.config(
{
ignores: [
'**/dist/**/*',
'jest.config.js',
'eslint.config.mjs',
'esbuild.mjs',
],
ignores: ['**/dist/**/*', 'eslint.config.mjs', 'esbuild.mjs'],
},
{
languageOptions: {
Expand Down Expand Up @@ -78,6 +73,7 @@ export default tseslint.config(

'unicorn/no-null': 'off',
'unicorn/prefer-spread': 'off',
'unicorn/no-nested-ternary': 'off',
'unicorn/no-useless-undefined': 'off',
'unicorn/catch-error-name': 'off',
'unicorn/filename-case': 'off',
Expand Down
6 changes: 0 additions & 6 deletions jest.config.js

This file was deleted.

15 changes: 6 additions & 9 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"src"
],
"scripts": {
"test": "jest",
"test": "vitest",
"clean": "rimraf dist",
"format": "prettier --write .",
"prebuild": "npm run clean",
Expand All @@ -37,8 +37,7 @@
"@mui/material": "^5.12.0",
"@mui/system": "^5.12.0",
"@mui/x-data-grid": "^7.3.0",
"@types/jest": "^29.5.12",
"@types/node": "^20.11.16",
"@types/node": "^22.5.0",
"@types/pako": "^2.0.0",
"@types/react": "^18.2.54",
"@typescript-eslint/eslint-plugin": "^8.1.0",
Expand All @@ -48,11 +47,10 @@
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-prettier": "^5.0.0",
"eslint-plugin-react": "^7.20.3",
"eslint-plugin-react-hooks": "^4.0.8",
"eslint-plugin-react-refresh": "^0.4.3",
"eslint-plugin-react-hooks": "^5.1.0-rc-eb3ad065-20240822",
"eslint-plugin-react-refresh": "^0.4.11",
"eslint-plugin-unicorn": "^55.0.0",
"fp-ts": "^2.16.9",
"jest": "^29.7.0",
"mobx": "^6.0.0",
"mobx-react": "^9.0.1",
"mobx-state-tree": "5.4.2",
Expand All @@ -63,11 +61,10 @@
"react-dom": "^18.2.0",
"rimraf": "^6.0.1",
"rxjs": "^7.0.0",
"ts-jest": "^29.1.2",
"ts-node": "^10.3.0",
"tss-react": "^4.9.4",
"typescript": "^5.3.3",
"typescript-eslint": "^8.1.0"
"typescript-eslint": "^8.1.0",
"vitest": "^2.0.5"
},
"author": "Colin <[email protected]>",
"license": "MIT"
Expand Down
21 changes: 4 additions & 17 deletions src/AddHighlightModel/GenomeMouseoverHighlight.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,38 +4,25 @@ import { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view'
import { getSession } from '@jbrowse/core/util'
// locals
import Highlight from './Highlight'
import { checkHovered } from '../ProteinView/util'

const GenomeMouseoverHighlight = observer(function ({
model,
}: {
model: LinearGenomeViewModel
}) {
const { hovered } = getSession(model)
return hovered &&
typeof hovered === 'object' &&
'hoverPosition' in hovered ? (
<HoverHighlight model={model} />
) : null
})

const HoverHighlight = observer(function ({
model,
}: {
model: LinearGenomeViewModel
}) {
const session = getSession(model)
if (session.views.some(s => s.type === 'ProteinView')) {
const { hovered } = session
const { views, hovered } = session
if (checkHovered(hovered) && views.some(s => s.type === 'ProteinView')) {
const { assemblyNames } = model
// @ts-expect-error
const { coord, refName } = hovered.hoverPosition
return (
<Highlight
model={model}
start={coord - 1}
end={coord}
refName={refName}
assemblyName={assemblyNames[0]}
assemblyName={assemblyNames[0]!}
/>
)
}
Expand Down
24 changes: 13 additions & 11 deletions src/AddHighlightModel/ProteinToGenomeClickHighlight.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,23 +16,25 @@ const ProteinToGenomeClickHighlight = observer(function ({
}) {
const { assemblyManager, views } = getSession(model)
const { assemblyNames } = model
const p = views.find(f => f.type === 'ProteinView') as
const proteinView = views.find(f => f.type === 'ProteinView') as
| JBrowsePluginProteinViewModel
| undefined
const assemblyName = assemblyNames[0]!
const assembly = assemblyManager.get(assemblyName)
return assembly ? (
<>
{p?.clickGenomeHighlights.map((r, idx) => (
<Highlight
key={`${JSON.stringify(r)}-${idx}}`}
start={r.start}
end={r.end}
refName={r.refName}
assemblyName={assemblyName}
model={model}
/>
))}
{proteinView?.structures.map(structure =>
structure.clickGenomeHighlights.map((r, idx) => (
<Highlight
key={`${JSON.stringify(r)}-${idx}}`}
start={r.start}
end={r.end}
refName={r.refName}
assemblyName={assemblyName}
model={model}
/>
)),
)}
</>
) : null
})
Expand Down
28 changes: 14 additions & 14 deletions src/AddHighlightModel/ProteinToGenomeHoverHighlight.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,32 +7,32 @@ import { getSession } from '@jbrowse/core/util'
import { JBrowsePluginProteinViewModel } from '../ProteinView/model'
import Highlight from './Highlight'

type LGV = LinearGenomeViewModel

const ProteinToGenomeHoverHighlight = observer(function ({
model,
}: {
model: LGV
model: LinearGenomeViewModel
}) {
const { assemblyManager, views } = getSession(model)
const { assemblyNames } = model
const p = views.find(f => f.type === 'ProteinView') as
const proteinView = views.find(f => f.type === 'ProteinView') as
| JBrowsePluginProteinViewModel
| undefined
const assemblyName = assemblyNames[0]!
const assembly = assemblyManager.get(assemblyName)
return assembly ? (
<>
{p?.hoverGenomeHighlights.map((r, idx) => (
<Highlight
key={`${JSON.stringify(r)}-${idx}`}
start={r.start}
end={r.end}
refName={r.refName}
assemblyName={assemblyName}
model={model}
/>
))}
{proteinView?.structures.map(structure =>
structure.hoverGenomeHighlights.map((r, idx) => (
<Highlight
key={`${JSON.stringify(r)}-${idx}`}
start={r.start}
end={r.end}
refName={r.refName}
assemblyName={assemblyName}
model={model}
/>
)),
)}
</>
) : null
})
Expand Down
21 changes: 14 additions & 7 deletions src/LaunchProteinView/components/AlphaFoldDBSearch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,13 +78,14 @@ const AlphaFoldDBSearch = observer(function ({
? `https://alphafold.ebi.ac.uk/files/AF-${uniprotId}-F1-model_v4.cif`
: undefined
const {
seq: structureSequence,
sequences: structureSequences,
isLoading: isRemoteStructureSequenceLoading,
error: remoteStructureSequenceError,
} = useRemoteStructureFileSequence({ url })
const e =
myGeneError || isoformProteinSequencesError || remoteStructureSequenceError

const structureSequence = structureSequences?.[0]
useEffect(() => {
if (isoformSequences !== undefined) {
const ret =
Expand Down Expand Up @@ -116,7 +117,7 @@ const AlphaFoldDBSearch = observer(function ({
variant="h6"
message="Looking up UniProt ID from mygene.info"
/>
) : (uniprotId ? null : (
) : uniprotId ? null : (
<div>
UniProt ID not found. Search sequence on AlphaFoldDB{' '}
<a
Expand All @@ -130,7 +131,7 @@ const AlphaFoldDBSearch = observer(function ({
After visiting the above link, then paste the structure URL into the
Manual tab
</div>
))}
)}
{isIsoformProteinSequencesLoading ? (
<LoadingEllipses
variant="h6"
Expand Down Expand Up @@ -175,10 +176,16 @@ const AlphaFoldDBSearch = observer(function ({
onClick={() => {
session.addView('ProteinView', {
type: 'ProteinView',
url,
seq2: userSelectedProteinSequence?.seq,
feature: selectedTranscript?.toJSON(),
connectedViewId: view.id,
isFloating: true,
structures: [
{
url,
userProvidedTranscriptSequence:
userSelectedProteinSequence?.seq,
feature: selectedTranscript?.toJSON(),
connectedViewId: view.id,
},
],
displayName: `Protein view ${getGeneDisplayName(feature)} - ${getTranscriptDisplayName(selectedTranscript)}`,
})
handleClose()
Expand Down
12 changes: 6 additions & 6 deletions src/LaunchProteinView/components/TranscriptSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,32 +41,32 @@ export default function TranscriptSelector({
.filter(f => !!isoformSequences[f.id()])
.filter(
f =>
isoformSequences[f.id()].seq.replaceAll('*', '') ===
isoformSequences[f.id()]!.seq.replaceAll('*', '') ===
structureSequence,
)
.map(f => (
<MenuItem value={f.id()} key={f.id()}>
{getGeneDisplayName(feature)} - {getTranscriptDisplayName(f)} (
{isoformSequences[f.id()].seq.length}aa) (matches structure
{isoformSequences[f.id()]!.seq.length}aa) (matches structure
residues)
</MenuItem>
))}
{isoforms
.filter(f => !!isoformSequences[f.id()])
.filter(
f =>
isoformSequences[f.id()].seq.replaceAll('*', '') !==
isoformSequences[f.id()]!.seq.replaceAll('*', '') !==
structureSequence,
)
.sort(
(a, b) =>
isoformSequences[b.id()].seq.length -
isoformSequences[a.id()].seq.length,
isoformSequences[b.id()]!.seq.length -
isoformSequences[a.id()]!.seq.length,
)
.map(f => (
<MenuItem value={f.id()} key={f.id()}>
{getGeneDisplayName(feature)} - {getTranscriptDisplayName(f)} (
{isoformSequences[f.id()].seq.length}aa)
{isoformSequences[f.id()]!.seq.length}aa)
</MenuItem>
))}
{isoforms
Expand Down
7 changes: 4 additions & 3 deletions src/LaunchProteinView/components/UserProvidedStructure.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -96,14 +96,15 @@ const UserProvidedStructure = observer(function ({
view,
})
const protein = isoformSequences?.[userSelection ?? '']
const { seq: structureSequence1, error: error3 } =
const { sequences: structureSequences1, error: error3 } =
useLocalStructureFileSequence({ file })

const { seq: structureSequence2, error: error4 } =
const { sequences: structureSequences2, error: error4 } =
useRemoteStructureFileSequence({ url: structureURL })
const structureName =
file?.name ?? structureURL.slice(structureURL.lastIndexOf('/') + 1)
const structureSequence = structureSequence1 ?? structureSequence2
const structureSequences = structureSequences1 ?? structureSequences2
const structureSequence = structureSequences?.[0]

useEffect(() => {
if (isoformSequences !== undefined) {
Expand Down
8 changes: 5 additions & 3 deletions src/LaunchProteinView/components/calculateProteinSequence.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ function getItemId(feat: Feat) {
// filters if successive elements share same start/end
export function dedupe(list: Feat[]) {
return list.filter(
(item, pos, ary) => !pos || getItemId(item) !== getItemId(ary[pos - 1]),
(item, pos, ary) => !pos || getItemId(item) !== getItemId(ary[pos - 1]!),
)
}

Expand Down Expand Up @@ -102,8 +102,10 @@ export async function fetchProteinSeq({
const refName = feature.get('refName')
const session = getSession(view)
const { assemblyManager, rpcManager } = session
const [assemblyName] = view?.assemblyNames ?? []
const assembly = await assemblyManager.waitForAssembly(assemblyName)
const assemblyName = view?.assemblyNames?.[0]
const assembly = assemblyName
? await assemblyManager.waitForAssembly(assemblyName)
: undefined
if (!assembly) {
throw new Error('assembly not found')
}
Expand Down
Loading

0 comments on commit 39c30bd

Please sign in to comment.