Changeset - a1f6f3139995
[Not reviewed]
default
0 2 0
drewp@bigasterisk.com - 20 months ago 2023-05-27 06:07:40
drewp@bigasterisk.com
WIP trying to clarify how /live page works
2 files changed with 73 insertions and 41 deletions:
0 comments (0 inline, 0 general)
light9/live/GraphToControls.ts
Show inline comments
 
import debug from "debug";
 
import { NamedNode } from "n3";
 
import { SyncedGraph } from "../web/SyncedGraph";
 
import { ControlValue, Effect } from "./Effect";
 
const log = debug("g2c");
 

	
 
// Callback for GraphToControls to set a ControlValue on a
 
// Light9LiveControl (widget for a Device+Attr)
 
type NewValueCb = (newValue: ControlValue | null) => void;
 

	
 
// More efficient bridge between liveControl widgets and graph edits (inside Effect),
 
// as opposed to letting each widget scan the graph and push lots of
 
// tiny patches to it.
 
//
 
// When you create a Light9LiveControl, it registers with GraphToControls (and does not
 
// watch value updates from the graph).
 
export class GraphToControls {
 
  // rename to PageControls?
 
  effect: Effect | null = null; // this uri should sync to the editchoice
 
  registeredWidgets: Map<NamedNode, Map<NamedNode, NewValueCb>> = new Map();
 
  constructor(public graph: SyncedGraph) {}
 
  private effect: Effect | null = null; // this uri should sync to the editchoice
 

	
 
  // This will fill with every device+attr in the show. Currently there's no unregister to forget a device or attr.
 
  private registeredWidgets: Map<
 
    NamedNode /*Device*/,
 
    Map<
 
      NamedNode /*DeviceAttr*/, //
 
      NewValueCb
 
    >
 
  > = new Map();
 
  constructor(public graph: SyncedGraph) {
 
    
 
  }
 

	
 
  debugDump() {
 
    log("dump: effect", this.effect);
 
    log("registered widgets");
 
    for (let e of this.registeredWidgets.entries()) {
 
      log(" rw:", e[0], e[1]);
 
    }
 
  }
 

	
 
  setEffect(effect: NamedNode | null) {
 
    log(`setEffect ${effect?.value}`);
 
    this.effect = effect ? new Effect(this.graph, effect, this.onValuesChanged.bind(this)) : null;
 
  }
 

	
 
  newEffect(): NamedNode {
 
    // wrong- this should be our editor's scratch effect, promoted to a
 
    // real one when you name it.
 
    const uri = this.graph.nextNumberedResource(this.graph.Uri("http://light9.bigasterisk.com/effect/effect"));
 

	
 
    this.effect = new Effect(this.graph, uri, this.onValuesChanged.bind(this));
 
    log("add new eff");
 
    this.effect.addNewEffectToGraph();
 
    return this.effect.uri;
 
  }
 

	
 
  onValuesChanged() {
 
  emptyEffect() {
 
    throw new Error("not implemented");
 
  }
 

	
 
  private onValuesChanged() {
 
    log(`i learned values changed for ${this.effect?.uri.value} `);
 
    this.registeredWidgets.forEach((d1: Map<NamedNode, NewValueCb>, device: NamedNode) => {
 
      d1.forEach((cb: NewValueCb, deviceAttr: NamedNode) => {
 
        const v = this.effect ? this.effect.currentValue(device, deviceAttr) : null;
 
        cb(v);
 
      });
 
    });
 
  }
 

	
 
  register(device: NamedNode, deviceAttr: NamedNode, graphValueChanged: NewValueCb) {
 
    // log(`control for ${device.value}-${deviceAttr.value} registring with g2c`);
 
    let d1 = this.registeredWidgets.get(device);
light9/live/Light9LiveControls.ts
Show inline comments
 
@@ -34,74 +34,79 @@ export class Light9LiveControls extends 
 
      light9-live-device-control > div {
 
        break-inside: avoid-column;
 
      }
 
      light9-live-device-control {
 
      }
 
    `,
 
  ];
 

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

	
 
      <h1>device control</h1>
 
      <h1>effect deviceattrs</h1>
 

	
 
      <div id="save">
 
        <div>
 
          <button @click=${this.newEffect}>New effect</button>
 
          <edit-choice .uri=${this.effectChoice}></edit-choice>
 
          <edit-choice .uri=${this.effectChoice} @edited=${this.onEffectChoice2}></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.effectChoice} .graphToControls=${this.graphToControls}></light9-device-control>
 
          `
 
        )}
 
      </div>
 
