Changeset - 197a4ec877a7
[Not reviewed]
default
0 2 0
drewp@bigasterisk.com - 3 years ago 2022-06-06 01:15:17
drewp@bigasterisk.com
acso relayout so only the song list scrolls
2 files changed with 258 insertions and 108 deletions:
0 comments (0 inline, 0 general)
light9/ascoltami/Light9AscoltamiUi.ts
Show inline comments
 
@@ -6,13 +6,12 @@ import { NamedNode } from "n3";
 
import Sylvester from "sylvester";
 
import { Zoom } from "../web/light9-timeline-audio";
 
import { PlainViewState } from "../web/Light9CursorCanvas";
 
import { getTopGraph } from "../web/RdfdbSyncedGraph";
 
import { SyncedGraph } from "../web/SyncedGraph";
 
import { TimingUpdate } from "./main";
 
import { ResourceDisplay } from '../web/ResourceDisplay';
 
export { Light9TimelineAudio } from "../web/light9-timeline-audio";
 
export { Light9CursorCanvas } from "../web/Light9CursorCanvas";
 
export { RdfdbSyncedGraph } from "../web/RdfdbSyncedGraph";
 
export { ResourceDisplay } from "../web/ResourceDisplay";
 
const $V = Sylvester.Vector.create;
 

	
 
