diff options
| author | Florian Fischer <florian.fischer@muhq.space> | 2023-05-30 11:29:39 +0200 |
|---|---|---|
| committer | Florian Fischer <florian.fischer@muhq.space> | 2023-05-30 11:29:39 +0200 |
| commit | cc922fb33b3e6dac22da4812572eebfcc709af3c (patch) | |
| tree | d1260e8e9c0ef0beda08c7e959ecf77a26c45f63 | |
| parent | 3b1012ed66c8bbc6c550833148cd2fc25aed125b (diff) | |
| download | WRMS-cc922fb33b3e6dac22da4812572eebfcc709af3c.tar.gz WRMS-cc922fb33b3e6dac22da4812572eebfcc709af3c.zip | |
distinguish between search queries
Introduce a new Id field for events to distinguish between
different searches.
Since search result reporting is split up a old search could introduce
results or terminate the current search.
Keep track of a search counter to distinguish between result from the
active search and stale ones.
Allow to add multiple songs from the current search.
| -rw-r--r-- | TODO.md | 2 | ||||
| -rw-r--r-- | main.go | 23 | ||||
| -rw-r--r-- | web/client.html | 34 | ||||
| -rw-r--r-- | wrms.go | 17 |
4 files changed, 55 insertions, 21 deletions
@@ -9,7 +9,7 @@ - [X] escape HTML - [X] support incremental search results - - [ ] distinguish different search queries + - [X] distinguish different search queries ## Web Backend @@ -8,6 +8,7 @@ import ( "html/template" "io" "net/http" + "strconv" "strings" "time" @@ -79,6 +80,16 @@ func searchHandler(w http.ResponseWriter, r *http.Request) { } pattern := r.URL.Query().Get("pattern") + if pattern == "" { + http.Error(w, "No search pattern provided", http.StatusBadRequest) + return + } + + id, ok := strconv.Atoi(r.URL.Query().Get("id")) + if ok != err { + http.Error(w, "No or invalid search id provided", http.StatusBadRequest) + return + } llog.Debug("Searching for %s", pattern) @@ -88,11 +99,11 @@ func searchHandler(w http.ResponseWriter, r *http.Request) { go func() { for result := range resultsChan { if len(result) > 0 { - conn.Send(Event{"search", result}) + conn.Send(newSearchResultEvent(id, result)) } } - conn.Send(newNotification("finish-search")) + conn.Send(Event{Event: "finish-search", Id: id}) llog.Debug("searching for %s took %v", pattern, time.Since(start)) }() @@ -187,7 +198,7 @@ func wsEndpoint(w http.ResponseWriter, r *http.Request) { initialCmds := [][]byte{} if wrms.Playing { - data, err := json.Marshal(Event{"play", []Song{wrms.CurrentSong}}) + data, err := json.Marshal(newEvent("play", []Song{wrms.CurrentSong})) if err != nil { llog.Error("Encoding the play event failed with %s", err) return @@ -198,7 +209,7 @@ func wsEndpoint(w http.ResponseWriter, r *http.Request) { upvoted := []Song{} downvoted := []Song{} if len(wrms.Songs) > 0 { - data, err := json.Marshal(Event{"add", wrms.Songs}) + data, err := json.Marshal(newEvent("add", wrms.Songs)) if err != nil { llog.Error("Encoding the add event failed with %s", err) return @@ -215,7 +226,7 @@ func wsEndpoint(w http.ResponseWriter, r *http.Request) { } if len(upvoted) > 0 { - data, err := json.Marshal(Event{"upvoted", upvoted}) + data, err := json.Marshal(newEvent("upvoted", upvoted)) if err != nil { llog.Error("Encoding the upvoted event failed with %s", err) return @@ -224,7 +235,7 @@ func wsEndpoint(w http.ResponseWriter, r *http.Request) { } if len(downvoted) > 0 { - data, err := json.Marshal(Event{"downvoted", downvoted}) + data, err := json.Marshal(newEvent("downvoted", downvoted)) if err != nil { llog.Error("Encoding the upvoted event failed with %s", err) return diff --git a/web/client.html b/web/client.html index 0f94c45..20aa7ad 100644 --- a/web/client.html +++ b/web/client.html @@ -73,6 +73,7 @@ } let isAdmin = false; + let searchId = 0; let songs = []; let playing = {}; let votes = new Map(); @@ -237,10 +238,10 @@ handleVotes("down", cmd.songs) break; case "search": - handleSearch(cmd.songs) + handleSearch(cmd.id, cmd.songs) break; case "finish-search": - handleFinishSearch() + handleFinishSearch(cmd.id) break; } }; @@ -250,9 +251,7 @@ }; function submitSearch() { - // Clear search Results - let searchResults = document.getElementById("searchResults"); - searchResults.innerHTML = ""; + resetSearch(); let searchingIndicator = document.getElementById("searching"); searchingIndicator.style.display = "block"; @@ -260,8 +259,9 @@ searchResultsOverlay['aria-busy'] = true; const form = new FormData(document.getElementById("searchForm")); - const pattern = new URLSearchParams(form).toString(); - new HttpClient().get("/search?" + pattern, console.log); + const urlParams = new URLSearchParams(form); + urlParams.append("id", searchId); + new HttpClient().get("/search?" + urlParams.toString(), console.log); return false; } @@ -281,21 +281,27 @@ new HttpClient().post("/add", JSON.stringify(song), console.log); } - function handleFinishSearch() { + function handleFinishSearch(id) { + // Ignore stale finish search notifications + if (id < searchId) { return; } + let searchingIndicator = document.getElementById("searching"); searchingIndicator.style.display = "none"; let searchResultsOverlay = document.getElementById("searchResultsOverlay"); searchResultsOverlay['aria-busy'] = false; + searchId += 1; } - function handleSearch(songs) { + function handleSearch(id, songs) { + // Ignore stale search results + if (id < searchId) { return; } + let searchResultsOverlay = document.getElementById("searchResultsOverlay"); let searchResults = document.getElementById("searchResults"); for (const song of songs) { let listItem = document.createElement("li"); listItem.addEventListener("click", function() { addSong(song); - searchResultsOverlay.style.display = "none"; }); listItem.classList.add("searchResult") @@ -308,6 +314,14 @@ searchResultsOverlay.style.display = "block"; } + function resetSearch() { + // Clear search Results + let searchResults = document.getElementById("searchResults"); + searchResults.innerHTML = ""; + + searchId += 1; + } + window.onload = function() { {{if .IsAdmin}} document.getElementById("ppbutton").addEventListener("click", function() { @@ -40,11 +40,20 @@ func NewSongFromJson(data []byte) (Song, error) { type Event struct { Event string `json:"cmd"` + Id int `json:"id"` Songs []Song `json:"songs"` } +func newEvent(event string, songs []Song) Event { + return Event{Event: event, Songs: songs} +} + func newNotification(notification string) Event { - return Event{notification, nil} + return Event{Event: notification} +} + +func newSearchResultEvent(id int, songs []Song) Event { + return Event{Event: "search", Id: id, Songs: songs} } type Connection struct { @@ -100,7 +109,7 @@ func (wrms *Wrms) Broadcast(ev Event) { } func (wrms *Wrms) startPlaying() { - wrms.Broadcast(Event{"play", []Song{wrms.CurrentSong}}) + wrms.Broadcast(newEvent("play", []Song{wrms.CurrentSong})) wrms.Player.Play(&wrms.CurrentSong) } @@ -110,7 +119,7 @@ func (wrms *Wrms) AddSong(song Song) { s := &wrms.Songs[len(wrms.Songs)-1] wrms.queue.Add(s) llog.Info("Added song %s (ptr=%p) to Songs", s.Uri, s) - wrms.Broadcast(Event{"add", []Song{song}}) + wrms.Broadcast(newEvent("add", []Song{song})) if startPlayingAgain { wrms.Next() @@ -218,7 +227,7 @@ func (wrms *Wrms) AdjustSongWeight(connId uuid.UUID, songUri string, vote string } wrms.queue.Adjust(s) - wrms.Broadcast(Event{"update", []Song{*s}}) + wrms.Broadcast(newEvent("update", []Song{*s})) break } } |
