From b5ecf95a7b65909b60a85c9b0dab4e9f5a5a8073 Mon Sep 17 00:00:00 2001 From: David Vogel Date: Mon, 19 May 2025 12:03:40 +0200 Subject: [PATCH] Add split function - Add ServerWebsocketPacketQueueSplit - Add Split method to Queue - Correct automatic separator insertion for newly ingested documents - Rework UI so that entries have their own set of buttons - Refactor occurrences of "document" to "queueEntry" --- main.go | 7 ++-- queue.go | 33 ++++++++++++++++--- server-websocket-packets.go | 16 +++++++-- server-websocket.go | 4 +++ static/js/api.js | 14 ++++++++ .../components/document-queue-entry-page.js | 32 ++++++++++++++++-- .../document-queue-entry-separator.js | 24 ++++++++++++-- static/js/components/document-queue-entry.js | 5 ++- static/js/model.d.ts | 4 +++ 9 files changed, 120 insertions(+), 19 deletions(-) diff --git a/main.go b/main.go index e824dbb..40e57b2 100644 --- a/main.go +++ b/main.go @@ -58,7 +58,12 @@ func main() { continue } + server.Documents.Lock() + var entries []QueueEntry + if len(server.Documents.Documents) > 0 { + entries = append(entries, QueueEntry{ID: NewQueueEntryID(), QueueEntryData: QueueEntryDataSeparator{}}) + } for _, page := range docPages { entries = append(entries, QueueEntry{ ID: NewQueueEntryID(), @@ -66,9 +71,7 @@ func main() { QueueEntryData: QueueEntryDataPage{Page: &page}, }) } - entries = append(entries, QueueEntry{ID: NewQueueEntryID(), QueueEntryData: QueueEntryDataSeparator{}}) - server.Documents.Lock() server.Documents.Append(entries...) server.Documents.Unlock() } diff --git a/queue.go b/queue.go index 0569f68..87fbe9c 100644 --- a/queue.go +++ b/queue.go @@ -171,15 +171,38 @@ func (d *Queue) Shift(offset int, ids ...QueueEntryID) { } } +// Split will add a separator behind every queue entry with the given IDs. +// +// The Queue must be locked when calling this. +func (d *Queue) Split(ids ...QueueEntryID) { + for _, id := range ids { + if i := slices.IndexFunc(d.Documents, func(e QueueEntry) bool { return e.ID == id }); i >= 0 && i < len(d.Documents)-1 { + doc := &d.Documents[i] + + switch d.Documents[i+1].QueueEntryData.(type) { + case QueueEntryDataPage: + default: + // If the next entry is not a page, we will not put a separator here. + continue + } + + switch doc.QueueEntryData.(type) { + case QueueEntryDataPage: + d.InsertAt(i+1, QueueEntry{ + ID: NewQueueEntryID(), + QueueEntryData: QueueEntryDataSeparator{}, + }) + } + } + } +} + // QueueEntryByID returns the QueueEntry with the given ID. // // The Queue must be locked when calling this. func (d *Queue) QueueEntryByID(id QueueEntryID) *QueueEntry { - for i := range d.Documents { - document := &d.Documents[i] - if document.ID == id { - return document - } + if i := slices.IndexFunc(d.Documents, func(e QueueEntry) bool { return e.ID == id }); i >= 0 { + return &d.Documents[i] } return nil diff --git a/server-websocket-packets.go b/server-websocket-packets.go index 7363436..88e4c5e 100644 --- a/server-websocket-packets.go +++ b/server-websocket-packets.go @@ -51,7 +51,7 @@ func init() { ServerWebsocketPacketRegister(&ServerWebsocketPacketQueueShiftAt{} // ServerWebsocketPacketQueueShift represents a shift operation on a document queue list. type ServerWebsocketPacketQueueShift struct { - IDs []QueueEntryID `json:"ids"` // IDs of the documents. + IDs []QueueEntryID `json:"ids"` // IDs of the entries. Offset int `json:"offset"` // Shift offset. } @@ -59,8 +59,8 @@ func (s *ServerWebsocketPacketQueueShift) Type() string { return "QueueShift" } func init() { ServerWebsocketPacketRegister(&ServerWebsocketPacketQueueShift{}) } -// ServerWebsocketPacketQueueUpdate represents an update operation of documents in a queue list. -// The receiver should update any of the received documents in their local queue list. +// ServerWebsocketPacketQueueUpdate represents an update operation of entries in a queue list. +// The receiver should update any of the received entries in their local queue list. type ServerWebsocketPacketQueueUpdate struct { Documents []QueueEntry `json:"documents"` } @@ -68,3 +68,13 @@ type ServerWebsocketPacketQueueUpdate struct { func (s *ServerWebsocketPacketQueueUpdate) Type() string { return "QueueUpdate" } func init() { ServerWebsocketPacketRegister(&ServerWebsocketPacketQueueUpdate{}) } + +// ServerWebsocketPacketQueueSplit represents a "add separator" operation in a queue list. +// The split is added behind the given entry IDs. +type ServerWebsocketPacketQueueSplit struct { + IDs []QueueEntryID `json:"ids"` // IDs of the entries. +} + +func (s *ServerWebsocketPacketQueueSplit) Type() string { return "QueueSplit" } + +func init() { ServerWebsocketPacketRegister(&ServerWebsocketPacketQueueSplit{}) } diff --git a/server-websocket.go b/server-websocket.go index 594858c..d1e1fd9 100644 --- a/server-websocket.go +++ b/server-websocket.go @@ -92,6 +92,10 @@ func (s *Server) handleWebSocket(w http.ResponseWriter, r *http.Request) { s.Documents.Lock() s.Documents.Shift(packet.Offset, packet.IDs...) s.Documents.Unlock() + case *ServerWebsocketPacketQueueSplit: + s.Documents.Lock() + s.Documents.Split(packet.IDs...) + s.Documents.Unlock() default: log.Printf("Websocket client %q sent unsupported packet type %T.", conn.LocalAddr(), prototype) return diff --git a/static/js/api.js b/static/js/api.js index e3d8a24..6b45bb8 100644 --- a/static/js/api.js +++ b/static/js/api.js @@ -96,6 +96,20 @@ export class API extends EventTarget { this.#socket.send(JSON.stringify(message)); } + /** + * Sends a document queue split request to the server. + * @param {...number} ids Document ids. + */ + queueSplit(...ids) { + if (this.#socket.readyState !== WebSocket.OPEN) { + return + } + + /** @type {{type: string, payload: import("./model").APIPacketQueueSplit}} */ + const message = { type: "QueueSplit", payload: { ids: ids } }; + this.#socket.send(JSON.stringify(message)); + } + /** * * @param {"GET"|"POST"|"DELETE"|"UPDATE"} method diff --git a/static/js/components/document-queue-entry-page.js b/static/js/components/document-queue-entry-page.js index a588d93..8b09642 100644 --- a/static/js/components/document-queue-entry-page.js +++ b/static/js/components/document-queue-entry-page.js @@ -14,20 +14,46 @@ export class DocumentQueueEntryPage extends LitElement { /** @type {API} */ this.api; /** @type {import('model').APIQueueEntry} */ - this.document; + this.queueEntry; } static styles = css` + :host { + width: 100%; + display: flex; + justify-content: space-between; + gap: 8px; + } + img { width: 128px; } + + #buttons { + display: flex; + flex-direction: column; + justify-content: space-between; + gap: 8px; + } + + button { + padding: 8px; + } `; // @ts-ignore render() { return html` - - This is a document + +
+ This is a document +
+
+ + + + +
`; } } diff --git a/static/js/components/document-queue-entry-separator.js b/static/js/components/document-queue-entry-separator.js index ae7f44b..df42ae2 100644 --- a/static/js/components/document-queue-entry-separator.js +++ b/static/js/components/document-queue-entry-separator.js @@ -14,20 +14,38 @@ export class DocumentQueueEntrySeparator extends LitElement { /** @type {API} */ this.api; /** @type {import('model').APIQueueEntry} */ - this.document; + this.queueEntry; } static styles = css` :host { width: 100%; - background: black; + display: flex; + justify-content: space-between; + gap: 8px; + } + + #buttons { + display: flex; + flex-direction: column; + justify-content: space-between; + gap: 8px; + } + + button { + padding: 8px; } `; // @ts-ignore render() { return html` - +
+
+
+
+ +
`; } } diff --git a/static/js/components/document-queue-entry.js b/static/js/components/document-queue-entry.js index 10f4522..0806898 100644 --- a/static/js/components/document-queue-entry.js +++ b/static/js/components/document-queue-entry.js @@ -61,9 +61,9 @@ export class DocumentQueueEntry extends LitElement { let embeddedElement; switch (this.queueEntry.type) { case "Page": - embeddedElement = html``; break; + embeddedElement = html``; break; case "Separator": - embeddedElement = html``; break; + embeddedElement = html``; break; default: embeddedElement = html`Unsupported entry type!` } @@ -71,7 +71,6 @@ export class DocumentQueueEntry extends LitElement { return html`
-
${embeddedElement} diff --git a/static/js/model.d.ts b/static/js/model.d.ts index 55e581c..1288da2 100644 --- a/static/js/model.d.ts +++ b/static/js/model.d.ts @@ -33,6 +33,10 @@ export type APIPacketQueueShift = { offset: number; } +export type APIPacketQueueSplit = { + ids: number[]; +} + export type APIEvents = { queuedeleteat: CustomEvent; queueinsertat: CustomEvent;