From 3c27450e8215382d9038a4c14724d07a6606ef69 Mon Sep 17 00:00:00 2001 From: Alexander Rose <alexander.rose@weirdbyte.de> Date: Wed, 15 Apr 2020 18:29:43 -0700 Subject: [PATCH] zipped snapshot that include files from asset-manager --- src/mol-plugin-ui/state/snapshots.tsx | 15 ++++--- src/mol-plugin/behavior/static/state.ts | 56 +++++++++++++++++++++---- src/mol-plugin/commands.ts | 2 +- 3 files changed, 59 insertions(+), 14 deletions(-) diff --git a/src/mol-plugin-ui/state/snapshots.tsx b/src/mol-plugin-ui/state/snapshots.tsx index 21ec17014..b7640fbb9 100644 --- a/src/mol-plugin-ui/state/snapshots.tsx +++ b/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 } from '@material-ui/icons'; +import { ArrowDownward, ArrowUpward, CloudUpload, DeleteOutlined, SwapHoriz, SaveOutlined, GetApp } from '@material-ui/icons'; import { OrderedMap } from 'immutable'; import * as React from 'react'; import { PluginCommands } from '../../mol-plugin/commands'; @@ -19,8 +19,12 @@ import { Button, IconButton, SectionHeader } from '../controls/common'; import { ParameterControls } from '../controls/parameters'; export class StateSnapshots extends PluginUIComponent<{ }> { - downloadToFile = () => { - PluginCommands.State.Snapshots.DownloadToFile(this.plugin, { }); + downloadToFileJson = () => { + PluginCommands.State.Snapshots.DownloadToFile(this.plugin, { type: 'json' }); + } + + downloadToFileZip = () => { + PluginCommands.State.Snapshots.DownloadToFile(this.plugin, { type: 'zip' }); } open = (e: React.ChangeEvent<HTMLInputElement>) => { @@ -37,9 +41,10 @@ export class StateSnapshots extends PluginUIComponent<{ }> { {this.plugin.spec.components?.remoteState !== 'none' && <RemoteStateSnapshots />} <div className='msp-flex-row' style={{ marginTop: '10px' }}> - <Button onClick={this.downloadToFile}>Download JSON</Button> + <Button icon={GetApp} onClick={this.downloadToFileJson}>JSON</Button> + <Button icon={GetApp} onClick={this.downloadToFileZip}>ZIP</Button> <div className='msp-btn msp-btn-block msp-btn-action msp-loader-msp-btn-file'> - {'Open JSON'} <input onChange={this.open} type='file' multiple={false} accept='.json' /> + {'Open'} <input onChange={this.open} type='file' multiple={false} accept='.json,.zip' /> </div> </div> </div>; diff --git a/src/mol-plugin/behavior/static/state.ts b/src/mol-plugin/behavior/static/state.ts index 744dcde44..e0fa24b31 100644 --- a/src/mol-plugin/behavior/static/state.ts +++ b/src/mol-plugin/behavior/static/state.ts @@ -16,6 +16,10 @@ import { download } from '../../../mol-util/download'; import { Structure } from '../../../mol-model/structure'; import { urlCombine } from '../../../mol-util/url'; import { PluginConfig } from '../../config'; +import { zip } from '../../../mol-util/zip/zip'; +import { utf8Write, utf8ByteCount } from '../../../mol-io/common/utf8'; +import { objectForEach } from '../../../mol-util/object'; +import { UUID } from '../../../mol-util'; export function registerDefault(ctx: PluginContext) { SyncBehaviors(ctx); @@ -179,19 +183,55 @@ export function Snapshots(ctx: PluginContext) { await ctx.state.snapshots.setRemoteSnapshot(json.data); }); - PluginCommands.State.Snapshots.DownloadToFile.subscribe(ctx, ({ name }) => { - const json = JSON.stringify(ctx.state.getSnapshot(), null, 2); - const blob = new Blob([json], {type : 'application/json;charset=utf-8'}); - download(blob, `mol-star_state_${(name || getFormattedTime())}.json`); + PluginCommands.State.Snapshots.DownloadToFile.subscribe(ctx, async ({ name, type }) => { + const json = JSON.stringify(ctx.state.getSnapshot(), ctx.managers.asset.replacer, 2); + name = `mol-star_state_${(name || getFormattedTime())}`; + + if (type === 'json') { + const blob = new Blob([json], {type : 'application/json;charset=utf-8'}); + download(blob, `${name}.json`); + } else { + const state = new Uint8Array(utf8ByteCount(json)); + utf8Write(state, 0, json); + + const zipDataObj: { [k: string]: Uint8Array } = { + 'state.json': state + }; + for (const [file, id] of ctx.managers.asset.list) { + + zipDataObj[`${id}/${file.name}`] = new Uint8Array(await file.arrayBuffer()); + } + const zipFile = zip(zipDataObj); + + const blob = new Blob([zipFile], {type : 'application/zip'}); + download(blob, `${name}.zip`); + } }); PluginCommands.State.Snapshots.OpenFile.subscribe(ctx, async ({ file }) => { try { - const data = await readFromFile(file, 'string').run(); - const snapshot = JSON.parse(data as string); - return ctx.state.setSnapshot(snapshot); + if (file.name.toLowerCase().endsWith('json')) { + const data = await readFromFile(file, 'string').run(); + const snapshot = JSON.parse(data); + return ctx.state.setSnapshot(snapshot); + } else { + const data = await readFromFile(file, 'zip').run(); + objectForEach(data, (v, k) => { + if (k === 'state.json') return; + + const slash = k.indexOf('/'); + const id = k.substring(0, slash) as UUID; + const name = k.substring(slash + 1); + const file = new File([v], name); + ctx.managers.asset.set(id, file); + }); + const stateFile = new File([data['state.json']], 'state.json'); + const stateData = await readFromFile(stateFile, 'string').run(); + const snapshot = JSON.parse(stateData); + return ctx.state.setSnapshot(snapshot); + } } catch (e) { - ctx.log.error(`Reading JSON state: ${e}`); + ctx.log.error(`Reading state: ${e}`); } }); } \ No newline at end of file diff --git a/src/mol-plugin/commands.ts b/src/mol-plugin/commands.ts index 9cae3ed02..fe37f4b4e 100644 --- a/src/mol-plugin/commands.ts +++ b/src/mol-plugin/commands.ts @@ -37,7 +37,7 @@ export const PluginCommands = { Upload: PluginCommand<{ name?: string, description?: string, playOnLoad?: boolean, serverUrl: string }>(), Fetch: PluginCommand<{ url: string }>(), - DownloadToFile: PluginCommand<{ name?: string }>(), + DownloadToFile: PluginCommand<{ name?: string, type: 'json' | 'zip' }>(), OpenFile: PluginCommand<{ file: File }>(), } }, -- GitLab