Merge branch 'placed-pixels' into 'main'
Pixel pulses & whois display name fix See merge request sc07/canvas!21
This commit is contained in:
commit
95b61fa050
|
@ -13,6 +13,7 @@ import { KeybindManager } from "../lib/keybinds";
|
||||||
import { BlankOverlay } from "./Overlay/BlankOverlay";
|
import { BlankOverlay } from "./Overlay/BlankOverlay";
|
||||||
import { HeatmapOverlay } from "./Overlay/HeatmapOverlay";
|
import { HeatmapOverlay } from "./Overlay/HeatmapOverlay";
|
||||||
import { useTemplateContext } from "../contexts/TemplateContext";
|
import { useTemplateContext } from "../contexts/TemplateContext";
|
||||||
|
import { PixelPulses } from "./Overlay/PixelPulses";
|
||||||
|
|
||||||
export const CanvasWrapper = () => {
|
export const CanvasWrapper = () => {
|
||||||
const { config } = useAppContext();
|
const { config } = useAppContext();
|
||||||
|
@ -23,6 +24,7 @@ export const CanvasWrapper = () => {
|
||||||
<PanZoomWrapper>
|
<PanZoomWrapper>
|
||||||
<BlankOverlay />
|
<BlankOverlay />
|
||||||
<HeatmapOverlay />
|
<HeatmapOverlay />
|
||||||
|
<PixelPulses />
|
||||||
{config && <Template />}
|
{config && <Template />}
|
||||||
<CanvasInner />
|
<CanvasInner />
|
||||||
<Cursor />
|
<Cursor />
|
||||||
|
|
|
@ -7,7 +7,7 @@ export const User = () => {
|
||||||
return user ? (
|
return user ? (
|
||||||
<Card>
|
<Card>
|
||||||
<UserElement
|
<UserElement
|
||||||
name={user.user.username}
|
name={user.user.display_name || user.user.username}
|
||||||
description={user.service.instance.hostname}
|
description={user.service.instance.hostname}
|
||||||
avatarProps={{
|
avatarProps={{
|
||||||
showFallback: true,
|
showFallback: true,
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { Slider, Spinner, Switch } from "@nextui-org/react";
|
||||||
import { useAppContext } from "../../contexts/AppContext";
|
import { useAppContext } from "../../contexts/AppContext";
|
||||||
|
|
||||||
export const OverlaySettings = () => {
|
export const OverlaySettings = () => {
|
||||||
const { blankOverlay, setBlankOverlay, heatmapOverlay, setHeatmapOverlay } =
|
const { blankOverlay, setBlankOverlay, heatmapOverlay, setHeatmapOverlay, pixelPulses, setPixelPulses } =
|
||||||
useAppContext();
|
useAppContext();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -56,6 +56,15 @@ export const OverlaySettings = () => {
|
||||||
getValue={(v) => (v as number) * 100 + "%"}
|
getValue={(v) => (v as number) * 100 + "%"}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
<Switch
|
||||||
|
isSelected={pixelPulses}
|
||||||
|
onValueChange={(v) =>
|
||||||
|
setPixelPulses(v)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
New Pixel Pulses
|
||||||
|
</Switch>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -0,0 +1,59 @@
|
||||||
|
import { CSSProperties, useEffect, useState } from "react";
|
||||||
|
import { useAppContext } from "../../contexts/AppContext";
|
||||||
|
import network from "../../lib/network";
|
||||||
|
import { Pixel } from "@sc07-canvas/lib/src/net";
|
||||||
|
import { Canvas } from "../../lib/canvas";
|
||||||
|
|
||||||
|
export const PixelPulses = () => {
|
||||||
|
const { pixelPulses } = useAppContext();
|
||||||
|
const [pulses, setPulses] = useState<JSX.Element[]>([]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
function handlePixel({ x, y, color }: Pixel) {
|
||||||
|
if (!pixelPulses) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const paletteColor = Canvas.instance?.Pallete.getColor(color);
|
||||||
|
|
||||||
|
const pulseStyle: CSSProperties = {
|
||||||
|
position: "absolute",
|
||||||
|
zIndex: "100",
|
||||||
|
left: x + "px",
|
||||||
|
top: y + "px",
|
||||||
|
width: "50px",
|
||||||
|
height: "50px",
|
||||||
|
border: `1px solid #${paletteColor?.hex || "000"}`, // default to black in the case it fails to load, but that shouldn't happen
|
||||||
|
borderRadius: "100px",
|
||||||
|
transform: "translate(-24.5px, -24.5px)",
|
||||||
|
animationName: "pixel-pulse",
|
||||||
|
animationTimingFunction: "ease-in-out",
|
||||||
|
animationDuration: "2s",
|
||||||
|
animationFillMode: "forwards",
|
||||||
|
};
|
||||||
|
|
||||||
|
// used in the case of two pixels coming through for the same position
|
||||||
|
// rare, but causes issues with react
|
||||||
|
// even if the pixels are close to eachother, the ms will be different
|
||||||
|
const timestamp = Date.now();
|
||||||
|
|
||||||
|
const pulseElement = (
|
||||||
|
<div key={`${x}-${y}-${timestamp}`} style={pulseStyle}></div>
|
||||||
|
);
|
||||||
|
|
||||||
|
setPulses((prevPulses) => [...prevPulses, pulseElement]);
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
setPulses((prevPulses) => prevPulses.slice(1)); // Remove the oldest pulse after 3700ms
|
||||||
|
}, 2500);
|
||||||
|
}
|
||||||
|
|
||||||
|
network.on("pixel", handlePixel);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
network.off("pixel", handlePixel);
|
||||||
|
};
|
||||||
|
}, [pixelPulses]);
|
||||||
|
|
||||||
|
return <div>{pulses}</div>;
|
||||||
|
};
|
|
@ -2,7 +2,7 @@ import { faMessage, faWarning } from "@fortawesome/free-solid-svg-icons";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
import { Avatar, Button, Link, Spinner, User } from "@nextui-org/react";
|
import { Avatar, Button, Link, Spinner, User } from "@nextui-org/react";
|
||||||
import { ClientConfig } from "@sc07-canvas/lib/src/net";
|
import { ClientConfig } from "@sc07-canvas/lib/src/net";
|
||||||
import { MouseEvent, useEffect, useState } from "react";
|
import { MouseEvent, useEffect, useMemo, useState } from "react";
|
||||||
import { toast } from "react-toastify";
|
import { toast } from "react-toastify";
|
||||||
import { useAppContext } from "../../contexts/AppContext";
|
import { useAppContext } from "../../contexts/AppContext";
|
||||||
|
|
||||||
|
@ -72,11 +72,26 @@ export const UserCard = ({ user }: { user: IUser }) => {
|
||||||
setProfile(user.sub);
|
setProfile(user.sub);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const name = useMemo(() => {
|
||||||
|
if (!user || !user.sub) {
|
||||||
|
return 'Unknown'
|
||||||
|
}
|
||||||
|
|
||||||
|
const regex = /^(.*)@/;
|
||||||
|
const match = user.sub.match(regex);
|
||||||
|
|
||||||
|
if (match) {
|
||||||
|
return match[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'Unknown'
|
||||||
|
}, [user])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-1">
|
<div className="flex flex-col gap-1">
|
||||||
<div className="flex flex-row space-between p-2">
|
<div className="flex flex-row space-between p-2">
|
||||||
<User
|
<User
|
||||||
name={user?.display_name || 'Unknown'}
|
name={user?.display_name || name}
|
||||||
description={user?.sub || 'Unknown'}
|
description={user?.sub || 'Unknown'}
|
||||||
avatarProps={{
|
avatarProps={{
|
||||||
showFallback: true,
|
showFallback: true,
|
||||||
|
|
|
@ -15,7 +15,7 @@ export const SidebarBase = ({children, shown, icon, setSidebarShown, title, desc
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<motion.div
|
<motion.div
|
||||||
className={`absolute w-screen h-screen z-50 left-0 top-0 bg-black`}
|
className={`absolute w-screen h-screen z-50 left-0 top-0 bg-black pointer-events-none`}
|
||||||
initial={{ opacity: 0, visibility: 'hidden' }}
|
initial={{ opacity: 0, visibility: 'hidden' }}
|
||||||
animate={{ opacity: shown ? 0.25 : 0, visibility: shown ? 'visible' : 'hidden' }}
|
animate={{ opacity: shown ? 0.25 : 0, visibility: shown ? 'visible' : 'hidden' }}
|
||||||
transition={{ type: 'spring', stiffness: 50 }}
|
transition={{ type: 'spring', stiffness: 50 }}
|
||||||
|
|
|
@ -38,6 +38,8 @@ interface IAppContext {
|
||||||
setBlankOverlay: React.Dispatch<React.SetStateAction<IMapOverlay>>;
|
setBlankOverlay: React.Dispatch<React.SetStateAction<IMapOverlay>>;
|
||||||
heatmapOverlay: IMapOverlay;
|
heatmapOverlay: IMapOverlay;
|
||||||
setHeatmapOverlay: React.Dispatch<React.SetStateAction<IMapOverlay>>;
|
setHeatmapOverlay: React.Dispatch<React.SetStateAction<IMapOverlay>>;
|
||||||
|
pixelPulses: boolean;
|
||||||
|
setPixelPulses: (state: boolean) => void
|
||||||
|
|
||||||
profile?: string; // sub
|
profile?: string; // sub
|
||||||
setProfile: (v?: string) => void;
|
setProfile: (v?: string) => void;
|
||||||
|
@ -123,6 +125,7 @@ export const AppContext = ({ children }: PropsWithChildren) => {
|
||||||
opacity: 1,
|
opacity: 1,
|
||||||
loading: false,
|
loading: false,
|
||||||
});
|
});
|
||||||
|
const [pixelPulses, setPixelPulses] = useState<boolean>(false)
|
||||||
|
|
||||||
const [profile, setProfile] = useState<string>();
|
const [profile, setProfile] = useState<string>();
|
||||||
|
|
||||||
|
@ -229,6 +232,8 @@ export const AppContext = ({ children }: PropsWithChildren) => {
|
||||||
setBlankOverlay,
|
setBlankOverlay,
|
||||||
heatmapOverlay,
|
heatmapOverlay,
|
||||||
setHeatmapOverlay,
|
setHeatmapOverlay,
|
||||||
|
pixelPulses,
|
||||||
|
setPixelPulses,
|
||||||
profile,
|
profile,
|
||||||
setProfile,
|
setProfile,
|
||||||
infoSidebar,
|
infoSidebar,
|
||||||
|
|
|
@ -201,6 +201,19 @@ main {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@keyframes pixel-pulse {
|
||||||
|
from {
|
||||||
|
width: 50px;
|
||||||
|
height: 50px;
|
||||||
|
transform: translate(-24.5px, -24.5px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
width: 10px;
|
||||||
|
height: 10px;
|
||||||
|
transform: translate(-4.5px, -4.5px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@import "./components/Toolbar/Palette.scss";
|
@import "./components/Toolbar/Palette.scss";
|
||||||
@import "./components/Templating/Template.scss";
|
@import "./components/Templating/Template.scss";
|
||||||
@import "./board.scss";
|
@import "./board.scss";
|
||||||
|
|
|
@ -227,6 +227,7 @@ export type AuthSession = {
|
||||||
};
|
};
|
||||||
user: {
|
user: {
|
||||||
username: string;
|
username: string;
|
||||||
|
display_name?: string;
|
||||||
picture_url?: string;
|
picture_url?: string;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue