Merge branch 'placed-pixels' into 'main'

Pixel pulses & whois display name fix

See merge request sc07/canvas!21
This commit is contained in:
Grant 2024-07-15 22:30:13 +00:00
commit 95b61fa050
9 changed files with 109 additions and 5 deletions

View File

@ -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 />

View File

@ -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,

View File

@ -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>
); );

View File

@ -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>;
};

View File

@ -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,

View File

@ -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 }}

View File

@ -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,

View File

@ -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";

View File

@ -227,6 +227,7 @@ export type AuthSession = {
}; };
user: { user: {
username: string; username: string;
display_name?: string;
picture_url?: string; picture_url?: string;
}; };
}; };