From 8b775cb39f45628e7734b5bbd3e852f361858725 Mon Sep 17 00:00:00 2001 From: Stein Ivar Berghei Date: Mon, 21 Nov 2022 23:49:43 +0100 Subject: [PATCH 1/3] Fix youtube api paging --- youtube.go | 1 + 1 file changed, 1 insertion(+) diff --git a/youtube.go b/youtube.go index c7d1590..885018e 100644 --- a/youtube.go +++ b/youtube.go @@ -150,6 +150,7 @@ func (app App) Playlist(playlist string) ([]string, error) { if pageToken == "" { break } + call.PageToken(pageToken) } return list, nil } From cb33ba2d4190fdff855258ac3458a7dee0c56ded Mon Sep 17 00:00:00 2001 From: Trond A Ekseth Date: Mon, 21 Nov 2022 23:52:07 +0100 Subject: [PATCH 2/3] Handle touch input in chrome mobile --- css/style.css | 5 ++++ js/script.js | 67 ++++++++++++++++++++++++++++++++++++--------------- 2 files changed, 52 insertions(+), 20 deletions(-) diff --git a/css/style.css b/css/style.css index 3abc00c..b7b4089 100644 --- a/css/style.css +++ b/css/style.css @@ -28,6 +28,11 @@ h2.bot { } .item { + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + display: flex; justify-content: center; align-items: center; diff --git a/js/script.js b/js/script.js index 75baa7f..5908bcf 100644 --- a/js/script.js +++ b/js/script.js @@ -1,6 +1,29 @@ import ReconnectingWebSocket from 'reconnecting-websocket'; import Sortable from 'sortablejs'; +const DND_DELAY = 300; + +const addInteractHandler = (source, handler) => { + source.addEventListener("click", e => { + console.log("click", e); + handler(e, false); + }); + + let touchStart; + source.addEventListener("touchstart", e => { + console.log("touch start", touchStart, e.timeStamp, e); + touchStart = e.timeStamp; + }, {passive: true}) + + source.addEventListener("touchend", e => { + console.log("touch end", touchStart, e); + console.log(e.timeStamp - touchStart) + if ((e.timeStamp - touchStart) < DND_DELAY) { + handler(e, true); + } + }, {passive: true}); +} + window.onload = function () { const ws = new ReconnectingWebSocket(((window.location.protocol === "https:") ? "wss://" : "ws://") + window.location.host + "/ws"); @@ -13,19 +36,22 @@ window.onload = function () { const time = document.querySelector("#time") const waiting = document.querySelector("#waiting") - document.querySelector("input#next").addEventListener("click", (e) => { - ws.send(JSON.stringify({ - "event": "next" - })) - }) + addInteractHandler( + document.querySelector("input#next"), (e) => { + ws.send(JSON.stringify({ + "event": "next" + })) + }) - document.querySelector("input#prev").addEventListener("click", (e) => { - ws.send(JSON.stringify({ - "event": "prev" - })) - }) + addInteractHandler( + document.querySelector("input#prev"), (e) => { + ws.send(JSON.stringify({ + "event": "prev" + })) + }) Sortable.create(items, { + delay: DND_DELAY, delayOnTouchOnly: true, group: "dndmusicbot-playlists", filter: ".locked", @@ -48,6 +74,7 @@ window.onload = function () { }) Sortable.create(amb, { + delay: DND_DELAY, delayOnTouchOnly: true, group: "dndmusicbot-ambiance", filter: ".locked", @@ -127,8 +154,8 @@ window.onload = function () { } } - document.querySelector("#addambiance").addEventListener("click", (e) => { - e.preventDefault() + addInteractHandler(document.querySelector("#addambiance"), (e, isTouch) => { + isTouch && e.preventDefault() output.innerText = "" var title = document.querySelector("#inputambiance > input[name='title']") @@ -147,8 +174,8 @@ window.onload = function () { })) }) - submit.addEventListener("click", (e) => { - e.preventDefault() + addInteractHandler(submit, (e, isTouch) => { + isTouch && e.preventDefault() output.innerText = "" var title = document.querySelector("#inputplaylist > input[name='title']") @@ -170,8 +197,8 @@ window.onload = function () { url.innerText = "" }) - document.querySelectorAll("#items .item").forEach(item => item.addEventListener("click", (e) => { - e.preventDefault() + document.querySelectorAll("#items .item").forEach(item => addInteractHandler(item, (e, isTouch) => { + isTouch && e.preventDefault() e.target.parentElement.style.pointerEvents = 'none' const disableui = setTimeout((t) => { t.style.pointerEvents = 'auto' @@ -184,8 +211,8 @@ window.onload = function () { })) })); - document.querySelectorAll("#ambiance .item").forEach(item => item.addEventListener("click", (e) => { - e.preventDefault() + document.querySelectorAll("#ambiance .item").forEach(item => addInteractHandler(item, (e, isTouch) => { + isTouch && e.preventDefault() e.target.parentElement.style.pointerEvents = 'none' const disableui = setTimeout((t) => { t.style.pointerEvents = 'auto' @@ -205,8 +232,8 @@ function addPlaylist(payload) { newdiv.className = "item" newdiv.dataset.id = payload.url newdiv.innerText = payload.title - newdiv.addEventListener("click", (e) => { - e.preventDefault() + addInteractHandler(newdiv, (e, isTouch) => { + isTouch && e.preventDefault() var id = e.target.dataset.id ws.send(JSON.stringify({ From fa7e5b5bafc8367eb2b0070c4060b3445fa697b7 Mon Sep 17 00:00:00 2001 From: Stein Ivar Berghei Date: Tue, 22 Nov 2022 00:33:43 +0100 Subject: [PATCH 3/3] Serve files with servecontent so that ETags etc should work. --- routes.go | 30 +++++++++++++++++++++++++++--- tmpl/index.tmpl | 2 +- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/routes.go b/routes.go index 4417393..190056a 100644 --- a/routes.go +++ b/routes.go @@ -3,6 +3,9 @@ package main import ( "log" "net/http" + "os" + "path" + "path/filepath" "text/template" "github.com/google/uuid" @@ -21,9 +24,8 @@ func init() { app.router.GET("/", app.Index) app.router.GET("/play/:playlist", app.Play) app.router.GET("/reset", app.Reset) - - app.router.ServeFiles("/js/*filepath", http.Dir("public")) - app.router.ServeFiles("/css/*filepath", http.Dir("css")) + app.router.GET("/public/*js", app.ServeFiles) + app.router.GET("/css/*css", app.ServeFiles) app.router.HandlerFunc("GET", "/ws", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { log.Printf("WS connection from %v\n", r.RemoteAddr) @@ -49,6 +51,28 @@ type IndexData struct { Ambiance []string } +func (app App) ServeFiles(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { + filePath := filepath.Join(".", r.URL.Path) + + file, err := os.Open(filePath) + if err != nil { + log.Println(err) + http.Error(w, "no such file", http.StatusNotFound) + return + } + defer file.Close() + + fileStat, err := os.Stat(filePath) + if err != nil { + log.Println(err) + http.Error(w, "unable to get file stat", http.StatusInternalServerError) + } + + _, filename := path.Split(filePath) + t := fileStat.ModTime() + http.ServeContent(w, r, filename, t, file) +} + func (app App) Index(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { playlists, err := app.GetPlaylists() if err != nil { diff --git a/tmpl/index.tmpl b/tmpl/index.tmpl index 8c436a7..e3ffd0f 100644 --- a/tmpl/index.tmpl +++ b/tmpl/index.tmpl @@ -74,6 +74,6 @@ - +