Test state machine hook
This commit is contained in:
parent
c3dd2608b2
commit
870489be1d
@ -36,6 +36,8 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "7.7.6",
|
"@babel/runtime": "7.7.6",
|
||||||
|
"@xstate/immer": "^0.1.0",
|
||||||
|
"@xstate/react": "^0.8.1",
|
||||||
"xstate": "^4.10.0"
|
"xstate": "^4.10.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,13 +1,10 @@
|
|||||||
import React, { useContext, useReducer } from 'react';
|
import React, { useContext } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
const TRACKED_MEASUREMENTS_INITIAL_STATE = {
|
import { useMachine } from '@xstate/react';
|
||||||
/** StudyInstanceUID */
|
import {
|
||||||
trackedStudy: '',
|
measurementTrackingMachine,
|
||||||
/** string[] of SeriesInstanceUID */
|
defaultOptions,
|
||||||
trackedSeries: [],
|
} from './measurementTrackingMachine';
|
||||||
/** key, value => displaySetInstanceUID, string label */
|
|
||||||
viewportLabels: new Map(),
|
|
||||||
};
|
|
||||||
|
|
||||||
const TrackedMeasurementsContext = React.createContext();
|
const TrackedMeasurementsContext = React.createContext();
|
||||||
|
|
||||||
@ -20,49 +17,27 @@ const useTrackedMeasurements = () => useContext(TrackedMeasurementsContext);
|
|||||||
* @param {*} param0
|
* @param {*} param0
|
||||||
*/
|
*/
|
||||||
function TrackedMeasurementsContextProvider({ children }) {
|
function TrackedMeasurementsContextProvider({ children }) {
|
||||||
/**
|
// TODO: Configure `UIViewportNotificationService` for appropriate actions
|
||||||
*
|
const measurementTrackingMachineOptions = Object.assign({}, defaultOptions);
|
||||||
* @param {object} state - previous state
|
const [
|
||||||
* @param {object} action
|
trackedMeasurements,
|
||||||
* @param {string} action.type
|
sendTrackedMeasurementsEvent,
|
||||||
* @param {object} action.payload
|
trackedMeasurementsService,
|
||||||
*/
|
] = useMachine(measurementTrackingMachine, measurementTrackingMachineOptions);
|
||||||
const reducer = (state, action) => {
|
|
||||||
switch (action.type) {
|
|
||||||
case 'TRACK_SERIES':
|
|
||||||
const { StudyInstanceUID, SeriesInstanceUID } = action.payload;
|
|
||||||
return state;
|
|
||||||
case 'UNTRACK_SERIES':
|
|
||||||
return state;
|
|
||||||
default:
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Computed:
|
|
||||||
// numTrackedSeries
|
|
||||||
|
|
||||||
// API
|
|
||||||
// -
|
|
||||||
// - TrackSeries(studyUid, seriesUid) (or... TrackViewport?)
|
|
||||||
// - Checks against tracked study + series
|
|
||||||
// - Initiates prompt if required
|
|
||||||
// - does nothing for SR modality? (how do we know SR viewport?????????)
|
|
||||||
// - UntrackSeries(seriesUid)
|
|
||||||
const [trackedMeasurements, dispatchTrackedMeasurements] = useReducer(
|
|
||||||
reducer,
|
|
||||||
TRACKED_MEASUREMENTS_INITIAL_STATE
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TrackedMeasurementsContext.Provider
|
<TrackedMeasurementsContext.Provider
|
||||||
value={[trackedMeasurements, dispatchTrackedMeasurements]}
|
value={[trackedMeasurements, sendTrackedMeasurementsEvent]}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</TrackedMeasurementsContext.Provider>
|
</TrackedMeasurementsContext.Provider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TrackedMeasurementsContextProvider.propTypes = {
|
||||||
|
children: PropTypes.oneOf([PropTypes.func, PropTypes.node]),
|
||||||
|
};
|
||||||
|
|
||||||
export {
|
export {
|
||||||
TrackedMeasurementsContext,
|
TrackedMeasurementsContext,
|
||||||
TrackedMeasurementsContextProvider,
|
TrackedMeasurementsContextProvider,
|
||||||
|
|||||||
@ -0,0 +1,196 @@
|
|||||||
|
import { Machine, assign } from 'xstate';
|
||||||
|
// import { assign } from '@xstate/immer';
|
||||||
|
|
||||||
|
const machineConfiguration = {
|
||||||
|
id: 'measurementTracking',
|
||||||
|
initial: 'notTracking',
|
||||||
|
context: {
|
||||||
|
prevTrackedStudy: '',
|
||||||
|
prevTrackedSeries: [],
|
||||||
|
trackedStudy: '',
|
||||||
|
trackedSeries: [],
|
||||||
|
promptResponse: 0,
|
||||||
|
},
|
||||||
|
states: {
|
||||||
|
notTracking: {
|
||||||
|
entry: 'clearContext',
|
||||||
|
on: {
|
||||||
|
TRACK_SERIES: {
|
||||||
|
target: 'awaitShouldTrackPrompt',
|
||||||
|
actions: 'trackSeries',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
awaitShouldTrackPrompt: {
|
||||||
|
initial: 'prompt',
|
||||||
|
states: {
|
||||||
|
prompt: {
|
||||||
|
invoke: {
|
||||||
|
id: 'shouldTrackPrompt',
|
||||||
|
src: () => confirmDialog('Should we start tracking?'),
|
||||||
|
onDone: {
|
||||||
|
target: 'validateResponse',
|
||||||
|
actions: assign({ promptResponse: (ctx, evt) => evt.data }),
|
||||||
|
},
|
||||||
|
onError: {
|
||||||
|
target: 'validateResponse',
|
||||||
|
actions: assign({ promptResponse: (ctx, evt) => evt.data }),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
validateResponse: {
|
||||||
|
on: {
|
||||||
|
'': [
|
||||||
|
{
|
||||||
|
target: '#measurementTracking.tracking',
|
||||||
|
cond: 'acceptResponse',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
target: '#measurementTracking.neverTrack',
|
||||||
|
cond: 'rejectResponse',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
target: '#measurementTracking.notTracking',
|
||||||
|
cond: 'cancelResponse',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
neverTrack: {
|
||||||
|
type: 'final',
|
||||||
|
},
|
||||||
|
tracking: {
|
||||||
|
on: {
|
||||||
|
TRACK_SERIES: [
|
||||||
|
{
|
||||||
|
target: 'tracking',
|
||||||
|
cond: 'hasNewSeriesForTrackedStudy',
|
||||||
|
actions: 'trackSeries',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
target: 'awaitShouldTrackNewStudyPrompt',
|
||||||
|
cond: 'willTrackNewStudy',
|
||||||
|
actions: ['setPrevTracked', 'clearTrackedSeries', 'trackSeries'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
UNTRACK_SERIES: [
|
||||||
|
{
|
||||||
|
target: 'notTracking',
|
||||||
|
cond: 'willBeEmpty',
|
||||||
|
actions: 'untrackSeries',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
target: 'tracking',
|
||||||
|
actions: 'untrackSeries',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
awaitShouldTrackNewStudyPrompt: {
|
||||||
|
initial: 'prompt',
|
||||||
|
states: {
|
||||||
|
prompt: {
|
||||||
|
invoke: {
|
||||||
|
id: 'shouldTrackPrompt',
|
||||||
|
src: () => confirmDialog('Should we track new study?'),
|
||||||
|
onDone: {
|
||||||
|
target: 'validateResponse',
|
||||||
|
actions: assign({ promptResponse: (ctx, evt) => evt.data }),
|
||||||
|
},
|
||||||
|
onError: {
|
||||||
|
target: 'validateResponse',
|
||||||
|
actions: assign({ promptResponse: (ctx, evt) => evt.data }),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
validateResponse: {
|
||||||
|
on: {
|
||||||
|
'': [
|
||||||
|
{
|
||||||
|
target: '#measurementTracking.tracking',
|
||||||
|
cond: 'acceptResponse',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
target: '#measurementTracking.tracking',
|
||||||
|
cond: 'rejectResponse',
|
||||||
|
actions: 'restorePrevTracked',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
strict: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
function confirmDialog(msg) {
|
||||||
|
return new Promise(function(resolve, reject) {
|
||||||
|
let confirmed = window.confirm(msg);
|
||||||
|
|
||||||
|
return confirmed ? resolve(1) : reject(-1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultOptions = {
|
||||||
|
actions: {
|
||||||
|
clearContext: assign({
|
||||||
|
prevTrackedStudy: '',
|
||||||
|
prevTrackedSeries: [],
|
||||||
|
trackedStudy: '',
|
||||||
|
trackedSeries: [],
|
||||||
|
promptResponse: 0,
|
||||||
|
}),
|
||||||
|
setPrevTracked: assign(ctx => ({
|
||||||
|
prevTrackedStudy: ctx.trackedStudy,
|
||||||
|
prevTrackedSeries: ctx.trackedSeries.slice(),
|
||||||
|
})),
|
||||||
|
restorePrevTracked: assign(ctx => ({
|
||||||
|
trackedStudy: ctx.prevTrackedStudy,
|
||||||
|
trackedSeries: ctx.prevTrackedSeries.slice(),
|
||||||
|
})),
|
||||||
|
clearTrackedSeries: assign(() => ({
|
||||||
|
trackedStudy: '',
|
||||||
|
trackedSeries: [],
|
||||||
|
})),
|
||||||
|
trackSeries: assign((ctx, evt) => {
|
||||||
|
const prevTrackedSeries = ctx.trackedSeries.slice();
|
||||||
|
return {
|
||||||
|
trackedStudy: evt.StudyInstanceUID || '',
|
||||||
|
trackedSeries: [...prevTrackedSeries, evt.SeriesInstanceUID],
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
untrackSeries: assign((ctx, evt) => {
|
||||||
|
const prevTrackedSeries = ctx.trackedSeries.slice();
|
||||||
|
return {
|
||||||
|
trackedSeries: prevTrackedSeries.filter(
|
||||||
|
serUid => serUid !== evt.SeriesInstanceUID
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
guards: {
|
||||||
|
hasNewSeriesForTrackedStudy: (ctx, evt) =>
|
||||||
|
evt.StudyInstanceUID === ctx.trackedStudy &&
|
||||||
|
!ctx.trackedSeries.includes(evt.SeriesInstanceUID),
|
||||||
|
willTrackNewStudy: ctx => ctx.StudyInstanceUID !== ctx.trackedStudy,
|
||||||
|
willBeEmpty: (ctx, evt) =>
|
||||||
|
evt.SeriesInstanceUID &&
|
||||||
|
ctx.trackedSeries.length === 1 &&
|
||||||
|
ctx.trackedSeries[0] === evt.SeriesInstanceUID,
|
||||||
|
acceptResponse: ctx => ctx.promptResponse === 1,
|
||||||
|
cancelResponse: ctx => ctx.promptResponse === 0,
|
||||||
|
rejectResponse: ctx => ctx.promptResponse === -1,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const measurementTrackingMachine = Machine(machineConfiguration, defaultOptions);
|
||||||
|
// .transition(state, eventArgument).value
|
||||||
|
// const service = interpret(measurementTrackingMachine).start();
|
||||||
|
// .send(event): nextState
|
||||||
|
// .state (getter)
|
||||||
|
// .onTransition(state => { state.vale })
|
||||||
|
export { defaultOptions, machineConfiguration, measurementTrackingMachine };
|
||||||
|
export default measurementTrackingMachine;
|
||||||
@ -1,116 +0,0 @@
|
|||||||
import { Machine, interpret } from 'xstate';
|
|
||||||
|
|
||||||
// {
|
|
||||||
// "type": "TRACK_SERIES",
|
|
||||||
// "StudyInstanceUID": "study1",
|
|
||||||
// "SeriesInstanceUID": "series1"
|
|
||||||
// }
|
|
||||||
|
|
||||||
// TODO:
|
|
||||||
// userPrompts should invoke promises:
|
|
||||||
// https://egghead.io/lessons/xstate-invoking-a-promise-for-asynchronous-state-transitions-in-xstate
|
|
||||||
|
|
||||||
const config = {
|
|
||||||
id: 'measurementTracking',
|
|
||||||
initial: 'notTracking',
|
|
||||||
context: {
|
|
||||||
prevTrackedStudy: '',
|
|
||||||
prevTrackedSeries: [],
|
|
||||||
trackedStudy: '',
|
|
||||||
trackedSeries: [],
|
|
||||||
},
|
|
||||||
states: {
|
|
||||||
notTracking: {
|
|
||||||
on: {
|
|
||||||
TRACK_SERIES: {
|
|
||||||
target: 'userPromptShouldTrack',
|
|
||||||
actions: 'trackSeries',
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
userPromptShouldTrack: {
|
|
||||||
on: {
|
|
||||||
YES: 'tracking',
|
|
||||||
CANCEL: 'notTracking',
|
|
||||||
NEVER: 'neverTracking',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
neverTracking: {
|
|
||||||
type: 'final',
|
|
||||||
},
|
|
||||||
tracking: {
|
|
||||||
on: {
|
|
||||||
TRACK_SERIES: [
|
|
||||||
{ target: 'tracking', cond: 'hasNewSeriesForTrackedStudy', actions: 'trackSeries' },
|
|
||||||
{ target: 'userPromptTrackingConflict', cond: 'hasConflictingStudyInstanceUID', actions: ['setPrevTracked', 'clearTrackedSeries', 'trackSeries'] },
|
|
||||||
],
|
|
||||||
UNTRACK_SERIES: [
|
|
||||||
{ target: 'tracking', cond: '', actions: ''},
|
|
||||||
{ target: 'notTracking', cond: '', actions: '' },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
userPromptShouldTrackNewStudy: {
|
|
||||||
on: {
|
|
||||||
CANCEL: {
|
|
||||||
target: 'tracking',
|
|
||||||
actions: 'restorePrevTracked',
|
|
||||||
},
|
|
||||||
YES: 'tracking',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
strict: true,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Actions:
|
|
||||||
// - entry
|
|
||||||
// - exit
|
|
||||||
// - transition (on)
|
|
||||||
|
|
||||||
// Activities:
|
|
||||||
// - can return a "clean-up" function to call on exit
|
|
||||||
|
|
||||||
// actions, guards, services, activities, delays
|
|
||||||
const options = {
|
|
||||||
actions: {
|
|
||||||
logStuff: (context, event) => { console.log(context, event ) },
|
|
||||||
setPrevTracked: assign(context => ({
|
|
||||||
prevTrackedStudy: context.trackedStudy,
|
|
||||||
prevTrackedSeries: context.trackedSeries.slice(),
|
|
||||||
})),
|
|
||||||
restorePrevTracked: assign(context => ({
|
|
||||||
trackedStudy: context.prevTrackedStudy,
|
|
||||||
trackedSeries: context.prevTrackedSeries.slice(),
|
|
||||||
})),
|
|
||||||
clearTrackedSeries: assign(() => ({
|
|
||||||
trackedStudy: '',
|
|
||||||
trackedSeries: [],
|
|
||||||
})),
|
|
||||||
trackSeries: assign((context, event) =>
|
|
||||||
{
|
|
||||||
console.log(context, event);
|
|
||||||
const prevTrackedSeries = context.trackedSeries.slice();
|
|
||||||
return {
|
|
||||||
trackedStudy: event.StudyInstanceUID || '',
|
|
||||||
trackedSeries: [...prevTrackedSeries, event.SeriesInstanceUID],
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
guards: {
|
|
||||||
hasConflictingStudyInstanceUID: (context, event) =>
|
|
||||||
context.StudyInstanceUID !== context.trackedStudy,
|
|
||||||
hasNewSeriesForTrackedStudy: (context, event) =>
|
|
||||||
event.StudyInstanceUID === context.trackedStudy &&
|
|
||||||
!context.trackedSeries.includes(event.SeriesInstanceUID),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
const measurementTrackingMachine = Machine(config, options);
|
|
||||||
// .transition(state, eventArgument).value
|
|
||||||
const service = interpret(measurementTrackingMachine).start();
|
|
||||||
// .send(event): nextState
|
|
||||||
// .state (getter)
|
|
||||||
// .onTransition(state => { state.vale })
|
|
||||||
|
|
||||||
export default measurementTrackingMachine;
|
|
||||||
@ -20,7 +20,7 @@ function PanelStudyBrowserTracking({
|
|||||||
const [{ StudyInstanceUIDs }, dispatchImageViewer] = useImageViewer();
|
const [{ StudyInstanceUIDs }, dispatchImageViewer] = useImageViewer();
|
||||||
const [
|
const [
|
||||||
trackedMeasurements,
|
trackedMeasurements,
|
||||||
dispatchTrackedMeasurements,
|
sendTrackedMeasurementsEvent,
|
||||||
] = useTrackedMeasurements();
|
] = useTrackedMeasurements();
|
||||||
console.warn('trackedMeasurementsState: ', trackedMeasurements);
|
console.warn('trackedMeasurementsState: ', trackedMeasurements);
|
||||||
const [activeTabName, setActiveTabName] = useState('primary');
|
const [activeTabName, setActiveTabName] = useState('primary');
|
||||||
@ -33,15 +33,14 @@ function PanelStudyBrowserTracking({
|
|||||||
|
|
||||||
// TODO: REPLACE ME
|
// TODO: REPLACE ME
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
dispatchTrackedMeasurements({
|
sendTrackedMeasurementsEvent('TRACK_SERIES', {
|
||||||
type: 'TRACK_SERIES',
|
StudyInstanceUID: '1.3.6.1.4.1.25403.345050719074.3824.20170125095258.1',
|
||||||
payload: {
|
SeriesInstanceUID: '1.3.6.1.4.1.25403.345050719074.3824.20170125095258.2',
|
||||||
StudyInstanceUID:
|
//'2.25.406240484039997428244790346419850090743',
|
||||||
'1.3.6.1.4.1.25403.345050719074.3824.20170125095258.1',
|
|
||||||
SeriesInstanceUID: '2.25.406240484039997428244790346419850090743',
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
}, [dispatchTrackedMeasurements]);
|
}, [sendTrackedMeasurementsEvent]);
|
||||||
|
|
||||||
|
const { trackedStudy, trackedSeries } = trackedMeasurements.context;
|
||||||
|
|
||||||
// ~~ studyDisplayList
|
// ~~ studyDisplayList
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -94,11 +93,16 @@ function PanelStudyBrowserTracking({
|
|||||||
const currentDisplaySets = DisplaySetService.activeDisplaySets;
|
const currentDisplaySets = DisplaySetService.activeDisplaySets;
|
||||||
const mappedDisplaySets = _mapDisplaySets(
|
const mappedDisplaySets = _mapDisplaySets(
|
||||||
currentDisplaySets,
|
currentDisplaySets,
|
||||||
thumbnailImageSrcMap
|
thumbnailImageSrcMap,
|
||||||
|
trackedSeries
|
||||||
);
|
);
|
||||||
|
|
||||||
setDisplaySets(mappedDisplaySets);
|
setDisplaySets(mappedDisplaySets);
|
||||||
}, [DisplaySetService.activeDisplaySets, thumbnailImageSrcMap]);
|
}, [
|
||||||
|
DisplaySetService.activeDisplaySets,
|
||||||
|
trackedSeries,
|
||||||
|
thumbnailImageSrcMap,
|
||||||
|
]);
|
||||||
|
|
||||||
// ~~ subscriptions --> displaySets
|
// ~~ subscriptions --> displaySets
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -132,7 +136,8 @@ function PanelStudyBrowserTracking({
|
|||||||
changedDisplaySets => {
|
changedDisplaySets => {
|
||||||
const mappedDisplaySets = _mapDisplaySets(
|
const mappedDisplaySets = _mapDisplaySets(
|
||||||
changedDisplaySets,
|
changedDisplaySets,
|
||||||
thumbnailImageSrcMap
|
thumbnailImageSrcMap,
|
||||||
|
trackedSeries
|
||||||
);
|
);
|
||||||
|
|
||||||
setDisplaySets(mappedDisplaySets);
|
setDisplaySets(mappedDisplaySets);
|
||||||
@ -143,7 +148,13 @@ function PanelStudyBrowserTracking({
|
|||||||
SubscriptionDisplaySetsAdded.unsubscribe();
|
SubscriptionDisplaySetsAdded.unsubscribe();
|
||||||
SubscriptionDisplaySetsChanged.unsubscribe();
|
SubscriptionDisplaySetsChanged.unsubscribe();
|
||||||
};
|
};
|
||||||
}, [DisplaySetService, dataSource, getImageSrc, thumbnailImageSrcMap]);
|
}, [
|
||||||
|
DisplaySetService,
|
||||||
|
dataSource,
|
||||||
|
getImageSrc,
|
||||||
|
thumbnailImageSrcMap,
|
||||||
|
trackedSeries,
|
||||||
|
]);
|
||||||
|
|
||||||
const tabs = _createStudyBrowserTabs(
|
const tabs = _createStudyBrowserTabs(
|
||||||
StudyInstanceUIDs,
|
StudyInstanceUIDs,
|
||||||
@ -221,7 +232,11 @@ function _mapDataSourceStudies(studies) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function _mapDisplaySets(displaySets, thumbnailImageSrcMap) {
|
function _mapDisplaySets(
|
||||||
|
displaySets,
|
||||||
|
thumbnailImageSrcMap,
|
||||||
|
trackedSeriesInstanceUIDs
|
||||||
|
) {
|
||||||
return displaySets.map(ds => {
|
return displaySets.map(ds => {
|
||||||
const imageSrc = thumbnailImageSrcMap[ds.displaySetInstanceUID];
|
const imageSrc = thumbnailImageSrcMap[ds.displaySetInstanceUID];
|
||||||
return {
|
return {
|
||||||
@ -239,6 +254,7 @@ function _mapDisplaySets(displaySets, thumbnailImageSrcMap) {
|
|||||||
displaySetInstanceUID: ds.displaySetInstanceUID,
|
displaySetInstanceUID: ds.displaySetInstanceUID,
|
||||||
// .. Any other data to pass
|
// .. Any other data to pass
|
||||||
},
|
},
|
||||||
|
isTracked: trackedSeriesInstanceUIDs.includes(ds.SeriesInstanceUID),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user