feat: processing images and notification centre
This commit is contained in:
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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 (
|
||||
<svg
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<circle
|
||||
cx="12"
|
||||
cy="12"
|
||||
r="10"
|
||||
fill="none"
|
||||
stroke-width="3"
|
||||
class="stroke-amber-400"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
case "complete":
|
||||
return (
|
||||
<svg
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<circle
|
||||
cx="12"
|
||||
cy="12"
|
||||
r="10"
|
||||
fill="none"
|
||||
stroke-width="3"
|
||||
class="stroke-emerald-400"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export const ProcessingImages: Component = () => {
|
||||
const notifications = useNotifications();
|
||||
const notifications = useNotifications();
|
||||
|
||||
return (
|
||||
<div class="w-full">
|
||||
<h2 class="text-xl">Processing Images</h2>
|
||||
<For each={Object.entries(notifications.state.ProcessingImages)}>
|
||||
{([id]) => <img alt="processing" src={`${base}/image/${id}`} />}
|
||||
</For>
|
||||
</div>
|
||||
);
|
||||
return (
|
||||
<Popover>
|
||||
<Popover.Trigger class="w-full flex justify-between rounded-xl bg-slate-800 text-gray-100 px-4 py-2">
|
||||
<p class="text-md">Processing Images</p>
|
||||
<Show
|
||||
when={Object.keys(notifications.state.ProcessingImages).length === 0}
|
||||
fallback={<LoadingCircle status="loading" />}
|
||||
>
|
||||
<LoadingCircle status="complete" />
|
||||
</Show>
|
||||
</Popover.Trigger>
|
||||
<Popover.Content class="w-96 flex flex-col gap-2 bg-slate-800 rounded-xl">
|
||||
<For each={Object.entries(notifications.state.ProcessingImages)}>
|
||||
{([id, _image]) => (
|
||||
<Show when={_image}>
|
||||
{(image) => (
|
||||
<div class="flex gap-2">
|
||||
<img
|
||||
class="w-16 h-16 aspect-square"
|
||||
alt="processing"
|
||||
src={`${base}/image/${id}`}
|
||||
/>
|
||||
<div class="flex flex-col gap-1">
|
||||
<p class="text-slate-100">{image().ImageName}</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</Show>
|
||||
)}
|
||||
</For>
|
||||
</Popover.Content>
|
||||
</Popover>
|
||||
);
|
||||
};
|
||||
|
@ -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<typeof processingImagesValidator>["Status"] | undefined
|
||||
InferOutput<typeof processingImagesValidator> | 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,
|
||||
},
|
||||
]),
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
|
Reference in New Issue
Block a user