add mod menu & ignore place limit (fixes #14)
This commit is contained in:
parent
a35f8ff59b
commit
78d97b52e3
|
@ -17,6 +17,7 @@ import { KeybindModal } from "./KeybindModal";
|
|||
import { ProfileModal } from "./Profile/ProfileModal";
|
||||
import { WelcomeModal } from "./Welcome/WelcomeModal";
|
||||
import { InfoSidebar } from "./Info/InfoSidebar";
|
||||
import { ModModal } from "./Moderation/ModModal";
|
||||
|
||||
const Chat = lazy(() => import("./Chat/Chat"));
|
||||
|
||||
|
@ -148,6 +149,7 @@ const AppInner = () => {
|
|||
|
||||
<ProfileModal />
|
||||
<WelcomeModal />
|
||||
<ModModal />
|
||||
|
||||
<ToastContainer position="top-left" />
|
||||
</>
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
import {
|
||||
Modal,
|
||||
ModalBody,
|
||||
ModalContent,
|
||||
ModalFooter,
|
||||
ModalHeader,
|
||||
Switch,
|
||||
} from "@nextui-org/react";
|
||||
import { useAppContext } from "../../contexts/AppContext";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { KeybindManager } from "../../lib/keybinds";
|
||||
import { Canvas } from "../../lib/canvas";
|
||||
|
||||
export const ModModal = () => {
|
||||
const { showModModal, setShowModModal, hasAdmin } = useAppContext();
|
||||
const [bypassCooldown, setBypassCooldown_] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
setBypassCooldown_(Canvas.instance?.getCooldownBypass() || false);
|
||||
|
||||
const handleKeybind = () => {
|
||||
if (!hasAdmin) {
|
||||
console.warn("Unable to open mod menu; hasAdmin is not set");
|
||||
return;
|
||||
}
|
||||
|
||||
setShowModModal((m) => !m);
|
||||
};
|
||||
|
||||
KeybindManager.on("TOGGLE_MOD_MENU", handleKeybind);
|
||||
|
||||
return () => {
|
||||
KeybindManager.off("TOGGLE_MOD_MENU", handleKeybind);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const setBypassCooldown = useCallback(
|
||||
(value: boolean) => {
|
||||
setBypassCooldown_(value);
|
||||
Canvas.instance?.setCooldownBypass(value);
|
||||
},
|
||||
[setBypassCooldown_]
|
||||
);
|
||||
|
||||
return (
|
||||
<Modal isOpen={showModModal} onOpenChange={setShowModModal}>
|
||||
<ModalContent>
|
||||
{(onClose) => (
|
||||
<>
|
||||
<ModalHeader>Mod Menu</ModalHeader>
|
||||
<ModalBody>
|
||||
<Switch
|
||||
isSelected={bypassCooldown}
|
||||
onValueChange={setBypassCooldown}
|
||||
>
|
||||
Bypass placement cooldown
|
||||
</Switch>
|
||||
</ModalBody>
|
||||
</>
|
||||
)}
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
);
|
||||
};
|
|
@ -1,4 +1,4 @@
|
|||
import {
|
||||
import React, {
|
||||
PropsWithChildren,
|
||||
createContext,
|
||||
useContext,
|
||||
|
@ -43,6 +43,8 @@ interface IAppContext {
|
|||
setProfile: (v?: string) => void;
|
||||
|
||||
hasAdmin: boolean;
|
||||
showModModal: boolean;
|
||||
setShowModModal: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
}
|
||||
|
||||
interface ICanvasPosition {
|
||||
|
@ -119,6 +121,7 @@ export const AppContext = ({ children }: PropsWithChildren) => {
|
|||
const [profile, setProfile] = useState<string>();
|
||||
|
||||
const [hasAdmin, setHasAdmin] = useState(false);
|
||||
const [showModModal, setShowModModal] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
function loadSettings() {
|
||||
|
@ -224,6 +227,8 @@ export const AppContext = ({ children }: PropsWithChildren) => {
|
|||
setProfile,
|
||||
infoSidebar,
|
||||
setInfoSidebar,
|
||||
showModModal,
|
||||
setShowModModal,
|
||||
}}
|
||||
>
|
||||
{!config && (
|
||||
|
|
|
@ -39,6 +39,8 @@ export class Canvas extends EventEmitter<CanvasEvents> {
|
|||
} = {};
|
||||
lastPlace: number | undefined;
|
||||
|
||||
private bypassCooldown = false;
|
||||
|
||||
constructor(canvas: HTMLCanvasElement, PanZoom: PanZoom) {
|
||||
super();
|
||||
Canvas.instance = this;
|
||||
|
@ -99,6 +101,14 @@ export class Canvas extends EventEmitter<CanvasEvents> {
|
|||
return this.PanZoom;
|
||||
}
|
||||
|
||||
setCooldownBypass(value: boolean) {
|
||||
this.bypassCooldown = value;
|
||||
}
|
||||
|
||||
getCooldownBypass() {
|
||||
return this.bypassCooldown;
|
||||
}
|
||||
|
||||
getAllPixels() {
|
||||
let pixels: {
|
||||
x: number;
|
||||
|
@ -252,11 +262,15 @@ export class Canvas extends EventEmitter<CanvasEvents> {
|
|||
// }
|
||||
|
||||
Network.socket
|
||||
.emitWithAck("place", {
|
||||
x,
|
||||
y,
|
||||
color: this.Pallete.getSelectedColor()!.id,
|
||||
})
|
||||
.emitWithAck(
|
||||
"place",
|
||||
{
|
||||
x,
|
||||
y,
|
||||
color: this.Pallete.getSelectedColor()!.id,
|
||||
},
|
||||
this.bypassCooldown
|
||||
)
|
||||
.then((ack) => {
|
||||
if (ack.success) {
|
||||
this.lastPlace = Date.now();
|
||||
|
|
|
@ -49,6 +49,11 @@ const KEYBINDS = enforceObjectType({
|
|||
key: "KeyH",
|
||||
},
|
||||
],
|
||||
TOGGLE_MOD_MENU: [
|
||||
{
|
||||
key: "KeyM",
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
class KeybindManager_ extends EventEmitter<{
|
||||
|
|
|
@ -27,6 +27,7 @@ export interface ServerToClientEvents {
|
|||
export interface ClientToServerEvents {
|
||||
place: (
|
||||
pixel: Pixel,
|
||||
bypassCooldown: boolean,
|
||||
ack: (
|
||||
_: PacketAck<
|
||||
Pixel,
|
||||
|
|
|
@ -193,7 +193,7 @@ export class SocketServer {
|
|||
});
|
||||
});
|
||||
|
||||
socket.on("place", async (pixel, ack) => {
|
||||
socket.on("place", async (pixel, bypassCooldown, ack) => {
|
||||
if (!user) {
|
||||
ack({ success: false, error: "no_user" });
|
||||
return;
|
||||
|
@ -212,7 +212,13 @@ export class SocketServer {
|
|||
// force a user data update
|
||||
await user.update(true);
|
||||
|
||||
if (user.pixelStack < 1) {
|
||||
if (bypassCooldown && !user.isModerator) {
|
||||
// only moderators can do this
|
||||
ack({ success: false, error: "invalid_pixel" });
|
||||
return;
|
||||
}
|
||||
|
||||
if (!bypassCooldown && user.pixelStack < 1) {
|
||||
ack({ success: false, error: "pixel_cooldown" });
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -60,6 +60,8 @@ export class User {
|
|||
this.lastPixelTime = userData.lastPixelTime;
|
||||
this.pixelStack = userData.pixelStack;
|
||||
this.undoExpires = userData.undoExpires || undefined;
|
||||
this.isAdmin = userData.isAdmin;
|
||||
this.isModerator = userData.isModerator;
|
||||
}
|
||||
|
||||
async modifyStack(modifyBy: number): Promise<any> {
|
||||
|
|
Loading…
Reference in New Issue