Mercurial > code > home > repos > light9
comparison light9/ascoltami/Light9AscoltamiUi.ts @ 2135:197a4ec877a7
acso relayout so only the song list scrolls
author | drewp@bigasterisk.com |
---|---|
date | Mon, 06 Jun 2022 01:15:17 +0000 |
parents | 1dbbf0db3036 |
children | b39f6c363163 |
comparison
equal
deleted
inserted
replaced
2134:40475b0ee0ba | 2135:197a4ec877a7 |
---|---|
7 import { Zoom } from "../web/light9-timeline-audio"; | 7 import { Zoom } from "../web/light9-timeline-audio"; |
8 import { PlainViewState } from "../web/Light9CursorCanvas"; | 8 import { PlainViewState } from "../web/Light9CursorCanvas"; |
9 import { getTopGraph } from "../web/RdfdbSyncedGraph"; | 9 import { getTopGraph } from "../web/RdfdbSyncedGraph"; |
10 import { SyncedGraph } from "../web/SyncedGraph"; | 10 import { SyncedGraph } from "../web/SyncedGraph"; |
11 import { TimingUpdate } from "./main"; | 11 import { TimingUpdate } from "./main"; |
12 import { ResourceDisplay } from '../web/ResourceDisplay'; | |
13 export { Light9TimelineAudio } from "../web/light9-timeline-audio"; | 12 export { Light9TimelineAudio } from "../web/light9-timeline-audio"; |
14 export { Light9CursorCanvas } from "../web/Light9CursorCanvas"; | 13 export { Light9CursorCanvas } from "../web/Light9CursorCanvas"; |
15 export { RdfdbSyncedGraph } from "../web/RdfdbSyncedGraph"; | 14 export { RdfdbSyncedGraph } from "../web/RdfdbSyncedGraph"; |
16 export { ResourceDisplay } from "../web/ResourceDisplay"; | 15 export { ResourceDisplay } from "../web/ResourceDisplay"; |
17 const $V = Sylvester.Vector.create; | 16 const $V = Sylvester.Vector.create; |
35 times!: { intro: number; post: number }; | 34 times!: { intro: number; post: number }; |
36 @property() nextText: string = ""; | 35 @property() nextText: string = ""; |
37 @property() isPlaying: boolean = false; | 36 @property() isPlaying: boolean = false; |
38 @property() show: NamedNode | null = null; | 37 @property() show: NamedNode | null = null; |
39 @property() song: NamedNode | null = null; | 38 @property() song: NamedNode | null = null; |
40 @property() requestedSong: NamedNode | null = null; | 39 @property() selectedSong: NamedNode | null = null; |
41 @property() currentDuration: number = 0; | 40 @property() currentDuration: number = 0; |
42 @property() zoom: Zoom; | 41 @property() zoom: Zoom; |
43 @property() overviewZoom: Zoom; | 42 @property() overviewZoom: Zoom; |
44 @property() viewState: PlainViewState | null = null; | 43 @property() viewState: PlainViewState | null = null; |
45 static styles = [ | 44 static styles = [ |
46 css` | 45 css` |
46 :host { | |
47 display: flex; | |
48 flex-direction: column; | |
49 } | |
47 .timeRow { | 50 .timeRow { |
48 margin: 14px; | 51 margin: 14px; |
49 position: relative; | 52 position: relative; |
50 } | 53 } |
51 #overview { | 54 #overview { |
60 left: 0; | 63 left: 0; |
61 top: 0; | 64 top: 0; |
62 width: 100%; | 65 width: 100%; |
63 height: 100%; | 66 height: 100%; |
64 } | 67 } |
68 #grow { | |
69 | |
70 flex: 1 1 auto; | |
71 display: flex; | |
72 | |
73 } | |
74 #grow > span { | |
75 display: flex; | |
76 position: relative; | |
77 width:50%; | |
78 } | |
79 #playSelected { | |
80 height:100px; | |
81 } | |
82 #songList { | |
83 overflow-y: scroll; | |
84 position: absolute; | |
85 left:0;top:0;right:0;bottom:0; | |
86 } | |
87 #songList .row { | |
88 width: 60%; | |
89 min-height: 40px; | |
90 text-align: left; | |
91 position: relative; | |
92 } | |
93 #songList .row:nth-child(even) {background: #333;} | |
94 #songList .row:nth-child(odd) {background: #444;} | |
95 #songList button { | |
96 min-height: 40px; | |
97 margin-bottom: 10px; | |
98 } | |
99 #songList .row.playing { | |
100 box-shadow: 0 0 30px red; | |
101 background-color: #de5050; | |
102 } | |
65 `, | 103 `, |
66 ]; | 104 ]; |
67 render() { | 105 render() { |
68 return html`<rdfdb-synced-graph></rdfdb-synced-graph> | 106 return html`<rdfdb-synced-graph></rdfdb-synced-graph> |
69 | 107 |
70 <link rel="stylesheet" href="./style.css" /> | 108 <link rel="stylesheet" href="./style.css" /> |
71 | 109 |
72 <!-- <h1>ascoltami <a href="metrics">[metrics]</a></h1> --> | 110 <!-- <h1>ascoltami <a href="metrics">[metrics]</a></h1> --> |
73 <!-- <div> | 111 |
74 songlist: | 112 <div id="grow"> |
75 ${this.songList.map((song) => html` | 113 <span> |
76 <div>song: <resource-display .uri=${song}></resource-display> | 114 <div id="songList"> |
77 </div> | 115 <table> |
78 `)} | 116 ${this.songList.map( |
79 </div> --> | 117 (song) => html` |
80 <div class="timeRow"> | 118 <tr |
81 <div id="timeSlider"></div> | 119 class="row ${classMap({ |
82 <light9-timeline-audio id="overview" .show=${this.show} .song=${this.song} .zoom=${this.overviewZoom}> | 120 playing: !!(this.song && song.equals(this.song)), |
83 </light9-timeline-audio> | 121 })}" |
84 <light9-timeline-audio id="zoomed" .show=${this.show} .song=${this.song} .zoom=${this.zoom}></light9-timeline-audio> | 122 > |
85 <light9-cursor-canvas id="cursor" .viewState=${this.viewState}></light9-cursor-canvas> | 123 <td><resource-display .uri=${song}></resource-display></td> |
86 </div> | 124 <td> |
87 | 125 <button @click=${this.onSelectSong.bind(this, song)}> |
88 <div class="commands"> | 126 <span>Select</span> |
89 <button id="cmd-stop" @click=${this.onCmdStop} class="playMode ${classMap({ active: !this.isPlaying })}"> | 127 </button> |
90 <strong>Stop</strong> | 128 </td> |
91 <div class="key">s</div> | 129 </tr> |
92 </button> | 130 ` |
93 <button id="cmd-play" @click=${this.onCmdPlay} class="playMode ${classMap({ active: this.isPlaying })}"> | 131 )} |
94 <strong>Play</strong> | 132 </table> |
95 <div class="key">p</div> | 133 </div> |
96 </button> | 134 </span><span> |
97 <button id="cmd-intro" @click=${this.onCmdIntro}> | 135 |
98 <strong>Skip intro</strong> | 136 <div id="right"> |
99 <div class="key">i</div> | 137 <div> |
100 </button> | 138 Selected: |
101 <button id="cmd-post" @click=${this.onCmdPost}> | 139 <resource-display .uri=${this.selectedSong}></resource-display> |
102 <strong>Skip to Post</strong> | 140 </div> |
103 <div class="key">t</div> | 141 <div> |
104 </button> | 142 <button id="playSelected" |
105 <button id="cmd-go" @click=${this.onCmdGo}> | 143 ?disabled=${this.selectedSong === null} |
106 <strong>Go</strong> | 144 @click=${this.onPlaySelected} |
107 <div class="key">g</div> | 145 > |
108 <div id="next">${this.nextText}</div> | 146 Play selected from start |
109 </button> | 147 </button> |
110 </div>`; | 148 </div> |
149 </div> | |
150 </span> | |
151 </div> | |
152 | |
153 | |
154 <div class="timeRow"> | |
155 <div id="timeSlider"></div> | |
156 <light9-timeline-audio | |
157 id="overview" | |
158 .show=${this.show} | |
159 .song=${this.song} | |
160 .zoom=${this.overviewZoom} | |
161 > | |
162 </light9-timeline-audio> | |
163 <light9-timeline-audio | |
164 id="zoomed" | |
165 .show=${this.show} | |
166 .song=${this.song} | |
167 .zoom=${this.zoom} | |
168 ></light9-timeline-audio> | |
169 <light9-cursor-canvas | |
170 id="cursor" | |
171 .viewState=${this.viewState} | |
172 ></light9-cursor-canvas> | |
173 </div> | |
174 | |
175 <div class="commands"> | |
176 <button | |
177 id="cmd-stop" | |
178 @click=${this.onCmdStop} | |
179 class="playMode ${classMap({ active: !this.isPlaying })}" | |
180 > | |
181 <strong>Stop</strong> | |
182 <div class="key">s</div> | |
183 </button> | |
184 <button | |
185 id="cmd-play" | |
186 @click=${this.onCmdPlay} | |
187 class="playMode ${classMap({ active: this.isPlaying })}" | |
188 > | |
189 <strong>Play</strong> | |
190 <div class="key">p</div> | |
191 </button> | |
192 <button id="cmd-intro" @click=${this.onCmdIntro}> | |
193 <strong>Skip intro</strong> | |
194 <div class="key">i</div> | |
195 </button> | |
196 <button id="cmd-post" @click=${this.onCmdPost}> | |
197 <strong>Skip to Post</strong> | |
198 <div class="key">t</div> | |
199 </button> | |
200 <button id="cmd-go" @click=${this.onCmdGo}> | |
201 <strong>Go</strong> | |
202 <div class="key">g</div> | |
203 <div id="next">${this.nextText}</div> | |
204 </button> | |
205 </div>`; | |
206 } | |
207 | |
208 onSelectSong(song: NamedNode, ev: MouseEvent) { | |
209 if (this.selectedSong && song.equals(this.selectedSong)) { | |
210 this.selectedSong = null; | |
211 } else { | |
212 this.selectedSong = song; | |
213 } | |
214 } | |
215 async onPlaySelected(ev: Event) { | |
216 if (!this.selectedSong) { | |
217 return; | |
218 } | |
219 await fetch("api/song", { method: "POST", body: this.selectedSong.value }); | |
111 } | 220 } |
112 | 221 |
113 onCmdStop(ev?: MouseEvent): void { | 222 onCmdStop(ev?: MouseEvent): void { |
114 postJson("api/time", { pause: true }); | 223 postJson("api/time", { pause: true }); |
115 } | 224 } |
118 } | 227 } |
119 onCmdIntro(ev?: MouseEvent): void { | 228 onCmdIntro(ev?: MouseEvent): void { |
120 postJson("api/time", { t: this.times.intro, resume: true }); | 229 postJson("api/time", { t: this.times.intro, resume: true }); |
121 } | 230 } |
122 onCmdPost(ev?: MouseEvent): void { | 231 onCmdPost(ev?: MouseEvent): void { |
123 postJson("api/time", { t: this.currentDuration - this.times.post, resume: true }); | 232 postJson("api/time", { |
233 t: this.currentDuration - this.times.post, | |
234 resume: true, | |
235 }); | |
124 } | 236 } |
125 onCmdGo(ev?: MouseEvent): void { | 237 onCmdGo(ev?: MouseEvent): void { |
126 postJson("api/go", {}); | 238 postJson("api/go", {}); |
127 } | 239 } |
128 | 240 |
160 this.times = config.times; | 272 this.times = config.times; |
161 document.title = document.title.replace("{{host}}", config.host); | 273 document.title = document.title.replace("{{host}}", config.host); |
162 try { | 274 try { |
163 const h1 = document.querySelector("h1")!; | 275 const h1 = document.querySelector("h1")!; |
164 h1.innerText = h1.innerText.replace("{{host}}", config.host); | 276 h1.innerText = h1.innerText.replace("{{host}}", config.host); |
165 } catch (e) { | 277 } catch (e) {} |
166 | |
167 } | |
168 byId("nav").innerText = navigator.userAgent; | 278 byId("nav").innerText = navigator.userAgent; |
169 var updateFreq = navigator.userAgent.indexOf("Linux") != -1 ? 10 : 2; | 279 var updateFreq = navigator.userAgent.indexOf("Linux") != -1 ? 10 : 2; |
170 if (navigator.userAgent.match(/Windows NT/)) { | 280 if (navigator.userAgent.match(/Windows NT/)) { |
171 // helper laptop | 281 // helper laptop |
172 updateFreq = 10; | 282 updateFreq = 10; |
173 } | 283 } |
174 byId("updateReq").innerText = "" + updateFreq; | 284 byId("updateReq").innerText = "" + updateFreq; |
175 | 285 |
176 (window as any).finishOldStyleSetup(this.times, updateFreq, (data: TimingUpdate) => { | 286 (window as any).finishOldStyleSetup( |
177 this.nextText = data.next; | 287 this.times, |
178 this.isPlaying = data.playing; | 288 updateFreq, |
179 this.currentDuration = data.duration; | 289 this.onOldStyleUpdate.bind(this) |
180 this.song = new NamedNode(data.song); | 290 ); |
181 this.overviewZoom = { duration: data.duration, t1: 0, t2: data.duration }; | 291 } |
182 const t1 = data.t - 2, | 292 |
183 t2 = data.t + 20; | 293 onOldStyleUpdate(data: TimingUpdate) { |
184 this.zoom = { duration: data.duration, t1, t2 }; | 294 this.nextText = data.next; |
185 const timeRow = this.shadowRoot!.querySelector(".timeRow") as HTMLDivElement; | 295 this.isPlaying = data.playing; |
186 const w = timeRow.offsetWidth; | 296 this.currentDuration = data.duration; |
187 this.viewState = { | 297 this.song = new NamedNode(data.song); |
188 zoomSpec: { t1: () => t1, t2: () => t2 }, | 298 this.overviewZoom = { duration: data.duration, t1: 0, t2: data.duration }; |
189 cursor: { t: () => data.t }, | 299 const t1 = data.t - 2, |
190 audioY: () => 0, | 300 t2 = data.t + 20; |
191 audioH: () => 60, | 301 this.zoom = { duration: data.duration, t1, t2 }; |
192 zoomedTimeY: () => 60, | 302 const timeRow = this.shadowRoot!.querySelector( |
193 zoomedTimeH: () => 40, | 303 ".timeRow" |
194 fullZoomX: (sec: number) => (sec / data.duration) * w, | 304 ) as HTMLDivElement; |
195 zoomInX: (sec: number) => ((sec - t1) / (t2 - t1)) * w, | 305 const w = timeRow.offsetWidth; |
196 mouse: { pos: () => $V([0, 0]) }, | 306 this.viewState = { |
197 }; | 307 zoomSpec: { t1: () => t1, t2: () => t2 }, |
198 }); | 308 cursor: { t: () => data.t }, |
199 } | 309 audioY: () => 0, |
200 @property() songList: NamedNode[] = [] | 310 audioH: () => 60, |
311 zoomedTimeY: () => 60, | |
312 zoomedTimeH: () => 40, | |
313 fullZoomX: (sec: number) => (sec / data.duration) * w, | |
314 zoomInX: (sec: number) => ((sec - t1) / (t2 - t1)) * w, | |
315 mouse: { pos: () => $V([0, 0]) }, | |
316 }; | |
317 } | |
318 | |
319 @property() songList: NamedNode[] = []; | |
201 constructor() { | 320 constructor() { |
202 super(); | 321 super(); |
203 this.bindKeys(); | 322 this.bindKeys(); |
204 this.zoom = this.overviewZoom = { duration: null, t1: 0, t2: 1 }; | 323 this.zoom = this.overviewZoom = { duration: null, t1: 0, t2: 1 }; |
205 | 324 |
206 getTopGraph().then((g) => { | 325 getTopGraph().then((g) => { |
207 this.graph = g; | 326 this.graph = g; |
208 this.musicSetup(); // async | 327 this.musicSetup(); // async |
209 this.graph.runHandler(this.graphChanged.bind(this), 'loadsongs'); | 328 this.graph.runHandler(this.graphChanged.bind(this), "loadsongs"); |
210 }); | 329 }); |
211 } | 330 } |
212 graphChanged() { | 331 graphChanged() { |
213 this.songList = [] | 332 this.songList = []; |
214 try { | 333 try { |
215 const playList = this.graph.uriValue(// | 334 const playList = this.graph.uriValue( |
216 this.graph.Uri('http://light9.bigasterisk.com/show/dance2022'), | 335 // |
217 this.graph.Uri(':playList')); | 336 this.graph.Uri("http://light9.bigasterisk.com/show/dance2022"), |
218 log(playList) | 337 this.graph.Uri(":playList") |
338 ); | |
339 log(playList); | |
219 this.songList = this.graph.items(playList) as NamedNode[]; | 340 this.songList = this.graph.items(playList) as NamedNode[]; |
220 } catch (e) { | 341 } catch (e) { |
221 log('no playlist yet') | 342 log("no playlist yet"); |
222 } | 343 } |
223 log(this.songList.length); | 344 log(this.songList.length); |
224 | |
225 } | 345 } |
226 } | 346 } |