@@ -6,7 +6,7 @@ import { PluginContext } from 'molstar/lib/mol-plugin/context';
6
6
import { isPlainObject } from 'molstar/lib/mol-util/object' ;
7
7
import { sleep } from 'molstar/lib/mol-util/sleep' ;
8
8
import { BehaviorSubject } from 'rxjs' ;
9
- import { PreemptiveQueue , PreemptiveQueueResult , combineUrl , createIndex , distinct } from '../../helpers' ;
9
+ import { PreemptiveQueue , PreemptiveQueueResult , combineUrl , createIndex , distinct , nonnegativeModulo } from '../../helpers' ;
10
10
import { StateGalleryConfigValues , getStateGalleryConfig } from './config' ;
11
11
12
12
@@ -64,9 +64,9 @@ type ImageCategory = typeof ImageCategory[number]
64
64
65
65
export interface Image {
66
66
filename : string ,
67
- alt : string ,
68
- description : string ,
69
- clean_description : string ,
67
+ alt ? : string ,
68
+ description ? : string ,
69
+ clean_description ? : string ,
70
70
category ?: ImageCategory ,
71
71
simple_title ?: string ,
72
72
}
@@ -76,8 +76,11 @@ export class StateGalleryManager {
76
76
public readonly images : Image [ ] ; // TODO Rename to states, add docstring
77
77
/** Maps filename to its index within `this.images` */
78
78
private readonly filenameIndex : Map < string , number > ;
79
- public readonly requestedStateName = new BehaviorSubject < string | undefined > ( undefined ) ; // TODO remove if not needed
80
- public readonly loadedStateName = new BehaviorSubject < string | undefined > ( undefined ) ; // TODO remove if not needed
79
+ public readonly events = {
80
+ requestedStateName : new BehaviorSubject < Image | undefined > ( undefined ) , // TODO remove if not needed
81
+ loadedStateName : new BehaviorSubject < Image | undefined > ( undefined ) , // TODO remove if not needed
82
+ status : new BehaviorSubject < 'ready' | 'loading' | 'error' > ( 'ready' ) , // TODO remove if not needed
83
+ } ;
81
84
/** True if at least one state has been loaded (this is to skip animation on the first load) */
82
85
private firstLoaded = false ;
83
86
@@ -104,7 +107,6 @@ export class StateGalleryManager {
104
107
private async _load ( filename : string ) : Promise < void > {
105
108
if ( ! this . plugin . canvas3d ) throw new Error ( 'plugin.canvas3d is not defined' ) ;
106
109
107
- const state = this . getImageByFilename ( filename ) ;
108
110
let snapshot = await this . getSnapshot ( filename ) ;
109
111
const oldCamera = getCurrentCamera ( this . plugin ) ;
110
112
const incomingCamera = getCameraFromSnapshot ( snapshot ) ; // Camera position from the MOLJ file, which may be incorrectly zoomed if viewport width < height
@@ -115,7 +117,6 @@ export class StateGalleryManager {
115
117
camera : ( this . options . LoadCameraOrientation && ! this . firstLoaded ) ? newCamera : oldCamera ,
116
118
transitionDurationInMs : 0 ,
117
119
} ,
118
- description : state ?. simple_title ,
119
120
} ) ;
120
121
await this . plugin . managers . snapshot . setStateSnapshot ( JSON . parse ( snapshot ) ) ;
121
122
await sleep ( this . firstLoaded ? this . options . CameraPreTransitionMs : 0 ) ; // it is necessary to sleep even for 0 ms here, to get animation
@@ -127,14 +128,34 @@ export class StateGalleryManager {
127
128
this . firstLoaded = true ;
128
129
}
129
130
private readonly loader = new PreemptiveQueue ( ( filename : string ) => this . _load ( filename ) ) ;
130
- async load ( filename : string ) : Promise < PreemptiveQueueResult < void > > {
131
- this . requestedStateName . next ( filename ) ;
132
- this . loadedStateName . next ( undefined ) ;
133
- const result = await this . loader . requestRun ( filename ) ;
134
- if ( result . status === 'completed' ) {
135
- this . loadedStateName . next ( filename ) ;
131
+ async load ( img : Image | string ) : Promise < PreemptiveQueueResult < void > > {
132
+ if ( typeof img === 'string' ) {
133
+ img = this . getImageByFilename ( img ) ?? { filename : img } ;
134
+ }
135
+ this . events . requestedStateName . next ( img ) ;
136
+ this . events . loadedStateName . next ( undefined ) ;
137
+ this . events . status . next ( 'loading' ) ;
138
+ let result ;
139
+ try {
140
+ result = await this . loader . requestRun ( img . filename ) ;
141
+ return result ;
142
+ } finally {
143
+ if ( result ?. status === 'completed' ) {
144
+ this . events . loadedStateName . next ( img ) ;
145
+ this . events . status . next ( 'ready' ) ;
146
+ }
147
+ // if resolves with result.status 'cancelled' or 'skipped', keep current state
148
+ if ( ! result ) {
149
+ this . events . status . next ( 'error' ) ;
150
+ }
136
151
}
137
- return result ;
152
+ }
153
+ async shift ( shift : number ) {
154
+ const current = this . events . requestedStateName . value ;
155
+ const iCurrent = ( current !== undefined ) ? this . filenameIndex . get ( current . filename ) : undefined ;
156
+ let iNew = ( iCurrent !== undefined ) ? ( iCurrent + shift ) : ( shift > 0 ) ? ( shift - 1 ) : shift ;
157
+ iNew = nonnegativeModulo ( iNew , this . images . length ) ;
158
+ return await this . load ( this . images [ iNew ] ) ;
138
159
}
139
160
140
161
private readonly cache : { [ filename : string ] : string } = { } ;
@@ -258,7 +279,7 @@ function removeWithSuffixes(images: Image[], suffixes: string[]): Image[] {
258
279
return images . filter ( img => ! suffixes . some ( suffix => img . filename . endsWith ( suffix ) ) ) ;
259
280
}
260
281
261
- function modifySnapshot ( snapshot : string , options : { removeCanvasProps ?: boolean , replaceCamera ?: { camera : Camera . Snapshot , transitionDurationInMs : number } , description ?: string | null } ) {
282
+ function modifySnapshot ( snapshot : string , options : { removeCanvasProps ?: boolean , replaceCamera ?: { camera : Camera . Snapshot , transitionDurationInMs : number } } ) {
262
283
const json = JSON . parse ( snapshot ) as PluginStateSnapshotManager . StateSnapshot ;
263
284
for ( const entry of json . entries ?? [ ] ) {
264
285
if ( entry . snapshot ) {
@@ -273,11 +294,6 @@ function modifySnapshot(snapshot: string, options: { removeCanvasProps?: boolean
273
294
transitionDurationInMs : transitionDurationInMs > 0 ? transitionDurationInMs : undefined ,
274
295
} ;
275
296
}
276
- if ( typeof options . description === 'string' ) {
277
- entry . description = options . description ;
278
- } else if ( options . description === null ) {
279
- delete entry . description ;
280
- }
281
297
}
282
298
}
283
299
return JSON . stringify ( json ) ;
0 commit comments