diff --git a/packages/jbrowse-plugin-apollo/src/BackendDrivers/CollaborationServerDriver.ts b/packages/jbrowse-plugin-apollo/src/BackendDrivers/CollaborationServerDriver.ts index 9f73f0409..5650ea97b 100644 --- a/packages/jbrowse-plugin-apollo/src/BackendDrivers/CollaborationServerDriver.ts +++ b/packages/jbrowse-plugin-apollo/src/BackendDrivers/CollaborationServerDriver.ts @@ -18,6 +18,7 @@ import { import { type ChangeMessage, ValidationResultSet, + makeUserSessionId, } from '@apollo-annotation/shared' import { getConf } from '@jbrowse/core/configuration' import { type BaseInternetAccountModel } from '@jbrowse/core/pluggableElementTypes' @@ -154,6 +155,10 @@ export class CollaborationServerDriver extends BackendDriver { ) { const { socket } = internetAccount const token = internetAccount.retrieveToken() + if (!token) { + return + } + const localSessionId = makeUserSessionId(token) const channel = `${assembly}-${refSeq}` const changeManager = new ChangeManager(this.clientStore) @@ -163,11 +168,12 @@ export class CollaborationServerDriver extends BackendDriver { internetAccount.setLastChangeSequenceNumber( Number(message.changeSequence), ) - if (message.userSessionId !== token && message.channel === channel) { - const change = Change.fromJSON(message.changeInfo) - if (isFeatureChange(change) && this.haveDataForChange(change)) { - await changeManager.submit(change, { submitToBackend: false }) - } + if (message.userSessionId === localSessionId) { + return // we did this change, no need to apply it again + } + const change = Change.fromJSON(message.changeInfo) + if (isFeatureChange(change) && this.haveDataForChange(change)) { + await changeManager.submit(change, { submitToBackend: false }) } }) } diff --git a/packages/jbrowse-plugin-apollo/src/ChangeManager.ts b/packages/jbrowse-plugin-apollo/src/ChangeManager.ts index bb6433fe6..a6b908f97 100644 --- a/packages/jbrowse-plugin-apollo/src/ChangeManager.ts +++ b/packages/jbrowse-plugin-apollo/src/ChangeManager.ts @@ -42,15 +42,25 @@ export class ChangeManager { const session = getSession(this.dataStore) const controller = new AbortController() - const { jobsManager, isLocked } = getSession( - this.dataStore, - ) as unknown as ApolloSessionModel + // eslint-disable-next-line @typescript-eslint/unbound-method + const { jobsManager, isLocked, changeInProgress, setChangeInProgress } = + getSession(this.dataStore) as unknown as ApolloSessionModel if (isLocked) { session.notify('Cannot submit changes in locked mode') + setChangeInProgress(false) return } + if (changeInProgress) { + session.notify( + 'Could not submit change, there is another change still in progress', + ) + return + } + + setChangeInProgress(true) + const job = { name: change.typeName, statusMessage: 'Pre-validating', @@ -71,6 +81,7 @@ export class ChangeManager { jobsManager.abortJob(job.name, msg) } session.notify(msg, 'error') + setChangeInProgress(false) return } @@ -86,6 +97,7 @@ export class ChangeManager { `Error encountered in client: ${String(error)}. Data may be out of sync, please refresh the page`, 'error', ) + setChangeInProgress(false) return } @@ -120,6 +132,7 @@ export class ChangeManager { console.error(error) session.notify(String(error), 'error') await this.undo(change, false) + setChangeInProgress(false) return } if (!backendResult.ok) { @@ -129,6 +142,7 @@ export class ChangeManager { } session.notify(msg, 'error') await this.undo(change, false) + setChangeInProgress(false) return } if (change.notification) { @@ -143,6 +157,7 @@ export class ChangeManager { if (updateJobsManager) { jobsManager.done(job) } + setChangeInProgress(false) } async undo(change: Change, submitToBackend = true) { diff --git a/packages/jbrowse-plugin-apollo/src/FeatureDetailsWidget/TranscriptWidgetEditLocation.tsx b/packages/jbrowse-plugin-apollo/src/FeatureDetailsWidget/TranscriptWidgetEditLocation.tsx index 1e0115d7e..699e0bb8f 100644 --- a/packages/jbrowse-plugin-apollo/src/FeatureDetailsWidget/TranscriptWidgetEditLocation.tsx +++ b/packages/jbrowse-plugin-apollo/src/FeatureDetailsWidget/TranscriptWidgetEditLocation.tsx @@ -113,6 +113,7 @@ export const TranscriptWidgetEditLocation = observer( const refData = currentAssembly?.getByRefName(refName) const { changeManager } = session.apolloDataStore const seqRef = useRef(null) + const { changeInProgress } = session if (!refData) { return null @@ -724,12 +725,15 @@ export const TranscriptWidgetEditLocation = observer( { + if (changeInProgress) { + return + } // NOTE: codonGenomicPos is important here for calculating the genomic location // of the start codon. We are using the codonGenomicPos as the key in the typography // elements to maintain the genomic postion of the codon start @@ -848,7 +852,7 @@ export const TranscriptWidgetEditLocation = observer( // Trim any sequence before first start codon and after stop codon const startCodonIndex = translationSequence.indexOf('M') - const stopCodonIndex = translationSequence.indexOf('*') + 1 + const stopCodonIndex = translationSequence.indexOf('*') const startCodonPos = translSeqCodonStartGenomicPosArr[startCodonIndex].codonGenomicPos @@ -861,7 +865,7 @@ export const TranscriptWidgetEditLocation = observer( const startCodonGenomicLoc = getCodonGenomicLocation( startCodonPos as unknown as number, ) - const stopCodonGenomicLoc = getCodonGenomicLocation( + let stopCodonGenomicLoc = getCodonGenomicLocation( stopCodonPos as unknown as number, ) @@ -874,6 +878,7 @@ export const TranscriptWidgetEditLocation = observer( return } let promise + stopCodonGenomicLoc += 3 // move to end of stop codon if (startCodonGenomicLoc !== cdsMin) { promise = new Promise((resolve) => { updateCDSLocation( @@ -909,6 +914,7 @@ export const TranscriptWidgetEditLocation = observer( return } let promise + stopCodonGenomicLoc -= 3 // move to end of stop codon if (startCodonGenomicLoc !== cdsMax) { promise = new Promise((resolve) => { updateCDSLocation( @@ -978,16 +984,22 @@ export const TranscriptWidgetEditLocation = observer( }} > - + style={{ border: 'none', background: 'none', padding: 0 }} + disabled={changeInProgress} + > + + - + style={{ border: 'none', background: 'none', padding: 0 }} + disabled={changeInProgress} + > + + @@ -1014,6 +1026,7 @@ export const TranscriptWidgetEditLocation = observer( ) }} style={{ border: '1px solid black', borderRadius: 5 }} + disabled={changeInProgress} /> ) : ( @@ -1031,6 +1044,7 @@ export const TranscriptWidgetEditLocation = observer( ) }} style={{ border: '1px solid black', borderRadius: 5 }} + disabled={changeInProgress} /> )} @@ -1052,6 +1066,7 @@ export const TranscriptWidgetEditLocation = observer( ) }} style={{ border: '1px solid black', borderRadius: 5 }} + disabled={changeInProgress} /> ) : ( @@ -1069,6 +1084,7 @@ export const TranscriptWidgetEditLocation = observer( ) }} style={{ border: '1px solid black', borderRadius: 5 }} + disabled={changeInProgress} /> )} @@ -1113,6 +1129,7 @@ export const TranscriptWidgetEditLocation = observer( true, ) }} + disabled={changeInProgress} /> ) : ( @@ -1129,6 +1146,7 @@ export const TranscriptWidgetEditLocation = observer( false, ) }} + disabled={changeInProgress} /> )} @@ -1149,6 +1167,7 @@ export const TranscriptWidgetEditLocation = observer( false, ) }} + disabled={changeInProgress} /> ) : ( @@ -1165,6 +1184,7 @@ export const TranscriptWidgetEditLocation = observer( true, ) }} + disabled={changeInProgress} /> )} diff --git a/packages/jbrowse-plugin-apollo/src/session/session.ts b/packages/jbrowse-plugin-apollo/src/session/session.ts index dae42af49..bf983dcd6 100644 --- a/packages/jbrowse-plugin-apollo/src/session/session.ts +++ b/packages/jbrowse-plugin-apollo/src/session/session.ts @@ -74,6 +74,7 @@ export function extendSession( apolloSelectedFeature: types.safeReference(AnnotationFeatureExtended), jobsManager: types.optional(ApolloJobModel, {}), isLocked: types.optional(types.boolean, false), + changeInProgress: types.optional(types.boolean, false), }) .volatile(() => ({ apolloHoveredFeature: undefined as HoveredFeature | undefined, @@ -141,6 +142,9 @@ export function extendSession( toggleLocked() { self.isLocked = !self.isLocked }, + setChangeInProgress(changeInProgress: boolean) { + self.changeInProgress = changeInProgress + }, getPluginConfiguration() { const { jbrowse } = getRoot(self) const pluginConfiguration =