server now notifies the client when the canvas isn't cached

- added foundation for server-sent alerts (related #53)
This commit is contained in:
Grant 2024-07-02 14:25:55 -06:00
parent 0e97316096
commit 076ff1c942
5 changed files with 110 additions and 3 deletions

View File

@ -0,0 +1,44 @@
/**
* Handle alerts sent by the server (moderation or internal)
*/
import { IAlert } from "@sc07-canvas/lib/src/net";
import { toast } from "react-toastify";
/**
* Handles IAlert outside of react
* @param alert
*/
export const handleAlert = (alert: IAlert) => {
switch (alert.is) {
case "toast":
handleToast(alert);
break;
case "modal":
handleModal(alert);
break;
}
};
export const handleDismiss = (id: string) => {
toast.dismiss(id);
};
const handleToast = (alert: IAlert<"toast">) => {
const Body = (
<>
<b>{alert.title}</b>
{alert.body && <> {alert.body}</>}
</>
);
toast(Body, {
toastId: alert.id,
type: alert.severity,
autoClose: alert.autoDismiss ? 5000 : false,
});
};
const handleModal = (alert: IAlert<"modal">) => {
window.alert("alerts#handleModal triggered, but no implementation exists");
};

View File

@ -9,6 +9,7 @@ import {
Subscription,
} from "@sc07-canvas/lib/src/net";
import { toast } from "react-toastify";
import { handleAlert, handleDismiss } from "./alerts";
export interface INetworkEvents {
connected: () => void;
@ -121,6 +122,9 @@ class Network extends EventEmitter<INetworkEvents> {
this.socket.on("heatmap", (heatmap) => {
this.emit("heatmap", heatmap);
});
this.socket.on("alert", handleAlert);
this.socket.on("alert_dismiss", handleDismiss);
}
subscribe(subscription: Subscription) {

View File

@ -19,6 +19,9 @@ export interface ServerToClientEvents {
color: number
) => void;
alert: (alert: IAlert) => void;
alert_dismiss: (id: string) => void;
/* --- subscribe events --- */
/**
@ -57,6 +60,24 @@ export interface IPosition {
y: number;
}
export type IAlert<Is extends "toast" | "modal" = "toast" | "modal"> = {
is: Is;
action: "system" | "moderation";
id?: string;
title: string;
body?: string;
} & (
| {
is: "toast";
severity: "info" | "success" | "warning" | "error" | "default";
autoDismiss: boolean;
}
| {
is: "modal";
dismissable: boolean;
}
);
// other
export type Pixel = {

View File

@ -166,6 +166,8 @@ class Canvas {
* @returns 1D array of pixel values
*/
async canvasToRedis() {
const now = Date.now();
Logger.info("Starting canvasToRedis...");
const redis = await Redis.getClient();
const dbpixels = await prisma.pixel.findMany({
@ -198,6 +200,11 @@ class Canvas {
await redis.set(Redis.key("canvas"), pixels.join(","), { EX: 60 * 5 });
Logger.info(
"Finished canvasToRedis in " +
((Date.now() - now) / 1000).toFixed(2) +
"s"
);
return pixels;
}
@ -234,6 +241,12 @@ class Canvas {
await redis.set(Redis.key("canvas"), pixels.join(","), { EX: 60 * 5 });
}
async isPixelArrayCached() {
const redis = await Redis.getClient();
return await redis.exists(Redis.key("canvas"));
}
async getPixelsArray() {
const redis = await Redis.getClient();

View File

@ -181,9 +181,34 @@ export class SocketServer {
}
socket.emit("config", getClientConfig());
Canvas.getPixelsArray().then((pixels) => {
socket.emit("canvas", pixels);
});
{
let _clientNotifiedAboutCache = false;
Canvas.isPixelArrayCached().then((cached) => {
if (!cached) {
_clientNotifiedAboutCache = true;
socket.emit("alert", {
id: "canvas_cache_pending",
is: "toast",
action: "system",
severity: "info",
title: "Canvas loading",
body: "Canvas not cached, this may take a couple seconds",
autoDismiss: true,
});
}
});
Canvas.getPixelsArray().then((pixels) => {
socket.emit("canvas", pixels);
socket.emit("alert_dismiss", "canvas_cache_pending");
socket.emit("alert", {
is: "toast",
action: "system",
severity: "success",
title: "Canvas loaded!",
autoDismiss: true,
});
});
}
socket.on("disconnect", () => {
Logger.debug(`Socket ${socket.id} disconnected`);