view src/main.ts @ 11:028ed39aa78f

more ts types; attempted multiplayer but the change events are managed wrong
author drewp@bigasterisk.com
date Sun, 28 Jan 2024 17:31:06 -0800
parents 9f427d8073c3
children 7cc004eafb82
line wrap: on
line source

import { LitElement, css, html } from "lit";
import { customElement, state } from "lit/decorators.js";

interface Light {
  name: string;
  address: string;
  requestingColor: string;
  requestingDeviceColor: object;
  emittingColor: string;
  online: boolean;
  latencyMs: number;
}

@customElement("lb-page")
export class LbPage extends LitElement {
  static styles = [
    css`
      :host {
        display: flex;
        flex-direction: column;
        height: 100vh;
      }
      table {
        border-collapse: collapse;
      }
      td,
      th {
        border: 1px solid #aaa;
        text-align: center;
      }
      .color {
        display: inline-block;
        width: 30px;
        height: 30px;
        border-radius: 50%;
        margin: 3px;
        vertical-align: middle;
      }
      .col-group-1 {
        background: #e0ecf4;
      }
      .col-group-2 {
        background: #e0f3db;
      }
      .col-group-3 {
        background: #fee8c8;
      }
    `,
  ];

  // bug https://github.com/lit/lit/issues/4305
  @((state as any)()) lights: Light[] = [];
  @((state as any)()) lightByName: Map<string, Light> = new Map();
  @((state as any)()) reportTime: Date = new Date(0);

  connectedCallback(): void {
    super.connectedCallback();
    const es = new EventSource("api/table");
    es.onmessage = (ev) => {
      const body = JSON.parse(ev.data);
      this.lights = body.lights.map((wrappedLight: any) => wrappedLight.light as Light);
      this.rebuildLightByName();
      this.reportTime = new Date(body.now * 1000);
    };
  }
  private rebuildLightByName() {
    this.lightByName = new Map();
    this.lights.forEach((light: any) => {
      this.lightByName.set(light.name, light);
    });
  }

  render() {
    return html`
      <h1>Light-bridge</h1>

      <table>
        <tr>
          <th class="col-group-1">light</th>
          <th class="col-group-1">address</th>
          <th class="col-group-2">requested color</th>
          <th class="col-group-2">requested device color</th>
          <th class="col-group-3">emitting color</th>
          <th class="col-group-3">online</th>
          <th class="col-group-3">latency</th>
        </tr>
        ${this.lights.map(
          (d: Light) => html`
            <tr>
              <td class="col-group-1">${d.name}</td>
              <td class="col-group-1"><code>${d.address}</code></td>
              <td class="col-group-2">
                <code>${d.requestingColor}</code>
                <input type="color" @input=${this.onRequestingColor.bind(this, d.name)} .value="${d.requestingColor}" />
              </td>
              <td class="col-group-2"><code>${JSON.stringify(d.requestingDeviceColor)}</code></td>
              <td class="col-group-3">${d.emittingColor} <span class="color" style="background: ${d.emittingColor}"></span></td>
              <td class="col-group-3">${d.online ? "✔" : ""}</td>
              <td class="col-group-3">${d.latencyMs} ms</td>
            </tr>
          `
        )}
      </table>
      <p>Updated ${this.reportTime.toLocaleString("sv")}</p>
      <p>
        <a href="metrics">metrics</a> |
        <a href="api/graph">graph</a>
      </p>
      <bigast-loginbar></bigast-loginbar>
    `;
  }
  async onRequestingColor(lightName: string, ev: InputEvent) {
    const currentRequest = this.lightByName.get(lightName)!.requestingColor;
    const value = (ev.target as HTMLInputElement).value;
    console.log("LbPage ~ onRequestingColor ~ value === currentRequest:", value, currentRequest);
    if (value === currentRequest) {
      return;
    }
    const url = new URL("api/output", location as any);
    url.searchParams.append("light", lightName);
    fetch(url, { method: "PUT", body: value }); // todo: only run one fetch at a time, per light

    // sometime after this, the SSE table will send us back the change we made (probably)
  }
}