implement canvas freezing
This commit is contained in:
parent
93dc27b17a
commit
3dd4d7e0d0
|
@ -2,6 +2,7 @@ import { Button } from "@nextui-org/react";
|
||||||
import { useAppContext } from "../../contexts/AppContext";
|
import { useAppContext } from "../../contexts/AppContext";
|
||||||
import network from "../../lib/network";
|
import network from "../../lib/network";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
import { toast } from "react-toastify";
|
||||||
|
|
||||||
export const UndoButton = () => {
|
export const UndoButton = () => {
|
||||||
const { undo, config } = useAppContext<true>();
|
const { undo, config } = useAppContext<true>();
|
||||||
|
@ -34,7 +35,21 @@ export const UndoButton = () => {
|
||||||
// ref-ify this?
|
// ref-ify this?
|
||||||
function execUndo() {
|
function execUndo() {
|
||||||
network.socket.emitWithAck("undo").then((data) => {
|
network.socket.emitWithAck("undo").then((data) => {
|
||||||
console.log("undo", data);
|
if (data.success) {
|
||||||
|
console.log("Undo pixel successful");
|
||||||
|
} else {
|
||||||
|
console.log("Undo pixel error", data);
|
||||||
|
switch (data.error) {
|
||||||
|
case "pixel_covered":
|
||||||
|
toast.error("You cannot undo a covered pixel");
|
||||||
|
break;
|
||||||
|
case "unavailable":
|
||||||
|
toast.error("You have no undo available");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
toast.error("Undo error: " + data.error);
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -40,6 +40,7 @@ export interface ClientToServerEvents {
|
||||||
ack: (
|
ack: (
|
||||||
_: PacketAck<
|
_: PacketAck<
|
||||||
Pixel,
|
Pixel,
|
||||||
|
| "canvas_frozen"
|
||||||
| "no_user"
|
| "no_user"
|
||||||
| "invalid_pixel"
|
| "invalid_pixel"
|
||||||
| "pixel_cooldown"
|
| "pixel_cooldown"
|
||||||
|
@ -50,7 +51,12 @@ export interface ClientToServerEvents {
|
||||||
) => void
|
) => void
|
||||||
) => void;
|
) => void;
|
||||||
undo: (
|
undo: (
|
||||||
ack: (_: PacketAck<{}, "no_user" | "unavailable" | "pixel_covered">) => void
|
ack: (
|
||||||
|
_: PacketAck<
|
||||||
|
{},
|
||||||
|
"canvas_frozen" | "no_user" | "unavailable" | "pixel_covered"
|
||||||
|
>
|
||||||
|
) => void
|
||||||
) => void;
|
) => void;
|
||||||
|
|
||||||
subscribe: (topic: Subscription) => void;
|
subscribe: (topic: Subscription) => void;
|
||||||
|
@ -141,6 +147,7 @@ export type PalleteColor = {
|
||||||
|
|
||||||
export type CanvasConfig = {
|
export type CanvasConfig = {
|
||||||
size: [number, number];
|
size: [number, number];
|
||||||
|
frozen: boolean;
|
||||||
zoom: number;
|
zoom: number;
|
||||||
pixel: {
|
pixel: {
|
||||||
maxStack: number;
|
maxStack: number;
|
||||||
|
|
|
@ -136,6 +136,8 @@ Enum AuditLogAction {
|
||||||
BAN_DELETE
|
BAN_DELETE
|
||||||
CANVAS_SIZE
|
CANVAS_SIZE
|
||||||
CANVAS_FILL
|
CANVAS_FILL
|
||||||
|
CANVAS_FREEZE
|
||||||
|
CANVAS_UNFREEZE
|
||||||
}
|
}
|
||||||
|
|
||||||
Ref: Pixel.userId > User.sub
|
Ref: Pixel.userId > User.sub
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
-- AlterEnum
|
||||||
|
-- This migration adds more than one value to an enum.
|
||||||
|
-- With PostgreSQL versions 11 and earlier, this is not possible
|
||||||
|
-- in a single migration. This can be worked around by creating
|
||||||
|
-- multiple migrations, each migration adding only one value to
|
||||||
|
-- the enum.
|
||||||
|
|
||||||
|
|
||||||
|
ALTER TYPE "AuditLogAction" ADD VALUE 'CANVAS_FREEZE';
|
||||||
|
ALTER TYPE "AuditLogAction" ADD VALUE 'CANVAS_UNFREEZE';
|
|
@ -154,6 +154,8 @@ enum AuditLogAction {
|
||||||
BAN_DELETE
|
BAN_DELETE
|
||||||
CANVAS_SIZE
|
CANVAS_SIZE
|
||||||
CANVAS_FILL
|
CANVAS_FILL
|
||||||
|
CANVAS_FREEZE
|
||||||
|
CANVAS_UNFREEZE
|
||||||
}
|
}
|
||||||
|
|
||||||
model AuditLog {
|
model AuditLog {
|
||||||
|
|
|
@ -96,6 +96,49 @@ app.post("/canvas/size", async (req, res) => {
|
||||||
res.send({ success: true, auditLog });
|
res.send({ success: true, auditLog });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get canvas frozen status
|
||||||
|
*/
|
||||||
|
app.get("/canvas/freeze", async (req, res) => {
|
||||||
|
res.send({ success: true, frozen: Canvas.frozen });
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Freeze the canvas
|
||||||
|
*
|
||||||
|
* @header X-Audit
|
||||||
|
*/
|
||||||
|
app.post("/canvas/freeze", async (req, res) => {
|
||||||
|
await Canvas.setFrozen(true);
|
||||||
|
|
||||||
|
const user = (await User.fromAuthSession(req.session.user!))!;
|
||||||
|
const auditLog = AuditLog.Factory(user.sub)
|
||||||
|
.doing("CANVAS_FREEZE")
|
||||||
|
.reason(req.header("X-Audit") || null)
|
||||||
|
.withComment(`Freezed the canvas`)
|
||||||
|
.create();
|
||||||
|
|
||||||
|
res.send({ success: true, auditLog });
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unfreeze the canvas
|
||||||
|
*
|
||||||
|
* @header X-Audit
|
||||||
|
*/
|
||||||
|
app.delete("/canvas/freeze", async (req, res) => {
|
||||||
|
await Canvas.setFrozen(false);
|
||||||
|
|
||||||
|
const user = (await User.fromAuthSession(req.session.user!))!;
|
||||||
|
const auditLog = AuditLog.Factory(user.sub)
|
||||||
|
.doing("CANVAS_UNFREEZE")
|
||||||
|
.reason(req.header("X-Audit") || null)
|
||||||
|
.withComment(`Un-Freezed the canvas`)
|
||||||
|
.create();
|
||||||
|
|
||||||
|
res.send({ success: true, auditLog });
|
||||||
|
});
|
||||||
|
|
||||||
app.put("/canvas/heatmap", async (req, res) => {
|
app.put("/canvas/heatmap", async (req, res) => {
|
||||||
try {
|
try {
|
||||||
await Canvas.generateHeatmap();
|
await Canvas.generateHeatmap();
|
||||||
|
|
|
@ -13,14 +13,17 @@ class Canvas {
|
||||||
* Size of the canvas
|
* Size of the canvas
|
||||||
*/
|
*/
|
||||||
private canvasSize: [width: number, height: number];
|
private canvasSize: [width: number, height: number];
|
||||||
|
private isFrozen: boolean;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.canvasSize = [100, 100];
|
this.canvasSize = [100, 100];
|
||||||
|
this.isFrozen = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
getCanvasConfig(): CanvasConfig {
|
getCanvasConfig(): CanvasConfig {
|
||||||
return {
|
return {
|
||||||
size: this.canvasSize,
|
size: this.canvasSize,
|
||||||
|
frozen: this.isFrozen,
|
||||||
zoom: 7,
|
zoom: 7,
|
||||||
pixel: {
|
pixel: {
|
||||||
cooldown: 10,
|
cooldown: 10,
|
||||||
|
@ -33,6 +36,34 @@ class Canvas {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get frozen() {
|
||||||
|
return this.isFrozen;
|
||||||
|
}
|
||||||
|
|
||||||
|
async setFrozen(frozen: boolean) {
|
||||||
|
this.isFrozen = frozen;
|
||||||
|
|
||||||
|
await prisma.setting.upsert({
|
||||||
|
where: { key: "canvas.frozen" },
|
||||||
|
create: {
|
||||||
|
key: "canvas.frozen",
|
||||||
|
value: JSON.stringify(frozen),
|
||||||
|
},
|
||||||
|
update: {
|
||||||
|
key: "canvas.frozen",
|
||||||
|
value: JSON.stringify(frozen),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (SocketServer.instance) {
|
||||||
|
SocketServer.instance.broadcastConfig();
|
||||||
|
} else {
|
||||||
|
Logger.warn(
|
||||||
|
"[Canvas#setFrozen] SocketServer is not instantiated, cannot broadcast config"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Change size of the canvas
|
* Change size of the canvas
|
||||||
*
|
*
|
||||||
|
|
|
@ -26,6 +26,17 @@ export const loadSettings = async (frozen = false) => {
|
||||||
Logger.warn("Setting canvas.size is not set, did you run init_settings?");
|
Logger.warn("Setting canvas.size is not set, did you run init_settings?");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// canvas frozen
|
||||||
|
const canvasFrozen = await prisma.setting.findFirst({
|
||||||
|
where: { key: "canvas.frozen" },
|
||||||
|
});
|
||||||
|
if (canvasFrozen) {
|
||||||
|
const data = JSON.parse(canvasFrozen.value);
|
||||||
|
Logger.info(`Canvas frozen loaded as ${data}`);
|
||||||
|
|
||||||
|
Canvas.setFrozen(data);
|
||||||
|
}
|
||||||
|
|
||||||
Logger.info(
|
Logger.info(
|
||||||
"Settings loaded into memory, waiting for side effects to finish..."
|
"Settings loaded into memory, waiting for side effects to finish..."
|
||||||
);
|
);
|
||||||
|
|
|
@ -233,6 +233,11 @@ export class SocketServer {
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on("place", async (pixel, bypassCooldown, ack) => {
|
socket.on("place", async (pixel, bypassCooldown, ack) => {
|
||||||
|
if (getClientConfig().canvas.frozen) {
|
||||||
|
ack({ success: false, error: "canvas_frozen" });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
ack({ success: false, error: "no_user" });
|
ack({ success: false, error: "no_user" });
|
||||||
return;
|
return;
|
||||||
|
@ -317,6 +322,11 @@ export class SocketServer {
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on("undo", async (ack) => {
|
socket.on("undo", async (ack) => {
|
||||||
|
if (getClientConfig().canvas.frozen) {
|
||||||
|
ack({ success: false, error: "canvas_frozen" });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
ack({ success: false, error: "no_user" });
|
ack({ success: false, error: "no_user" });
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -14,6 +14,10 @@ async function main() {
|
||||||
height: 100,
|
height: 100,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: "canvas.frozen",
|
||||||
|
defaultValue: false,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
for (const setting of SETTINGS) {
|
for (const setting of SETTINGS) {
|
||||||
|
|
Loading…
Reference in New Issue