Files @ 69ca2b2fc133
Branch filter:

Location: light9/web/live/Light9DeviceSettings.ts

drewp@bigasterisk.com
overcomplicated attempt at persisting the pane layout in the rdf graph

this was hard because we have to somehow wait for the graph to load before config'ing the panes
import debug from "debug";
import { css, html, LitElement, PropertyValues } from "lit";
import { customElement, property } from "lit/decorators.js";
import { NamedNode } from "n3";
import { sortBy, uniq } from "underscore";
import { Patch } from "../patch";
import { getTopGraph } from "../RdfdbSyncedGraph";
import { SyncedGraph } from "../SyncedGraph";
import { Effect, newEffect } from "./Effect";
export { EditChoice } from "../EditChoice";
export { Light9DeviceControl as Light9LiveDeviceControl } from "./Light9DeviceControl";
const log = debug("settings");

@customElement("light9-device-settings")
export class Light9DeviceSettings extends LitElement {
  graph!: SyncedGraph;

  static styles = [
    css`
      :host {
        display: flex;
        flex-direction: column;
      }
      #preview {
        width: 100%;
      }
      #deviceControls {
        flex-grow: 1;
        position: relative;
        width: 100%;
        overflow-y: auto;
      }

      light9-device-control > div {
        break-inside: avoid-column;
      }
      light9-device-control {
        vertical-align: top;
      }
    `,
  ];

  render() {
    return html`
      <rdfdb-synced-graph></rdfdb-synced-graph>

      <h1>effect DeviceSettings</h1>

      <div id="save">
        <div>
          <button @click=${this.newEffect}>New effect</button>
          <edit-choice .uri=${this.currentEffect ? this.currentEffect.uri : null} @edited=${this.onEffectChoice2} rename></edit-choice>
          <button @click=${this.clearAll}>clear settings in this effect</button>
        </div>
      </div>

      <div id="deviceControls">
        ${this.devices.map(
          (device: NamedNode) => html`
            <light9-device-control .uri=${device} .effect=${this.currentEffect}> .graphToControls={this.graphToControls} </light9-device-control>
          `
        )}
      </div>
    `;
  }

  devices: Array<NamedNode> = [];
  @property() currentEffect: Effect | null = null;
  okToWriteUrl: boolean = false;

  constructor() {
    super();

    getTopGraph().then((g) => {
      this.graph = g;
      this.graph.runHandler(this.compile.bind(this), "findDevices");
      this.setEffectFromUrl();
    });
  }

  onEffectChoice2(ev: CustomEvent) {
    const uri = ev.detail.newValue as NamedNode;
    this.setCurrentEffect(uri);
  }
  setCurrentEffect(uri: NamedNode) {
    if (uri === null) {
      this.currentEffect = null;
      // todo: wipe the UI settings
    } else {
      this.currentEffect = new Effect(this.graph, uri);
    }
  }

  updated(changedProperties: PropertyValues<this>) {
    log("ctls udpated", changedProperties);
    if (changedProperties.has("currentEffect")) {
      log(`effectChoice to ${this.currentEffect?.uri?.value}`);
      this.writeToUrl(this.currentEffect?.uri);
    }
    // this.graphToControls?.debugDump();
  }

  // Note that this doesn't fetch setting values, so it only should get rerun
  // upon (rarer) changes to the devices etc. todo: make that be true
  private compile(patch?: Patch) {
    const U = this.graph.U();
    // if (patch && !patchContainsPreds(patch, [U("rdf:type")])) {
    //   return;
    // }

    this.devices = [];
    let classes = this.graph.subjects(U("rdf:type"), U(":DeviceClass"));
    log(`found ${classes.length} device classes`);
    uniq(sortBy(classes, "value"), true).forEach((dc) => {
      sortBy(this.graph.subjects(U("rdf:type"), dc), "value").forEach((dev) => {
        this.devices.push(dev as NamedNode);
      });
    });
    this.requestUpdate();
  }

  setEffectFromUrl() {
    // not a continuous bidi link between url and effect; it only reads
    // the url when the page loads.
    const effect = new URL(window.location.href).searchParams.get("effect");
    if (effect != null) {
      this.currentEffect = new Effect(this.graph, this.graph.Uri(effect));
    }
    this.okToWriteUrl = true;
  }

  writeToUrl(effect: NamedNode | undefined) {
    const effectStr = effect ? this.graph.shorten(effect) : "";
    if (!this.okToWriteUrl) {
      return;
    }
    const u = new URL(window.location.href);
    if ((u.searchParams.get("effect") || "") === effectStr) {
      return;
    }
    u.searchParams.set("effect", effectStr); // this escapes : and / and i wish it didn't
    window.history.replaceState({}, "", u.href);
    log("wrote new url", u.href);
  }

  newEffect() {
    this.setCurrentEffect(newEffect(this.graph));
  }

  clearAll() {
    this.currentEffect?.clearAllSettings()
  }
}