diff --git a/src/mol-plugin-state/transforms/data.ts b/src/mol-plugin-state/transforms/data.ts index 10f08680cd820a59ca5ec604af842405989fef06..33173bb6304b4b83ed93d1b3bc57a6f5a66ef043 100644 --- a/src/mol-plugin-state/transforms/data.ts +++ b/src/mol-plugin-state/transforms/data.ts @@ -18,6 +18,7 @@ import * as DSN6 from '../../mol-io/reader/dsn6/parser'; import * as PLY from '../../mol-io/reader/ply/parser'; import { parsePsf } from '../../mol-io/reader/psf/parser'; import { isTypedArray } from '../../mol-data/db/column-helpers'; +import { AssetManager } from '../../mol-plugin/util/asset-manager'; export { Download }; type Download = typeof Download @@ -149,10 +150,26 @@ const ReadFile = PluginStateTransform.BuiltIn({ plugin.log.error('No file(s) selected'); return StateObject.Null; } - const data = await readFromFile(p.file, p.isBinary ? 'binary' : 'string').runInContext(ctx); - return p.isBinary + + let file: File; + if (AssetManager.isItem(p.file)) { + if (!plugin.managers.asset.has(p.file.id)) { + throw new Error(`No asset found for '${p.file.name}'`); + } + file = plugin.managers.asset.get(p.file.id)!; + // will be added again below with new id + plugin.managers.asset.remove(p.file.id); + } else { + file = p.file; + } + + const data = await readFromFile(file, p.isBinary ? 'binary' : 'string').runInContext(ctx); + const o = p.isBinary ? new SO.Data.Binary(data as Uint8Array, { label: p.label ? p.label : p.file.name }) : new SO.Data.String(data as string, { label: p.label ? p.label : p.file.name }); + + plugin.managers.asset.set(o.id, file); + return o; }); }, update({ oldParams, newParams, b }) { diff --git a/src/mol-plugin/context.ts b/src/mol-plugin/context.ts index 9324decda152708a399d20988295d04eb24154d6..bb7021aba8ca91777aa30803c7901bc629110080 100644 --- a/src/mol-plugin/context.ts +++ b/src/mol-plugin/context.ts @@ -53,6 +53,7 @@ import { TaskManager } from './util/task-manager'; import { PluginToastManager } from './util/toast'; import { ViewportScreenshotHelper } from './util/viewport-screenshot'; import { PLUGIN_VERSION, PLUGIN_VERSION_DATE } from './version'; +import { AssetManager } from './util/asset-manager'; export class PluginContext { runTask = <T>(task: Task<T>) => this.tasks.run(task); @@ -150,7 +151,8 @@ export class PluginContext { interactivity: void 0 as any as InteractivityManager, camera: new CameraManager(this), lociLabels: void 0 as any as LociLabelManager, - toast: new PluginToastManager(this) + toast: new PluginToastManager(this), + asset: new AssetManager(this) } as const readonly customModelProperties = new CustomProperty.Registry<Model>(); diff --git a/src/mol-plugin/util/asset-manager.ts b/src/mol-plugin/util/asset-manager.ts new file mode 100644 index 0000000000000000000000000000000000000000..1e31895cba49b7a02391745b3abd8eaa6eaf7c2a --- /dev/null +++ b/src/mol-plugin/util/asset-manager.ts @@ -0,0 +1,73 @@ +/** + * Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author Alexander Rose <alexander.rose@weirdbyte.de> + */ + +import { UUID } from '../../mol-util'; +import { PluginContext } from '../context'; +import { iterableToArray } from '../../mol-data/util'; + +export { AssetManager }; + +class AssetManager { + private files = new Map<UUID, File>() + private ids = new Map<File, UUID>() + + get list() { + return iterableToArray(this.ids.entries()); + } + + set(id: UUID, file: File) { + this.files.set(id, file); + this.ids.set(file, id); + } + + remove(id: UUID) { + if (this.files.has(id)) { + const file = this.files.get(id)!; + this.files.delete(id); + this.ids.delete(file); + } + } + + has(id: UUID) { + return this.files.has(id); + } + + get(id: UUID) { + return this.files.get(id); + } + + /** For use with `JSON.stringify` */ + replacer = (key: string, value: any) => { + if (value instanceof File) { + const id = this.ids.get(value); + if (!id) { + // TODO throw? + console.warn(`No asset found for '${value.name}'`); + } + return id ? AssetManager.Item(id, value.name) : {}; + } else { + return value; + } + } + + constructor(public ctx: PluginContext) { + ctx.state.data.events.object.removed.subscribe(e => { + const id = e.obj?.id; + if (id) ctx.managers.asset.remove(id); + }); + } +} + +namespace AssetManager { + export type Item = { kind: 'asset-item', id: UUID, name: string }; + export function Item(id: UUID, name: string): Item { + return { kind: 'asset-item', id, name }; + } + + export function isItem(x?: any): x is Item { + return !!x && x?.kind === 'asset-item'; + } +} \ No newline at end of file