diff --git a/studio-frontend/src/app/app.element.ts b/studio-frontend/src/app/app.element.ts index 915f8d6..737d2eb 100644 --- a/studio-frontend/src/app/app.element.ts +++ b/studio-frontend/src/app/app.element.ts @@ -8,17 +8,17 @@ import { } from "@storyteller/studio"; import { ButtonElement } from "@storyteller/studio-ui/button"; import { - MediaFileType, SceneHierarchyElement, StorytellerApi, StudioURLParams, } from "@storyteller/studio-web"; -import { assert, match } from "@storyteller/utility"; +import { match } from "@storyteller/utility"; import { LitElement, html, nothing } from "lit"; import { customElement, state } from "lit/decorators.js"; import { classMap } from "lit/directives/class-map.js"; import { createRef, ref } from "lit/directives/ref.js"; import { styleMap } from "lit/directives/style-map.js"; +import { MediaLibraryElement } from "./media-library.element"; import "@storyteller/studio-ui/button"; import "@storyteller/studio-ui/icon"; @@ -37,6 +37,7 @@ import "./anim-editor.element"; import "./help.element"; import "./blocking.element"; +import "./media-library.element"; import "./app.element.scss"; @@ -58,6 +59,7 @@ export class AppElement extends LitElement { #enableAnimTimeline = false; // TODO #addToSceneButtonRef = createRef(); + #mediaLibraryRef = createRef(); #inspectorRef = createRef(); #hierarchyRef = createRef(); @@ -67,37 +69,6 @@ export class AppElement extends LitElement { return this; } - // TODO - override async connectedCallback(): Promise { - super.connectedCallback(); - - try { - const session = await this.api.session(); - console.log("session:", session); - - if (!session.logged_in) - return; // TODO: Prompt for login? - - assert(session.user?.username != null); - - const mediaFilesResult = await this.api.listMediaFiles({ - username: session.user.username, - filterMediaType: [ - MediaFileType.Bvh, - MediaFileType.Fbx, - MediaFileType.Glb, - MediaFileType.Gltf, - MediaFileType.Mocap, - MediaFileType.ScnRon, - ], - }); - console.log(mediaFilesResult); - } - catch (err) { - console.error(err); - } - } - @on("scene-saved") async onSceneSaved({ detail: scene }: SceneSavedEvent): Promise { try { @@ -220,7 +191,14 @@ export class AppElement extends LitElement { ${this.#params.mode === StudioMode.Editor ? html` - + { + this.#mediaLibraryRef.value?.open(); + }} + > + + ${this.showInspector ? html` diff --git a/studio-frontend/src/app/media-library.element.scss b/studio-frontend/src/app/media-library.element.scss new file mode 100644 index 0000000..cd71d90 --- /dev/null +++ b/studio-frontend/src/app/media-library.element.scss @@ -0,0 +1,14 @@ +:host { + display: block; + min-width: 512px; + min-height: 256px; + max-width: 80%; + max-height: 80%; + padding: 1rem; + overflow: auto; + + background: #0006; + color: #FFFC; + backdrop-filter: blur(16px); + border-radius: 0.5rem; +} diff --git a/studio-frontend/src/app/media-library.element.ts b/studio-frontend/src/app/media-library.element.ts new file mode 100644 index 0000000..8fc5bea --- /dev/null +++ b/studio-frontend/src/app/media-library.element.ts @@ -0,0 +1,131 @@ +import { inject, on } from "@storyteller/framework"; +import { + OVERLAY_PROVIDER, + type OverlayHandle, + type OverlayProvider, +} from "@storyteller/studio-ui/overlay-provider"; +import { + type MediaFile, + MediaFileType, + StorytellerApi, +} from "@storyteller/studio-web"; +import { LitElement, unsafeCSS, type PropertyValues, html } from "lit"; +import { customElement, property, state } from "lit/decorators.js"; +import { createRef, ref } from "lit/directives/ref.js"; +import { until } from "lit/directives/until.js"; + +import dialogStyles from "./media-library.element.scss?inline"; + +@customElement("sts-media-library") +export class MediaLibraryElement extends LitElement { + @property({ attribute: false }) + trigger = createRef(); + + @inject(OVERLAY_PROVIDER) + overlay!: OverlayProvider; + + #overlayHandle?: WeakRef; + #dialogRef = createRef(); + + open(): void { + this.#overlayHandle = this.overlay.attach(this, html` + this.close()} + > + `, { + containerStyles: { + display: "flex", + alignItems: "center", + justifyContent: "center", + }, + }); + } + + close(): void { + if (this.#overlayHandle) + this.overlay.detach(this.#overlayHandle); + } + + protected override updated(changes: PropertyValues): void { + if (changes.has("trigger")) { + const prevTrigger = changes.get("trigger"); + if (prevTrigger?.value) { + prevTrigger.value.removeEventListener("click", this.onTriggerClick); + } + + const trigger = this.trigger.value; + if (!trigger) return; + + trigger.addEventListener("click", this.onTriggerClick); + } + + super.updated(changes); + } + + override disconnectedCallback(): void { + this.trigger.value?.removeEventListener("click", this.onTriggerClick); + super.disconnectedCallback(); + } + + onTriggerClick = (): void => { + this.open(); + } +} + +@customElement("sts-media-library-dialog") +export class MediaLibraryDialogElement extends LitElement { + static override styles = unsafeCSS(dialogStyles); + + @property({ reflect: true }) + override role = "dialog"; + + @state() + mediaFiles?: Promise; + + @inject(StorytellerApi) + _api!: StorytellerApi; + + override async connectedCallback(): Promise { + this.mediaFiles = this._api + .session() + .then(session => { + if (!session.logged_in || !session.user?.username) + return Promise.reject(new Error("Not logged in")); + + return this._api.listMediaFiles({ + username: session.user.username, + filterMediaType: [ + MediaFileType.Bvh, + MediaFileType.Glb, + MediaFileType.Gltf, + MediaFileType.ScnRon, + ], + }); + }) + .then(result => Promise.all( + result.results.map(({ token }) => this._api.mediaFile(token)) + )) + .catch(err => { + console.error(err); + return []; + }); + + super.connectedCallback(); + } + + @on("window:click") + onWindowClick(event: PointerEvent): void { + if (!event.composedPath().includes(this)) + this.dispatchEvent(new CustomEvent("close")); + } + + protected override render = () => html` + ${until( + this.mediaFiles?.then(files => html` +
${JSON.stringify(files, null, "  ")}
+ `), + html`Loading...` + )} + `; +} diff --git a/studio-web/src/lib/add-to-scene-menu.element.ts b/studio-web/src/lib/add-to-scene-menu.element.ts index a8b0427..54684ad 100644 --- a/studio-web/src/lib/add-to-scene-menu.element.ts +++ b/studio-web/src/lib/add-to-scene-menu.element.ts @@ -60,6 +60,9 @@ export class AddToSceneMenuElement extends LitElement { { + this.dispatchEvent(new CustomEvent("open-media-library")); + }} > Media library...