Scene save operations (in-place overwrite vs copy) (#126)

This commit is contained in:
Danny McGee
2024-04-09 15:39:32 -04:00
committed by GitHub
parent 5cd98b4a35
commit 03f90eee47
16 changed files with 418 additions and 105 deletions
+1 -21
View File
@@ -1,6 +1,5 @@
import { drag, on, provide } from "@storyteller/framework";
import { on, provide } from "@storyteller/framework";
import {
type SceneSavedEvent,
type SceneStateEvent,
SceneState,
StudioMode,
@@ -78,25 +77,6 @@ export class AppElement extends LitElement {
return this;
}
@on("scene-saved")
async onSceneSaved({ detail: scene }: SceneSavedEvent): Promise<void> {
try {
const file = new File([scene], "scene.scn.ron", { type: "text/ron" });
const token = await this.api.createScene(file);
this.dispatchEvent(new CustomEvent("scene-uploaded", {
detail: token,
bubbles: true,
composed: true,
}));
}
catch (err) {
console.error(err);
}
}
@on("scene-state")
onSceneStateChange({ detail: state }: SceneStateEvent): void {
console.log("scene state change:", SceneState[state]);
@@ -0,0 +1,143 @@
import { bind, inject, observe } from "@storyteller/framework";
import { DialogElement } from "@storyteller/studio-ui";
import { REMOTE_SCENE_MANAGER, type RemoteSceneManager } from "@storyteller/studio-web";
import { LitElement, TemplateResult, html, unsafeCSS } from "lit";
import { customElement, property, state } from "lit/decorators.js";
import { createRef, ref } from "lit/directives/ref.js";
import styles from "./file-menu.element.scss?inline";
@customElement("sts-file-menu")
export class FileMenuElement extends LitElement {
static override styles = unsafeCSS(styles);
@property({ attribute: false })
trigger = createRef<HTMLElement>();
@observe(["canSaveInPlace", "canSaveAsCopy"])
@inject(REMOTE_SCENE_MANAGER)
_sceneManager!: RemoteSceneManager;
@state() _saveAsCopyTitle = "";
_saveAsCopyFormRef = createRef<HTMLFormElement>();
_saveAsCopyDialogRef = createRef<DialogElement>();
_onSaveInPlace(): void {
this._sceneManager.saveInPlace().catch(console.error);
}
_onSaveAsCopy(): void {
const title = this._saveAsCopyTitle;
this._saveAsCopyDialogRef.value?.close();
this._saveAsCopyTitle = "";
this._sceneManager.saveAsCopy(title).catch(console.error);
}
get _saveAsCopyDialogTemplate(): TemplateResult {
return html`
<sts-dialog-header icon="floppy-disk">
Save Scene
</sts-dialog-header>
<form
${ref(this._saveAsCopyFormRef)}
action=""
@submit=${(event: SubmitEvent) => {
event.preventDefault();
event.stopImmediatePropagation();
this._onSaveAsCopy();
}}
>
<label>
Title
<sts-text-field
autofocus
name="title"
required
.value=${bind(this, "_saveAsCopyTitle")}
@keydown=${(event: KeyboardEvent) => {
if (event.key === "Enter" && this._saveAsCopyTitle)
this._saveAsCopyFormRef.value?.requestSubmit();
}}
></sts-text-field>
</label>
</form>
<sts-dialog-footer>
<sts-button
@click=${() => {
this._saveAsCopyDialogRef.value?.close();
this._saveAsCopyTitle = "";
}}
>
Cancel
</sts-button>
<sts-button
secondary
?disabled=${!this._saveAsCopyTitle}
@click=${() => {
this._saveAsCopyFormRef.value?.requestSubmit();
}}
>
Save
</sts-button>
</sts-dialog-footer>
`;
}
protected override render() {
return html`
<sts-menu
autoClose
.trigger=${this.trigger}
>
<sts-button
role="menuitem"
keybind="Ctrl+N"
disabled
@click=${() => {
// TODO
}}
>
New scene
</sts-button>
<sts-button
role="menuitem"
keybind="Ctrl+Shift+N"
disabled
@click=${() => {
// TODO
}}
>
New scene from template...
</sts-button>
<hr />
<sts-button
role="menuitem"
keybind="Ctrl+S"
?disabled=${!this._sceneManager.canSaveInPlace}
@click=${this._onSaveInPlace}
>
Save scene
</sts-button>
<sts-button
role="menuitem"
keybind="Ctrl+Shift+S"
?disabled=${!this._sceneManager.canSaveAsCopy}
@click=${() => {
this._saveAsCopyDialogRef.value?.open();
}}
>
Save scene as copy...
</sts-button>
</sts-menu>
<sts-dialog
${ref(this._saveAsCopyDialogRef)}
.template=${[this, this._saveAsCopyDialogTemplate] as const}
></sts-dialog>
`;
}
}
@@ -23,6 +23,7 @@ import "@storyteller/studio-ui/forms/radio";
import "@storyteller/studio-ui/forms/select";
import "@storyteller/studio-ui/toolbar";
import "./anim-timeline-controls.element";
import "./file-menu.element";
import "./studio-viewport.element.scss";
import { match } from "@storyteller/utility";
@@ -251,51 +252,7 @@ export class StudioViewportElement extends LitElement {
})}
</sts-studio>
<sts-menu
autoClose
.trigger=${this.#fileTriggerRef}
>
<sts-button
role="menuitem"
keybind="Ctrl+N"
@click=${() => {
// TODO
}}
>
New scene
</sts-button>
<sts-button
role="menuitem"
keybind="Ctrl+Shift+N"
@click=${() => {
// TODO
}}
>
New scene from template...
</sts-button>
<hr />
<sts-button
role="menuitem"
keybind="Ctrl+S"
@click=${() => {
// TODO: Should we show a spinner here while we're waiting?
// Toast message for success/failure?
// TODO: Need to disable this when the scene is not ours
studio.saveScene();
}}
>
Save scene
</sts-button>
<sts-button
role="menuitem"
keybind="Ctrl+Shift+S"
@click=${() => {
// TODO
}}
>
Save scene as copy...
</sts-button>
</sts-menu>
<sts-file-menu .trigger=${this.#fileTriggerRef}></sts-file-menu>
<sts-dialog
.trigger=${this.#mediaLibraryTriggerRef}