@@ -34,19 +33,23 @@ export class Light9AscoltamiUi extends L
 
  graph!: SyncedGraph;
 
  times!: { intro: number; post: number };
 
  @property() nextText: string = "";
 
  @property() isPlaying: boolean = false;
 
  @property() show: NamedNode | null = null;
 
  @property() song: NamedNode | null = null;
 
  @property() requestedSong: NamedNode | null = null;
 
  @property() selectedSong: NamedNode | null = null;
 
  @property() currentDuration: number = 0;
 
  @property() zoom: Zoom;
 
  @property() overviewZoom: Zoom;
 
  @property() viewState: PlainViewState | null = null;
 
  static styles = [
 
    css`
 
      :host {
 
        display: flex;
 
        flex-direction: column;
 
      }
 
      .timeRow {
 
        margin: 14px;
 
        position: relative;
 
      }
 
      #overview {
 
        height: 60px;
 
@@ -59,71 +62,180 @@ export class Light9AscoltamiUi extends L
 
        position: absolute;
 
        left: 0;
 
        top: 0;
 
        width: 100%;
 
        height: 100%;
 
      }
 
      #grow {
 

	
 
        flex: 1 1 auto;
 
        display: flex;
 

	
 
      }
 
      #grow > span {
 
        display: flex;
 
        position: relative;
 
        width:50%;
 
      }
 
      #playSelected {
 
        height:100px;
 
      }
 
      #songList {
 
        overflow-y: scroll;
 
        position: absolute;
 
        left:0;top:0;right:0;bottom:0;
 
      }
 
      #songList .row {
 
        width: 60%;
 
        min-height: 40px;
 
        text-align: left;
 
        position: relative;
 
      }
 
      #songList .row:nth-child(even) {background: #333;}
 
      #songList .row:nth-child(odd) {background: #444;}
 
      #songList button {
 
        min-height: 40px;
 
        margin-bottom: 10px;
 
      }
 
      #songList .row.playing {
 
        box-shadow: 0 0 30px red;
 
        background-color: #de5050;
 
      }
 
    `,
 
  ];
 
  render() {
 
    return html`<rdfdb-synced-graph></rdfdb-synced-graph>
 

	
 
<link rel="stylesheet" href="./style.css" />
 

	
 
<!-- <h1>ascoltami <a href="metrics">[metrics]</a></h1> -->
 
<!-- <div>
 
  songlist:
 
  ${this.songList.map((song) => html`
 
  <div>song: <resource-display .uri=${song}></resource-display>
 
  </div>
 
  `)}
 
</div> -->
 
<div class="timeRow">
 
  <div id="timeSlider"></div>
 
  <light9-timeline-audio id="overview" .show=${this.show} .song=${this.song} .zoom=${this.overviewZoom}>
 
  </light9-timeline-audio>
 
  <light9-timeline-audio id="zoomed" .show=${this.show} .song=${this.song} .zoom=${this.zoom}></light9-timeline-audio>
 
  <light9-cursor-canvas id="cursor" .viewState=${this.viewState}></light9-cursor-canvas>
 
</div>
 
      <link rel="stylesheet" href="./style.css" />
 

	
 
<div class="commands">
 
  <button id="cmd-stop" @click=${this.onCmdStop} class="playMode ${classMap({ active: !this.isPlaying })}">
 
    <strong>Stop</strong>
 
    <div class="key">s</div>
 
  </button>
 
  <button id="cmd-play" @click=${this.onCmdPlay} class="playMode ${classMap({ active: this.isPlaying })}">
 
    <strong>Play</strong>
 
    <div class="key">p</div>
 
  </button>
 
  <button id="cmd-intro" @click=${this.onCmdIntro}>
 
    <strong>Skip intro</strong>
 
    <div class="key">i</div>
 
  </button>
 
  <button id="cmd-post" @click=${this.onCmdPost}>
 
    <strong>Skip to Post</strong>
 
    <div class="key">t</div>
 
  </button>
 
  <button id="cmd-go" @click=${this.onCmdGo}>
 
    <strong>Go</strong>
 
    <div class="key">g</div>
 
    <div id="next">${this.nextText}</div>
 
  </button>
 
</div>`;
 
      <!-- <h1>ascoltami <a href="metrics">[metrics]</a></h1> -->
 
      
 
      <div id="grow">
 
        <span>
 
                <div id="songList">
 
                <table>
 
                  ${this.songList.map(
 
                    (song) => html`
 
                      <tr
 
                        class="row ${classMap({
 
                          playing: !!(this.song && song.equals(this.song)),
 
                        })}"
 
                      >
 
                        <td><resource-display .uri=${song}></resource-display></td>
 
                        <td>
 
                          <button @click=${this.onSelectSong.bind(this, song)}>
 
                            <span>Select</span>
 
                          </button>
 
                        </td>
 
                      </tr>
 
                    `
 
                  )}
 
                </table>
 
              </div>    
 
        </span><span>
 
      
 
        <div id="right">
 
      <div>
 
        Selected:
 
        <resource-display .uri=${this.selectedSong}></resource-display>
 
      </div>
 
      <div>
 
        <button id="playSelected"
 
          ?disabled=${this.selectedSong === null}
 
          @click=${this.onPlaySelected}
 
        >
 
          Play selected from start
 
        </button>
 
      </div>
 
      </div>    
 
        </span>
 
      </div>
 
      
 
      
 
      <div class="timeRow">
 
        <div id="timeSlider"></div>
 
        <light9-timeline-audio
 
          id="overview"
 
          .show=${this.show}
 
          .song=${this.song}
 
          .zoom=${this.overviewZoom}
 
        >
 
        </light9-timeline-audio>
 
        <light9-timeline-audio
 
          id="zoomed"
 
          .show=${this.show}
 
          .song=${this.song}
 
          .zoom=${this.zoom}
 
        ></light9-timeline-audio>
 
        <light9-cursor-canvas
 
          id="cursor"
 
          .viewState=${this.viewState}
 
        ></light9-cursor-canvas>
 
      </div>
 

	
 
      <div class="commands">
 
        <button
 
          id="cmd-stop"
 
          @click=${this.onCmdStop}
 
          class="playMode ${classMap({ active: !this.isPlaying })}"
 
        >
 
          <strong>Stop</strong>
 
          <div class="key">s</div>
 
        </button>
 
        <button
 
          id="cmd-play"
 
          @click=${this.onCmdPlay}
 
          class="playMode ${classMap({ active: this.isPlaying })}"
 
        >
 
          <strong>Play</strong>
 
          <div class="key">p</div>
 
        </button>
 
        <button id="cmd-intro" @click=${this.onCmdIntro}>
 
          <strong>Skip intro</strong>
 
          <div class="key">i</div>
 
        </button>
 
        <button id="cmd-post" @click=${this.onCmdPost}>
 
          <strong>Skip to Post</strong>
 
          <div class="key">t</div>
 
        </button>
 
        <button id="cmd-go" @click=${this.onCmdGo}>
 
          <strong>Go</strong>
 
          <div class="key">g</div>
 
          <div id="next">${this.nextText}</div>
 
        </button>
 
      </div>`;
 
  }
 

	
 
  onSelectSong(song: NamedNode, ev: MouseEvent) {
 
    if (this.selectedSong && song.equals(this.selectedSong)) {
 
      this.selectedSong = null;
 
    } else {
 
      this.selectedSong = song;
 
    }
 
  }
 
  async onPlaySelected(ev: Event) {
 
    if (!this.selectedSong) {
 
      return;
 
    }
 
    await fetch("api/song", { method: "POST", body: this.selectedSong.value });
 
  }
 

	
 
  onCmdStop(ev?: MouseEvent): void {
 
    postJson("api/time", { pause: true });
 
  }
 
  onCmdPlay(ev?: MouseEvent): void {
 
    postJson("api/time", { resume: true });
 
  }
 
  onCmdIntro(ev?: MouseEvent): void {
 
    postJson("api/time", { t: this.times.intro, resume: true });
 
  }
 
  onCmdPost(ev?: MouseEvent): void {
 
    postJson("api/time", { t: this.currentDuration - this.times.post, resume: true });
 
    postJson("api/time", {
 
      t: this.currentDuration - this.times.post,
 
      resume: true,
 
    });
 
  }
 
  onCmdGo(ev?: MouseEvent): void {
 
    postJson("api/go", {});
 
  }
 

	
 
  bindKeys() {
 
@@ -159,68 +271,76 @@ export class Light9AscoltamiUi extends L
 
    this.show = new NamedNode(config.show);
 
    this.times = config.times;
 
    document.title = document.title.replace("{{host}}", config.host);
 
    try {
 
      const h1 = document.querySelector("h1")!;
 
      h1.innerText = h1.innerText.replace("{{host}}", config.host);
 
    } catch (e) {
 

	
 
    }
 
    } catch (e) {}
 
    byId("nav").innerText = navigator.userAgent;
 
    var updateFreq = navigator.userAgent.indexOf("Linux") != -1 ? 10 : 2;
 
    if (navigator.userAgent.match(/Windows NT/)) {
 
      // helper laptop
 
      updateFreq = 10;
 
    }
 
    byId("updateReq").innerText = "" + updateFreq;
 

	
 
    (window as any).finishOldStyleSetup(this.times, updateFreq, (data: TimingUpdate) => {
 
      this.nextText = data.next;
 
      this.isPlaying = data.playing;
 
      this.currentDuration = data.duration;
 
      this.song = new NamedNode(data.song);
 
      this.overviewZoom = { duration: data.duration, t1: 0, t2: data.duration };
 
      const t1 = data.t - 2,
 
        t2 = data.t + 20;
 
      this.zoom = { duration: data.duration, t1, t2 };
 
      const timeRow = this.shadowRoot!.querySelector(".timeRow") as HTMLDivElement;
 
      const w = timeRow.offsetWidth;
 
      this.viewState = {
 
        zoomSpec: { t1: () => t1, t2: () => t2 },
 
        cursor: { t: () => data.t },
 
        audioY: () => 0,
 
        audioH: () => 60,
 
        zoomedTimeY: () => 60,
 
        zoomedTimeH: () => 40,
 
        fullZoomX: (sec: number) => (sec / data.duration) * w,
 
        zoomInX: (sec: number) => ((sec - t1) / (t2 - t1)) * w,
 
        mouse: { pos: () => $V([0, 0]) },
 
      };
 
    });
 
    (window as any).finishOldStyleSetup(
 
      this.times,
 
      updateFreq,
 
      this.onOldStyleUpdate.bind(this)
 
    );
 
  }
 
  @property() songList: NamedNode[] = []
 

	
 
  onOldStyleUpdate(data: TimingUpdate) {
 
    this.nextText = data.next;
 
    this.isPlaying = data.playing;
 
    this.currentDuration = data.duration;
 
    this.song = new NamedNode(data.song);
 
    this.overviewZoom = { duration: data.duration, t1: 0, t2: data.duration };
 
    const t1 = data.t - 2,
 
      t2 = data.t + 20;
 
    this.zoom = { duration: data.duration, t1, t2 };
 
    const timeRow = this.shadowRoot!.querySelector(
 
      ".timeRow"
 
    ) as HTMLDivElement;
 
    const w = timeRow.offsetWidth;
 
    this.viewState = {
 
      zoomSpec: { t1: () => t1, t2: () => t2 },
 
      cursor: { t: () => data.t },
 
      audioY: () => 0,
 
      audioH: () => 60,
 
      zoomedTimeY: () => 60,
 
      zoomedTimeH: () => 40,
 
      fullZoomX: (sec: number) => (sec / data.duration) * w,
 
      zoomInX: (sec: number) => ((sec - t1) / (t2 - t1)) * w,
 
      mouse: { pos: () => $V([0, 0]) },
 
    };
 
  }
 

	
 
  @property() songList: NamedNode[] = [];
 
  constructor() {
 
    super();
 
    this.bindKeys();
 
    this.zoom = this.overviewZoom = { duration: null, t1: 0, t2: 1 };
 

	
 
    getTopGraph().then((g) => {
 
      this.graph = g;
 
      this.musicSetup(); // async
 
      this.graph.runHandler(this.graphChanged.bind(this), 'loadsongs');
 
      this.graph.runHandler(this.graphChanged.bind(this), "loadsongs");
 
    });
 
  }
 
  graphChanged() {
 
    this.songList = []
 
    this.songList = [];
 
    try {
 
      const playList = this.graph.uriValue(//
 
        this.graph.Uri('http://light9.bigasterisk.com/show/dance2022'),
 
        this.graph.Uri(':playList'));
 
      log(playList)
 
      const playList = this.graph.uriValue(
 
        //
 
        this.graph.Uri("http://light9.bigasterisk.com/show/dance2022"),
 
        this.graph.Uri(":playList")
 
      );
 
      log(playList);
 
      this.songList = this.graph.items(playList) as NamedNode[];
 
    } catch (e) {
 
      log('no playlist yet')
 
      log("no playlist yet");
 
    }
 
    log(this.songList.length);
 

	
 
  }
 
}
light9/ascoltami/index.html
Show inline comments
 
@@ -8,50 +8,80 @@
 
        min-width: 5em;
 
      }
 
      .song-name {
 
        padding-left: 0.4em;
 
      }
 
      .dimStalled #currentTime {
 
        font-size:70px;
 
        font-size: 40px;
 
        background: green;
 
        color: black;
 
        padding: 3px;
 
      }
 
      .dimStalled {
 
        font-size:70%
 
        font-size: 90%;
 
      }
 
      body {
 
        margin: 0;
 
        padding: 0;
 
        overflow: hidden;
 
        min-height: 100vh;
 
      }
 
      #page {
 
        width: 100%;
 
        height: 90vh; /* my phone was losing the bottom :( */
 
        display: flex;
 
        flex-direction: column;
 
      }
 
      #page > div,
 
      #page > p {
 
        flex: 0 1 auto;
 
        margin: 0;
 
      }
 
      light9-ascoltami-ui {
 
        flex: 1 1 auto;
 
      }
 
    </style>
 
    <meta name="viewport" content="user-scalable=no, width=500, initial-scale=1" />
 
    <meta
 
      name="viewport"
 
      content="user-scalable=no, width=500, initial-scale=1"
 
    />
 
    <script type="module" src="../ascoltami/Light9AscoltamiUi"></script>
 
  </head>
 
  <body>
 
    <h1>ascoltami on {{host}}</h1>
 
    <div class="songs"></div>
 
    <div id="page">
 
      <div class="songs" style="display: none"></div>
 

	
 
    <div class="dimStalled">
 
      <table>
 
        <tr>
 
          <td colspan="3"><strong>Song:</strong> <span id="currentSong"></span></td>
 
        </tr>
 
        <tr>
 
          <td><strong>Time:</strong> <span id="currentTime"></span></td>
 
          <td><strong>Left:</strong> <span id="leftTime"></span></td>
 
          <td><strong>Until autostop:</strong> <span id="leftAutoStopTime"></span></td>
 
        </tr>
 
        <tr>
 
          <td colspan="3">
 
            <strong>Update freq:</strong> requested <span id="updateReq"></span>, actual <span id="updateActual"></span> <strong>States:</strong>
 
            <span id="states"></span>
 
          </td>
 
        </tr>
 
      </table>
 
      <div class="dimStalled">
 
        <table>
 
          <tr>
 
            <td colspan="3">
 
              <strong>Song:</strong> <span id="currentSong"></span>
 
            </td>
 
          </tr>
 
          <tr>
 
            <td><strong>Time:</strong> <span id="currentTime"></span></td>
 
            <td><strong>Left:</strong> <span id="leftTime"></span></td>
 
            <td>
 
              <strong>Until autostop:</strong>
 
              <span id="leftAutoStopTime"></span>
 
            </td>
 
          </tr>
 
          <tr>
 
            <td colspan="3">
 
              <strong>Update freq:</strong> requested
 
              <span id="updateReq"></span>, actual
 
              <span id="updateActual"></span> <strong>States:</strong>
 
              <span id="states"></span>
 
            </td>
 
          </tr>
 
        </table>
 
      </div>
 

	
 
      <hr />
 
      <light9-ascoltami-ui></light9-ascoltami-ui>
 
      <p>Running on <span id="nav"></span></p>
 
      <p><a href="">reload</a></p>
 
    </div>
 

	
 
    <hr />
 
    new ui is here
 
    <light9-ascoltami-ui></light9-ascoltami-ui>
 
    <hr />
 

	
 
    <p>Running on <span id="nav"></span></p>
 
    <p><a href="">reload</a></p>
 

	
 
    <script type="module" src="../ascoltami/main.ts"></script>
 
  </body>
 
</html>
0 comments (0 inline, 0 general)