    `;
 
  }
 

	
 
  devices: Array<NamedNode> = [];
 
  // uri of the effect being edited, or null. This is the
 
  // master value; GraphToControls follows.
 
  @property() effectChoice: NamedNode | null = null;
 
  graphToControls!: GraphToControls;
 
  okToWriteUrl: boolean = false;
 

	
 
  constructor() {
 
    super();
 

	
 
    getTopGraph().then((g) => {
 
      this.graph = g;
 
      this.graph.runHandler(this.findDevices.bind(this), "findDevices");
 
      this.graphToControls = new GraphToControls(this.graph);
 
      // this.graph.runHandler(this.xupdate.bind(this), "Light9LiveControls update");
 
      // this.graph.runHandler(this.update.bind(this), "Light9LiveControls update");
 
      this.setEffectFromUrl();
 
    });
 
  }
 

	
 
  onEffectChoice2(ev: CustomEvent) {
 
    this.effectChoice = ev.detail.newValue as NamedNode;
 
  }
 
  updated(changedProperties: PropertyValues) {
 
    if (changedProperties.has("effectChoice")) {
 
      log(`effectChoice to ${this.effectChoice?.value}`);
 
      this.onEffectChoice();
 
    }
 
  }
 

	
 
  // Note that this doesn't fetch setting values, so it only should get rerun
 
  // upon (rarer) changes to the devices etc.
 
  findDevices(patch?: Patch) {
 
    const U = this.graph.U();
 
    if (patch && !patchContainsPreds(patch, [U("rdf:type")])) {
 
      return;
 
    }
 
    // 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) => {
 
        log(`found dev ${dev.value}`);
 
        this.devices.push(dev as NamedNode);
 
      });
 
    });
 
    this.requestUpdate();
 
  }
 
@@ -149,47 +154,47 @@ export class Light9LiveControls extends 
 
      } else {
 
        throw new Error("graphToControls not set");
 
      }
 
    }
 
    this.writeToUrl(this.effectChoice);
 
  }
 

	
 
  clearAll() {
 
    // clears the effect!
 
    return this.graphToControls.emptyEffect();
 
  }
 

	
 
  configureFromGraph() {
 
    const U = (x: string) => this.graph.Uri(x);
 
  // configureFromGraph() {
 
  //   const U = (x: string) => this.graph.Uri(x);
 

	
 
    const newDevs: NamedNode[] = [];
 
    for (let dc of Array.from(this.graph.sortedUris(this.graph.subjects(U("rdf:type"), U(":DeviceClass"))))) {
 
      for (let dev of Array.from(this.graph.sortedUris(this.graph.subjects(U("rdf:type"), dc)))) {
 
        if (this.graph.contains(dev, U(":hideInLiveUi"), null)) {
 
          continue;
 
        }
 
        if (newDevs.length == 0) newDevs.push(dev);
 
      }
 
    }
 
    log("is this called?");
 
    log(`controls update now has ${newDevs.length} devices`);
 
    this.devices = newDevs;
 
    this.requestUpdate();
 
  //   const newDevs: NamedNode[] = [];
 
  //   for (let dc of Array.from(this.graph.sortedUris(this.graph.subjects(U("rdf:type"), U(":DeviceClass"))))) {
 
  //     for (let dev of Array.from(this.graph.sortedUris(this.graph.subjects(U("rdf:type"), dc)))) {
 
  //       if (this.graph.contains(dev, U(":hideInLiveUi"), null)) {
 
  //         continue;
 
  //       }
 
  //       if (newDevs.length == 0) newDevs.push(dev);
 
  //     }
 
  //   }
 
  //   log("is this called?");
 
  //   log(`controls update now has ${newDevs.length} devices`);
 
  //   this.devices = newDevs;
 
  //   this.requestUpdate();
 

	
 
    return;
 
  //   return;
 

	
 
    // Tried css columns- big slowdown from relayout as I'm scrolling.
 
    // Tried isotope- seems to only scroll to the right.
 
    // Tried columnize- fails in jquery maybe from weird elements.
 
  //   // Tried css columns- big slowdown from relayout as I'm scrolling.
 
  //   // Tried isotope- seems to only scroll to the right.
 
  //   // Tried columnize- fails in jquery maybe from weird elements.
 

	
 
    // not sure how to get this run after the children are created
 
    return setTimeout(
 
      () =>
 
        $("#deviceControls").isotope({
 
          // fitColumns would be nice, but it doesn't scroll vertically
 
          layoutMode: "masonry",
 
          containerStyle: null,
 
        }),
 
      2000
 
    );
 
  //   // not sure how to get this run after the children are created
 
  //   return setTimeout(
 
  //     () =>
 
  //       $("#deviceControls").isotope({
 
  //         // fitColumns would be nice, but it doesn't scroll vertically
 
  //         layoutMode: "masonry",
 
  //         containerStyle: null,
 
  //       }),
 
  //     2000
 
  //   );
 
  // }
 
  }
 
}
0 comments (0 inline, 0 general)