Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { alpha } from '@mui/material'

import type { OntologyRecord } from '../../OntologyManager'
import { MergeExons, MergeTranscripts, SplitExon } from '../../components'
import { DuplicateTranscript } from '../../components/DuplicateTranscript'
import {
type MousePosition,
type MousePositionWithFeature,
Expand Down Expand Up @@ -950,29 +951,54 @@ function getContextMenuItems(
)
}
if (isTranscriptFeature(feature, session)) {
contextMenuItemsForFeature.push({
label: 'Merge transcript',
onClick: () => {
;(session as unknown as AbstractSessionModel).queueDialog(
(doneCallback) => [
MergeTranscripts,
{
session,
handleClose: () => {
doneCallback()
contextMenuItemsForFeature.push(
{
label: 'Merge transcript',
onClick: () => {
;(session as unknown as AbstractSessionModel).queueDialog(
(doneCallback) => [
MergeTranscripts,
{
session,
handleClose: () => {
doneCallback()
},
changeManager,
sourceFeature: feature,
sourceAssemblyId: currentAssemblyId,
selectedFeature,
setSelectedFeature: (feature?: AnnotationFeature) => {
display.setSelectedFeature(feature)
},
},
changeManager,
sourceFeature: feature,
sourceAssemblyId: currentAssemblyId,
selectedFeature,
setSelectedFeature: (feature?: AnnotationFeature) => {
display.setSelectedFeature(feature)
],
)
},
},
{
label: 'Duplicate feature',
onClick: () => {
;(session as unknown as AbstractSessionModel).queueDialog(
(doneCallback) => [
DuplicateTranscript,
{
session,
handleClose: () => {
doneCallback()
},
changeManager,
sourceFeature: feature,
sourceAssemblyId: currentAssemblyId,
selectedFeature,
setSelectedFeature: (feature?: AnnotationFeature) => {
display.setSelectedFeature(feature)
},
},
},
],
)
],
)
},
},
})
)
if (isSessionModelWithWidgets(session)) {
contextMenuItemsForFeature.splice(1, 0, {
label: 'Open transcript editor',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
AddChildFeature,
CopyFeature,
DeleteFeature,
DuplicateTranscript,
MergeExons,
MergeTranscripts,
SplitExon,
Expand Down Expand Up @@ -214,6 +215,27 @@ export function featureContextMenuItems(
session.showWidget(apolloTranscriptWidget)
},
},
{
label: 'Duplicate feature',
onClick: () => {
;(session as unknown as AbstractSessionModel).queueDialog(
(doneCallback) => [
DuplicateTranscript,
{
session,
handleClose: () => {
doneCallback()
},
changeManager,
sourceFeature: feature,
sourceAssemblyId: currentAssemblyId,
selectedFeature,
setSelectedFeature,
},
],
)
},
},
{
label: 'Visible',
type: 'checkbox',
Expand Down
132 changes: 132 additions & 0 deletions packages/jbrowse-plugin-apollo/src/components/DuplicateTranscript.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
/* eslint-disable @typescript-eslint/unbound-method */
import type {
AnnotationFeature,
AnnotationFeatureSnapshot,
} from '@apollo-annotation/mst'
import { AddFeatureChange } from '@apollo-annotation/shared'
import type { AbstractSessionModel } from '@jbrowse/core/util/types'
import { getSnapshot } from '@jbrowse/mobx-state-tree'
import {
Button,
DialogActions,
DialogContent,
DialogContentText,
} from '@mui/material'
import ObjectID from 'bson-objectid'
import React, { useState } from 'react'

import type { ChangeManager } from '../ChangeManager'
import type { ApolloSessionModel } from '../session'

import { Dialog } from './Dialog'

interface DuplicateTranscriptProps {
session: ApolloSessionModel
handleClose(): void
sourceFeature: AnnotationFeature
sourceAssemblyId: string
changeManager: ChangeManager
selectedFeature?: AnnotationFeature
setSelectedFeature(feature?: AnnotationFeature): void
}

export function DuplicateTranscript({
changeManager,
handleClose,
session,
sourceAssemblyId,
sourceFeature,
setSelectedFeature,
}: DuplicateTranscriptProps) {
const [errorMessage, setErrorMessage] = useState('')
const { notify } = session as unknown as AbstractSessionModel

async function onSubmit(event: React.FormEvent<HTMLFormElement>) {
event.preventDefault()
setErrorMessage('')

try {
const parentGene = sourceFeature.parent
if (!parentGene) {
setErrorMessage('No parent gene found for this transcript')
return
}

const transcriptSnapshot = getSnapshot(sourceFeature)
const newTranscriptId = new ObjectID().toHexString()
const duplicateTranscript = {
...transcriptSnapshot,
_id: newTranscriptId,
} as AnnotationFeatureSnapshot

if (duplicateTranscript.children) {
const newChildren: Record<string, AnnotationFeatureSnapshot> = {}
for (const [, child] of Object.entries(duplicateTranscript.children)) {
const newChildId = new ObjectID().toHexString()
newChildren[newChildId] = {
...child,
_id: newChildId,
} as AnnotationFeatureSnapshot
}
duplicateTranscript.children = newChildren
}

const change = new AddFeatureChange({
parentFeatureId: parentGene._id,
changedIds: [parentGene._id],
typeName: 'AddFeatureChange',
assembly: sourceAssemblyId,
addedFeature: duplicateTranscript,
})

await changeManager.submit(change).then(() => {
setSelectedFeature(undefined)
session.apolloSetSelectedFeature(newTranscriptId)
notify('Successfully duplicated transcript', 'success')
})

handleClose()
} catch (error) {
setErrorMessage(
error instanceof Error
? error.message
: 'Failed to duplicate transcript',
)
}
}

return (
<Dialog
open
title="Duplicate transcript"
handleClose={handleClose}
maxWidth={false}
data-testid="duplicate-transcript"
>
<form
onSubmit={(event) => {
void onSubmit(event)
}}
>
<DialogContent style={{ display: 'flex', flexDirection: 'column' }}>
<DialogContentText>
Are you sure you want to create a duplicate of this transcript?
</DialogContentText>
</DialogContent>
<DialogActions>
<Button variant="contained" type="submit">
Yes
</Button>
<Button variant="outlined" type="button" onClick={handleClose}>
Cancel
</Button>
</DialogActions>
</form>
{errorMessage ? (
<DialogContent>
<DialogContentText color="error">{errorMessage}</DialogContentText>
</DialogContent>
) : null}
</Dialog>
)
}
1 change: 1 addition & 0 deletions packages/jbrowse-plugin-apollo/src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ export * from './ViewChangeLog'
export * from './AddRefSeqAliases'
export * from './ViewCheckResults'
export * from './SplitExon'
export * from './DuplicateTranscript'