@jbrowse/plugin-linear-genome-view
Version:
JBrowse 2 linear genome view
243 lines (242 loc) • 8.42 kB
JavaScript
import { readConfObject } from '@jbrowse/core/configuration';
import { assembleLocString, getContainingDisplay, getSession, makeAbortableReaction, } from '@jbrowse/core/util';
import { stopStopToken } from '@jbrowse/core/util/stopToken';
import { getRpcSessionId, getTrackAssemblyNames, } from '@jbrowse/core/util/tracks';
import { isRetryException } from '@jbrowse/core/util/types';
import { getParent, isAlive, types } from '@jbrowse/mobx-state-tree';
import ServerSideRenderedBlockContent from "../components/ServerSideRenderedBlockContent.js";
const blockState = types
.model('BlockState', {
key: types.string,
region: types.frozen(),
reloadFlag: 0,
isLeftEndOfDisplayedRegion: false,
isRightEndOfDisplayedRegion: false,
})
.volatile(() => ({
stopToken: undefined,
filled: false,
reactElement: undefined,
features: undefined,
layout: undefined,
blockStatusMessage: '',
error: undefined,
message: undefined,
maxHeightReached: false,
ReactComponent: ServerSideRenderedBlockContent,
renderProps: undefined,
renderArgs: undefined,
isRenderingPending: true,
cachedDisplay: undefined,
}))
.actions(self => {
function stopCurrentToken() {
if (self.stopToken !== undefined) {
stopStopToken(self.stopToken);
self.stopToken = undefined;
}
}
function clearRenderState() {
self.filled = false;
self.reactElement = undefined;
self.features = undefined;
self.layout = undefined;
self.maxHeightReached = false;
self.renderProps = undefined;
self.renderArgs = undefined;
}
return {
doReload() {
self.reloadFlag = self.reloadFlag + 1;
},
setStatusMessage(message) {
self.blockStatusMessage = message;
},
setLoading(newStopToken) {
stopCurrentToken();
self.isRenderingPending = true;
self.error = undefined;
self.message = undefined;
self.stopToken = newStopToken;
},
setMessage(messageText) {
stopCurrentToken();
self.isRenderingPending = false;
self.message = messageText;
self.error = undefined;
clearRenderState();
},
setRendered(props) {
if (!props) {
return;
}
const { reactElement, features, layout, maxHeightReached, renderProps, renderArgs, } = props;
self.filled = true;
self.isRenderingPending = false;
self.message = undefined;
self.reactElement = reactElement;
self.features = features;
self.layout = layout;
self.error = undefined;
self.maxHeightReached = maxHeightReached;
self.renderProps = renderProps;
self.renderArgs = renderArgs;
self.stopToken = undefined;
},
setError(error) {
console.error(error);
stopCurrentToken();
self.isRenderingPending = false;
self.message = undefined;
self.error = error;
clearRenderState();
if (isRetryException(error)) {
this.reload();
}
},
reload() {
self.stopToken = undefined;
self.isRenderingPending = false;
self.error = undefined;
self.message = undefined;
self.ReactComponent = ServerSideRenderedBlockContent;
clearRenderState();
getParent(self, 2).reload();
},
setCachedDisplay(display) {
self.cachedDisplay = display;
},
beforeDestroy() {
;
(async () => {
try {
stopCurrentToken();
if (self.renderArgs && self.cachedDisplay) {
const { rpcManager } = getSession(self);
const { rendererType } = self.cachedDisplay;
await rendererType.freeResourcesInClient(rpcManager, JSON.parse(JSON.stringify(self.renderArgs)));
}
}
catch (e) {
console.error('Error while destroying block', e);
}
})();
},
};
})
.views(self => ({
get statusMessage() {
return self.isRenderingPending
? self.blockStatusMessage ||
self.cachedDisplay?.statusMessage ||
'Loading'
: undefined;
},
get displayHeight() {
return self.cachedDisplay?.height;
},
}))
.actions(self => ({
afterAttach() {
const display = self.cachedDisplay || getContainingDisplay(self);
setTimeout(() => {
if (isAlive(self)) {
makeAbortableReaction(self, renderBlockData, renderBlockEffect, {
name: `${display.id}/${assembleLocString(self.region)} rendering`,
delay: display.renderDelay,
fireImmediately: true,
}, self.setLoading, self.setRendered, self.setError);
}
}, display.renderDelay);
},
}));
export default blockState;
export function renderBlockData(self, optDisplay) {
try {
const display = (optDisplay ||
self.cachedDisplay ||
getContainingDisplay(self));
const { assemblyManager, rpcManager } = getSession(display);
const { adapterConfig, rendererType, error, parentTrack } = display;
const assemblyNames = getTrackAssemblyNames(parentTrack);
const regionAsm = self.region.assemblyName;
if (!assemblyNames.includes(regionAsm) &&
!assemblyNames.some(name => assemblyManager.get(name)?.hasName(regionAsm))) {
throw new Error(`region assembly (${regionAsm}) does not match track assemblies (${assemblyNames})`);
}
const renderProps = display.renderProps();
const renderingProps = display.renderingProps?.();
const { config } = renderProps;
readConfObject(config);
const sessionId = getRpcSessionId(display);
const trackInstanceId = parentTrack.id;
const cannotBeRenderedReason = display.regionCannotBeRendered(self.region);
const assembly = assemblyManager.get(self.region.assemblyName);
const seqAdapterRefName = assembly?.getSeqAdapterRefName(self.region.refName);
return {
rendererType,
rpcManager,
renderProps,
renderingProps,
cannotBeRenderedReason,
displayError: error,
renderArgs: {
statusCallback: (message) => {
if (isAlive(self)) {
self.setStatusMessage(message);
}
},
assemblyName: self.region.assemblyName,
regions: [
{
...self.region,
seqAdapterRefName,
},
],
adapterConfig,
rendererType: rendererType.name,
sessionId,
trackInstanceId,
blockKey: self.key,
reloadFlag: self.reloadFlag,
timeout: 1_000_000,
},
};
}
catch (e) {
return {
displayError: e,
};
}
}
async function renderBlockEffect(props, stopToken, self) {
if (!props || !isAlive(self)) {
return undefined;
}
const { rendererType, renderProps, renderingProps, rpcManager, renderArgs, cannotBeRenderedReason, displayError, } = props;
if (displayError) {
self.setError(displayError);
return undefined;
}
if (cannotBeRenderedReason) {
self.setMessage(cannotBeRenderedReason);
return undefined;
}
if (renderProps.notReady || !renderArgs) {
return undefined;
}
const { reactElement, features, layout, maxHeightReached } = await rendererType.renderInClient(rpcManager, {
...renderArgs,
...renderProps,
renderingProps,
stopToken,
});
return {
reactElement,
features,
layout,
maxHeightReached,
renderProps,
renderArgs,
};
}