Browse Source

state file export improvements
- save full state including all snapshots instead of just the current one

David Sehnal 5 years ago
parent
commit
2429111a59

+ 18 - 5
src/mol-plugin-state/manager/snapshots.ts

@@ -129,7 +129,7 @@ class PluginStateSnapshotManager extends StatefulPluginComponent<{
         return this.state.entries.get(idx).snapshot.id;
     }
 
-    async setRemoteSnapshot(snapshot: PluginStateSnapshotManager.RemoteSnapshot): Promise<PluginState.Snapshot | undefined> {
+    async setStateSnapshot(snapshot: PluginStateSnapshotManager.StateSnapshot): Promise<PluginState.Snapshot | undefined> {
         this.clear();
         const entries = List<PluginStateSnapshotManager.Entry>().asMutable();
         for (const e of snapshot.entries) {
@@ -157,8 +157,21 @@ class PluginStateSnapshotManager extends StatefulPluginComponent<{
         return next;
     }
 
-    getRemoteSnapshot(options?: { name?: string, description?: string, playOnLoad?: boolean }): PluginStateSnapshotManager.RemoteSnapshot {
+    private syncCurrent(options?: { name?: string, description?: string, params?: PluginState.GetSnapshotParams }) {
+        const snapshot = this.plugin.state.getSnapshot(options?.params);
+        if (this.state.entries.size === 0 || !this.state.current) {
+            this.add(PluginStateSnapshotManager.Entry(snapshot, { name: options?.name, description: options?.description }));
+        } else {
+            this.replace(this.state.current, snapshot);
+        }
+    }
+
+    getStateSnapshot(options?: { name?: string, description?: string, playOnLoad?: boolean, params?: PluginState.GetSnapshotParams }): PluginStateSnapshotManager.StateSnapshot {
         // TODO: diffing and all that fancy stuff
+
+        // TODO: the options need to be handled better, particularky options.params
+        this.syncCurrent(options);
+
         return {
             timestamp: +new Date(),
             name: options && options.name,
@@ -173,7 +186,7 @@ class PluginStateSnapshotManager extends StatefulPluginComponent<{
     }
 
     async serialize(type: 'json' | 'zip' = 'json') {
-        const json = JSON.stringify(this.plugin.state.getSnapshot(), null, 2);
+        const json = JSON.stringify(this.getStateSnapshot(), null, 2);
 
         if (type === 'json') {
             return new Blob([json], {type : 'application/json;charset=utf-8'});
@@ -234,7 +247,7 @@ class PluginStateSnapshotManager extends StatefulPluginComponent<{
                 }
 
                 const snapshot = JSON.parse(stateData);
-                return this.plugin.state.setSnapshot(snapshot);
+                return this.setStateSnapshot(snapshot);
             }
         } catch (e) {
             this.plugin.log.error(`Reading state: ${e}`);
@@ -312,7 +325,7 @@ namespace PluginStateSnapshotManager {
         return { timestamp: +new Date(), snapshot, ...params };
     }
 
-    export interface RemoteSnapshot {
+    export interface StateSnapshot {
         timestamp: number,
         name?: string,
         description?: string,

+ 0 - 1
src/mol-plugin-state/manager/structure/focus.ts

@@ -124,7 +124,6 @@ export class StructureFocusManager extends StatefulPluginComponent<StructureFocu
     }
 
     setSnapshot(snapshot: StructureFocusSnapshot) {
-        console.log(snapshot);
         if (!snapshot.current) return;
 
         const { label, ref, bundle, category } = snapshot.current;

+ 20 - 22
src/mol-plugin-ui/state/snapshots.tsx

@@ -4,7 +4,7 @@
  * @author David Sehnal <david.sehnal@gmail.com>
  */
 
-import { ArrowDownward, ArrowUpward, CloudUpload, DeleteOutlined, SwapHoriz, SaveOutlined, GetApp } from '@material-ui/icons';
+import { ArrowDownward, ArrowUpward, CloudUpload, DeleteOutlined, GetApp, OpenInBrowser, SaveOutlined, SwapHoriz } from '@material-ui/icons';
 import { OrderedMap } from 'immutable';
 import * as React from 'react';
 import { PluginCommands } from '../../mol-plugin/commands';
@@ -16,6 +16,7 @@ import { ParamDefinition as PD } from '../../mol-util/param-definition';
 import { urlCombine } from '../../mol-util/url';
 import { PluginUIComponent, PurePluginUIComponent } from '../base';
 import { Button, IconButton, SectionHeader } from '../controls/common';
+import { Icon } from '../controls/icons';
 import { ParameterControls } from '../controls/parameters';
 
 export class StateSnapshots extends PluginUIComponent<{}> {
@@ -50,10 +51,14 @@ export class StateExportImportControls extends PluginUIComponent {
 
     render() {
         return <div className='msp-flex-row'>
-            <Button icon={GetApp} onClick={this.downloadToFileJson} title='Save state description'>Base</Button>
-            <Button icon={GetApp} onClick={this.downloadToFileZip} title='Save state including the data as zip.'>All</Button>
+            <Button icon={GetApp} onClick={this.downloadToFileJson} title='Save the state description. Input data are loaded using the provided sources. Does not work if local files are used as input.'>
+                Base
+            </Button>
+            <Button icon={GetApp} onClick={this.downloadToFileZip} title='Save the state including the input data.'>
+                Full
+            </Button>
             <div className='msp-btn msp-btn-block msp-btn-action msp-loader-msp-btn-file'>
-                {'Open'} <input onChange={this.open} type='file' multiple={false} accept='.json,.zip' />
+                <Icon svg={OpenInBrowser} inline /> Open <input onChange={this.open} type='file' multiple={false} accept='.json,.zip' />
             </div>
         </div>;
     }
@@ -152,20 +157,19 @@ class LocalStateSnapshotList extends PluginUIComponent<{}, {}> {
 
     render() {
         const current = this.plugin.managers.snapshot.state.current;
-        return <ul style={{ listStyle: 'none' }} className='msp-state-list'>
-            {this.plugin.managers.snapshot.state.entries.map(e => <li key={e!.snapshot.id}>
-                <Button data-id={e!.snapshot.id} onClick={this.apply}>
+        return <ul style={{ listStyle: 'none', marginTop: '1px' }} className='msp-state-list'>
+            {this.plugin.managers.snapshot.state.entries.map(e => <li key={e!.snapshot.id} className='msp-flex-row'>
+                <Button data-id={e!.snapshot.id} onClick={this.apply} className='msp-no-overflow'>
+                    {(console.log(e!.snapshot.durationInMs), false)}
                     <span style={{ fontWeight: e!.snapshot.id === current ? 'bold' : void 0 }}>
                         {e!.name || new Date(e!.timestamp).toLocaleString()}</span> <small>
                         {`${e!.snapshot.durationInMs ? formatTimespan(e!.snapshot.durationInMs, false) + `${e!.description ? ', ' : ''}` : ''}${e!.description ? e!.description : ''}`}
                     </small>
                 </Button>
-                <div>
-                    <IconButton svg={ArrowUpward} data-id={e!.snapshot.id} title='Move Up' onClick={this.moveUp} small={true} />
-                    <IconButton svg={ArrowDownward} data-id={e!.snapshot.id} title='Move Down' onClick={this.moveDown} small={true} />
-                    <IconButton svg={SwapHoriz} data-id={e!.snapshot.id} title='Replace' onClick={this.replace} small={true} />
-                    <IconButton svg={DeleteOutlined} data-id={e!.snapshot.id} title='Remove' onClick={this.remove} small={true} />
-                </div>
+                <IconButton svg={ArrowUpward} data-id={e!.snapshot.id} title='Move Up' onClick={this.moveUp} flex='20px' />
+                <IconButton svg={ArrowDownward} data-id={e!.snapshot.id} title='Move Down' onClick={this.moveDown} flex='20px' />
+                <IconButton svg={SwapHoriz} data-id={e!.snapshot.id} title='Replace' onClick={this.replace} flex='20px' />
+                <IconButton svg={DeleteOutlined} data-id={e!.snapshot.id} title='Remove' onClick={this.remove} flex='20px' />
             </li>)}
         </ul>;
     }
@@ -235,20 +239,14 @@ export class RemoteStateSnapshots extends PluginUIComponent<
         this.setState({ isBusy: true });
         this.plugin.config.set(PluginConfig.State.CurrentServer, this.state.params.options.serverUrl);
 
-        if (this.plugin.managers.snapshot.state.entries.size === 0) {
-            await PluginCommands.State.Snapshots.Add(this.plugin, {
-                name: this.state.params.name,
-                description: this.state.params.options.description,
-                params: this.plugin.managers.snapshot.currentGetSnapshotParams
-            });
-        }
-
         await PluginCommands.State.Snapshots.Upload(this.plugin, {
             name: this.state.params.name,
             description: this.state.params.options.description,
             playOnLoad: this.state.params.options.playOnLoad,
-            serverUrl: this.state.params.options.serverUrl
+            serverUrl: this.state.params.options.serverUrl,
+            params: this.plugin.managers.snapshot.currentGetSnapshotParams
         });
+
         this.setState({ isBusy: false });
         this.plugin.log.message('Snapshot uploaded.');
         this.refresh();

+ 3 - 3
src/mol-plugin-ui/viewport/screenshot.tsx

@@ -145,10 +145,10 @@ export class DownloadScreenshotControls extends PluginUIComponent<{ close: () =>
                 <Button icon={Launch} onClick={this.openTab} disabled={this.state.isDisabled}>Open in new Tab</Button>
             </div>
             <ParameterControls params={this.plugin.helpers.viewportScreenshot!.params} values={this.plugin.helpers.viewportScreenshot!.values} onChange={this.setProps} isDisabled={this.state.isDisabled} />
-            <ExpandGroup header='State Snapshot' accent>
+            <ExpandGroup header='State Snapshot'>
                 <StateExportImportControls />
-                <div className='msp-help-description' style={{ padding: '10px'}}>
-                    <Icon svg={Warning} /> This is an experimental feature and states stored today might not be openable in an upcoming version.
+                <div className='msp-help-text' style={{ padding: '10px'}}>
+                    <Icon svg={Warning} /> This is an experimental feature and stored states might not be openable in a future version.
                 </div>
             </ExpandGroup>
         </div>;

+ 3 - 3
src/mol-plugin/behavior/static/state.ts

@@ -163,19 +163,19 @@ export function Snapshots(ctx: PluginContext) {
         return ctx.state.setSnapshot(snapshot);
     });
 
-    PluginCommands.State.Snapshots.Upload.subscribe(ctx, ({ name, description, playOnLoad, serverUrl }) => {
+    PluginCommands.State.Snapshots.Upload.subscribe(ctx, ({ name, description, playOnLoad, serverUrl, params }) => {
         return fetch(urlCombine(serverUrl, `set?name=${encodeURIComponent(name || '')}&description=${encodeURIComponent(description || '')}`), {
             method: 'POST',
             mode: 'cors',
             referrer: 'no-referrer',
             headers: { 'Content-Type': 'application/json; charset=utf-8' },
-            body: JSON.stringify(ctx.managers.snapshot.getRemoteSnapshot({ name, description, playOnLoad }))
+            body: JSON.stringify(ctx.managers.snapshot.getStateSnapshot({ name, description, playOnLoad, params }))
         }) as any as Promise<void>;
     });
 
     PluginCommands.State.Snapshots.Fetch.subscribe(ctx, async ({ url }) => {
         const json = await ctx.runTask(ctx.fetch({ url, type: 'json' })); //  fetch(url, { referrer: 'no-referrer' });
-        await ctx.managers.snapshot.setRemoteSnapshot(json.data);
+        await ctx.managers.snapshot.setStateSnapshot(json.data);
     });
 
     PluginCommands.State.Snapshots.DownloadToFile.subscribe(ctx, async ({ name, type }) => {

+ 1 - 1
src/mol-plugin/commands.ts

@@ -34,7 +34,7 @@ export const PluginCommands = {
             Apply: PluginCommand<{ id: string }>(),
             Clear: PluginCommand<{}>(),
 
-            Upload: PluginCommand<{ name?: string, description?: string, playOnLoad?: boolean, serverUrl: string }>(),
+            Upload: PluginCommand<{ name?: string, description?: string, playOnLoad?: boolean, serverUrl: string, params?: PluginState.GetSnapshotParams }>(),
             Fetch: PluginCommand<{ url: string }>(),
 
             DownloadToFile: PluginCommand<{ name?: string, type: 'json' | 'zip' }>(),

+ 1 - 1
src/mol-plugin/state.ts

@@ -56,7 +56,7 @@ class PluginState {
             canvas3d: p.canvas3d ? { props: this.plugin.canvas3d?.props } : void 0,
             interactivity: p.interactivity ? { props: this.plugin.managers.interactivity.props } : void 0,
             structureFocus: this.plugin.managers.structure.focus.getSnapshot(),
-            durationInMs: params && params.durationInMs
+            durationInMs: p?.durationInMs
         };
     }