From 6482a76a5181e09a73d800390f968d318cda7af0 Mon Sep 17 00:00:00 2001 From: John Costa Date: Wed, 2 Jul 2025 14:12:19 +0100 Subject: [PATCH] feat: processing images and notification centre --- backend/events.go | 10 ++- backend/models/image.go | 15 ++-- .../src/notifications/ProcessingImages.tsx | 90 ++++++++++++++++--- frontend/src/notifications/index.ts | 16 +++- 4 files changed, 108 insertions(+), 23 deletions(-) diff --git a/backend/events.go b/backend/events.go index c3cf95e..52dada0 100644 --- a/backend/events.go +++ b/backend/events.go @@ -18,8 +18,9 @@ import ( ) type Notification struct { - ImageID uuid.UUID - Status string + ImageID uuid.UUID + ImageName string + Status string } func ListenNewImageEvents(db *sql.DB, notifier *Notifier[Notification]) { @@ -116,8 +117,9 @@ func ListenProcessingImageStatus(db *sql.DB, images models.ImageModel, notifier logger.Info("Update", "id", imageStringUuid, "status", status) notification := Notification{ - ImageID: processingImage.ImageID, - Status: status, + ImageID: processingImage.ImageID, + ImageName: processingImage.Image.ImageName, + Status: status, } if err := notifier.SendAndCreate(processingImage.UserID.String(), notification); err != nil { diff --git a/backend/models/image.go b/backend/models/image.go index fd02b23..fe9efae 100644 --- a/backend/models/image.go +++ b/backend/models/image.go @@ -3,7 +3,6 @@ package models import ( "context" "database/sql" - "errors" "fmt" "screenmark/screenmark/.gen/haystack/haystack/enum" "screenmark/screenmark/.gen/haystack/haystack/model" @@ -69,16 +68,20 @@ func (m ImageModel) Process(ctx context.Context, userId uuid.UUID, image model.I return userImage, err } -func (m ImageModel) GetToProcess(ctx context.Context, imageId uuid.UUID) (model.UserImagesToProcess, error) { - getToProcessStmt := UserImagesToProcess. - SELECT(UserImagesToProcess.AllColumns). +func (m ImageModel) GetToProcess(ctx context.Context, imageId uuid.UUID) (UserProcessingImage, error) { + getToProcessStmt := SELECT(UserImagesToProcess.AllColumns, Image.ID, Image.ImageName). + FROM( + UserImagesToProcess.INNER_JOIN( + Image, Image.ID.EQ(UserImagesToProcess.ImageID), + ), + ). WHERE(UserImagesToProcess.ID.EQ(UUID(imageId))) - images := []model.UserImagesToProcess{} + images := []UserProcessingImage{} err := getToProcessStmt.QueryContext(ctx, m.dbPool, &images) if len(images) != 1 { - return model.UserImagesToProcess{}, errors.New(fmt.Sprintf("Expected 1, got %d\n", len(images))) + return UserProcessingImage{}, fmt.Errorf("Expected 1, got %d\n", len(images)) } return images[0], err diff --git a/frontend/src/notifications/ProcessingImages.tsx b/frontend/src/notifications/ProcessingImages.tsx index 13b3eb9..9f5faa5 100644 --- a/frontend/src/notifications/ProcessingImages.tsx +++ b/frontend/src/notifications/ProcessingImages.tsx @@ -1,16 +1,86 @@ -import { type Component, For } from "solid-js"; +import { Popover } from "@kobalte/core/popover"; +import { type Component, For, Show } from "solid-js"; import { base } from "../network"; import { useNotifications } from "../ProtectedRoute"; +import { useSearchImageContext } from "../contexts/SearchImageContext"; + +const LoadingCircle: Component<{ status: "loading" | "complete" }> = ( + props, +) => { + switch (props.status) { + case "loading": + return ( + + + + ); + case "complete": + return ( + + + + ); + } +}; export const ProcessingImages: Component = () => { - const notifications = useNotifications(); + const notifications = useNotifications(); - return ( -
-

Processing Images

- - {([id]) => processing} - -
- ); + return ( + + +

Processing Images

+ } + > + + +
+ + + {([id, _image]) => ( + + {(image) => ( +
+ processing +
+

{image().ImageName}

+
+
+ )} +
+ )} +
+
+
+ ); }; diff --git a/frontend/src/notifications/index.ts b/frontend/src/notifications/index.ts index 8b02ac6..0974279 100644 --- a/frontend/src/notifications/index.ts +++ b/frontend/src/notifications/index.ts @@ -15,6 +15,7 @@ import { useSearchImageContext } from "../contexts/SearchImageContext"; const processingImagesValidator = strictObject({ ImageID: pipe(string(), uuid()), + ImageName: string(), Status: union([ literal("not-started"), literal("in-progress"), @@ -25,7 +26,7 @@ const processingImagesValidator = strictObject({ type NotificationState = { ProcessingImages: Record< string, - InferOutput["Status"] | undefined + InferOutput | undefined >; }; @@ -69,7 +70,7 @@ export const Notifications = (onCompleteImage: () => void) => { setState("ProcessingImages", ImageID, undefined); onCompleteImage(); } else { - setState("ProcessingImages", ImageID, Status); + setState("ProcessingImages", ImageID, processingImage.output); } }; @@ -89,7 +90,16 @@ export const Notifications = (onCompleteImage: () => void) => { } upsertImageProcessing( - Object.fromEntries(images.map((i) => [i.ImageID, i.Status])), + Object.fromEntries( + images.map((i) => [ + i.ImageID, + { + ImageID: i.ImageID, + ImageName: i.Image.ImageName, + Status: i.Status, + }, + ]), + ), ); });