Skip to content

Commit 0ea81a7

Browse files
committed
chore: demo update
1 parent b586ca9 commit 0ea81a7

File tree

12 files changed

+213
-169
lines changed

12 files changed

+213
-169
lines changed

ui/src/app/api/messages/batch/route.ts

+3-4
Original file line numberDiff line numberDiff line change
@@ -104,16 +104,15 @@ export async function GET(req: Request, res: Response) {
104104

105105
try {
106106
// Process 10k events at a time.
107-
for (const line of eventBuffer.splice(0, 10000)) {
107+
const batch = eventBuffer.splice(0, 10000);
108+
for (const line of batch) {
108109
const data: IServerMessage = JSON.parse(line);
109110
processMessage(data, aggregatedData);
110111
}
111112

112-
const nextTime = eventBuffer.length > 0 ? JSON.parse(eventBuffer[0]).time / 1_000_000 : maxTime;
113-
const progress = nextTime / maxTime;
114113
const serializedData = {
115114
...aggregatedData,
116-
progress,
115+
progress: JSON.parse(batch[batch.length - 1]).time / 1_000_000,
117116
nodes: Array.from(aggregatedData.nodes.entries()),
118117
};
119118

ui/src/app/constants.ts

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const DEFAULT_SCALE = 6;

ui/src/app/page.tsx

+1-49
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { GraphWrapper } from "@/components/Graph/GraphWapper";
2-
import Image from "next/image";
32
import { getSetSimulationMaxTime, getSimulationTopography } from "./queries";
43

54
export default async function Home() {
@@ -10,56 +9,9 @@ export default async function Home() {
109

1110
return (
1211
<div>
13-
<main className="flex flex-col gap-8 row-start-2 items-center sm:items-start">
12+
<main className="flex flex-col gap-8 row-start-2 items-center sm:items-start overflow-hidden">
1413
<GraphWrapper maxTime={maxTime} topography={topography}/>
1514
</main>
16-
<footer className="row-start-3 flex gap-6 flex-wrap items-center justify-center">
17-
<a
18-
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
19-
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
20-
target="_blank"
21-
rel="noopener noreferrer"
22-
>
23-
<Image
24-
aria-hidden
25-
src="/file.svg"
26-
alt="File icon"
27-
width={16}
28-
height={16}
29-
/>
30-
Learn
31-
</a>
32-
<a
33-
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
34-
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
35-
target="_blank"
36-
rel="noopener noreferrer"
37-
>
38-
<Image
39-
aria-hidden
40-
src="/window.svg"
41-
alt="Window icon"
42-
width={16}
43-
height={16}
44-
/>
45-
Examples
46-
</a>
47-
<a
48-
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
49-
href="https://nextjs.org?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
50-
target="_blank"
51-
rel="noopener noreferrer"
52-
>
53-
<Image
54-
aria-hidden
55-
src="/globe.svg"
56-
alt="Globe icon"
57-
width={16}
58-
height={16}
59-
/>
60-
Go to nextjs.org →
61-
</a>
62-
</footer>
6315
</div>
6416
);
6517
}

ui/src/components/Graph/GraphWapper.tsx

+13-13
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ import { GraphContextProvider } from "@/contexts/GraphContext/GraphContextProvid
44
import { FC } from "react";
55
import { Canvas } from "./modules/Canvas";
66
import { Controls } from "./modules/Controls";
7-
import { Slider } from "./modules/Slider";
7+
import { NodeStats } from "./modules/NodeStats";
8+
import { Progress } from "./modules/Slider";
89
import { Stats } from "./modules/Stats";
910
import { IServerNodeMap } from "./types";
1011

@@ -19,19 +20,18 @@ export const GraphWrapper: FC<IGraphWrapperProps> = ({
1920
}) => {
2021
return (
2122
<GraphContextProvider maxTime={maxTime} topography={topography}>
22-
<div className="container mx-auto">
23-
<div className="flex items-center justify-center gap-4 my-4 max-w-3xl mx-auto">
24-
<Slider />
25-
<Controls />
26-
</div>
27-
28-
<div className="flex items-center justify-between gap-4">
23+
<div className="flex flex-col items-center justify-between gap-4 z-10 absolute left-10 top-10">
24+
<Stats />
25+
</div>
26+
<div className="flex items-center justify-between gap-4 relative h-screen w-screen">
27+
<div className="w-full h-full relative">
2928
<Canvas />
30-
<div className="flex flex-col w-1/3 items-center justify-between gap-4">
31-
<div className="w-full h-[400px]">
32-
<Stats />
33-
{/* <ChartTransactionsSent /> */}
34-
</div>
29+
<NodeStats />
30+
</div>
31+
<div className="absolute bottom-12 w-full">
32+
<div className="flex border-2 rounded-md p-4 border-gray-200 items-end justify-center gap-4 my-4 max-w-3xl mx-auto bg-white/80 backdrop-blur-sm">
33+
<Controls />
34+
<Progress />
3535
</div>
3636
</div>
3737
</div>

ui/src/components/Graph/hooks/useHandlers.ts

+24-16
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@ import {
33
useGraphContext,
44
} from "@/contexts/GraphContext/context";
55
import { useCallback } from "react";
6-
import { getOffsetCoordinates } from "../utils";
76

7+
import { DEFAULT_SCALE } from "@/app/constants";
8+
import { getOffsetCoordinates } from "../utils";
89

910
export const useHandlers = () => {
1011
const {
@@ -54,27 +55,34 @@ export const useHandlers = () => {
5455
context.moveTo(nodeStart.fx, nodeStart.fy);
5556
context.lineTo(nodeEnd.fx, nodeEnd.fy);
5657
context.strokeStyle = "#ddd";
58+
59+
if (link.source === Number(currentNode) || link.target === Number(currentNode)) {
60+
context.strokeStyle = "black";
61+
}
62+
5763
context.lineWidth = Math.min((0.2 / canvasScale) * 6, 0.2);
5864
context.stroke();
5965
});
6066

6167
// Draw the nodes
6268
topography.nodes.forEach((node) => {
6369
context.beginPath();
64-
context.arc(node.fx, node.fy, Math.min((1 / canvasScale) * 6, 1), 0, 2 * Math.PI);
65-
context.fillStyle = node.data.stake ? "green" : "blue";
70+
context.arc(
71+
node.fx,
72+
node.fy,
73+
Math.min((1 / canvasScale) * 6, 1),
74+
0,
75+
2 * Math.PI,
76+
);
77+
context.fillStyle = node.data.stake ? "#DC53DE" : "blue";
6678
context.stroke();
67-
context.lineWidth = Math.min((1 / canvasScale) * 6, 1);
68-
69-
const hasData = aggregatedData.nodes.has(node.id.toString());
70-
if (hasData) {
71-
context.strokeStyle = "red";
72-
} else {
73-
context.strokeStyle = "black";
74-
}
79+
context.lineWidth = Math.min((0.5 / canvasScale) * 6, 0.5);
80+
context.strokeStyle = "black";
7581

7682
if (currentNode === node.id.toString()) {
77-
context.fillStyle = "red";
83+
context.fillStyle = "blue";
84+
} else if (!node.data.stake) {
85+
context.fillStyle = "gray"
7886
}
7987

8088
context.fill();
@@ -89,7 +97,7 @@ export const useHandlers = () => {
8997
currentNode,
9098
canvasOffsetX,
9199
canvasOffsetY,
92-
canvasScale
100+
canvasScale,
93101
]);
94102

95103
const handleResetSim = useCallback(() => {
@@ -104,7 +112,7 @@ export const useHandlers = () => {
104112
topography,
105113
width,
106114
height,
107-
canvasScale,
115+
4,
108116
);
109117

110118
dispatch({
@@ -114,12 +122,12 @@ export const useHandlers = () => {
114122
aggregatedData: defaultAggregatedData,
115123
canvasOffsetX: offsetX,
116124
canvasOffsetY: offsetY,
117-
canvasScale: 4
125+
canvasScale: DEFAULT_SCALE,
118126
},
119127
});
120128

121129
drawTopography();
122-
}, []);
130+
}, [topography]);
123131

124132
return {
125133
handleResetSim,

ui/src/components/Graph/modules/Canvas.tsx

+43-27
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,19 @@ import { getOffsetCoordinates, isClickOnNode } from "../utils";
99
export const Canvas: FC = () => {
1010
const { drawTopography } = useHandlers();
1111
const {
12-
state: { canvasRef, canvasOffsetX, canvasOffsetY, canvasScale, topography },
12+
state: {
13+
canvasRef,
14+
canvasOffsetX,
15+
canvasOffsetY,
16+
canvasScale,
17+
topography,
18+
currentNode,
19+
},
1320
dispatch,
1421
} = useGraphContext();
1522
const isDragging = useRef(false);
1623
const dragStart = useRef({ x: 0, y: 0 });
24+
const pointerCapture = useRef<number>();
1725

1826
useEffect(() => {
1927
const canvas = canvasRef.current;
@@ -29,7 +37,7 @@ export const Canvas: FC = () => {
2937
height,
3038
canvasScale,
3139
);
32-
40+
3341
dispatch({
3442
type: "SET_CANVAS_PROPS",
3543
payload: {
@@ -40,17 +48,16 @@ export const Canvas: FC = () => {
4048
}, []);
4149

4250
const handleClick = useCallback(
43-
(ev: MouseEvent) => {
51+
(ev: PointerEvent) => {
4452
const canvas = canvasRef.current;
45-
if (!canvas || ev.target !== canvas || isDragging.current) {
53+
if (!canvas || ev.target !== canvas) {
4654
return;
4755
}
4856

4957
const rect = canvas.getBoundingClientRect();
5058
const x = ev.clientX - rect.left;
5159
const y = ev.clientY - rect.top;
52-
console.log(canvasScale);
53-
const { node } = isClickOnNode(
60+
const { node, clicked } = isClickOnNode(
5461
x,
5562
y,
5663
topography,
@@ -60,9 +67,11 @@ export const Canvas: FC = () => {
6067
canvasScale,
6168
);
6269

63-
dispatch({ type: "SET_CURRENT_NODE", payload: node });
70+
if (clicked && node) {
71+
dispatch({ type: "SET_CURRENT_NODE", payload: currentNode === node ? undefined : node });
72+
}
6473
},
65-
[canvasScale, canvasOffsetX, canvasOffsetY],
74+
[canvasScale, currentNode, canvasOffsetX, canvasOffsetY],
6675
);
6776

6877
const handleWheel = useCallback(
@@ -95,17 +104,19 @@ export const Canvas: FC = () => {
95104
[canvasOffsetX, canvasOffsetY],
96105
);
97106

98-
const handleMouseDown = useCallback((ev: MouseEvent) => {
107+
const handlePointerDown = useCallback((ev: PointerEvent) => {
99108
const canvas = canvasRef.current;
100-
if (!canvas || ev.target !== canvas) {
109+
if (!canvas || ev.target !== canvas || ev.button !== 0) {
101110
return;
102111
}
103112

104113
isDragging.current = true;
114+
canvas.setPointerCapture(ev.pointerId);
115+
pointerCapture.current = ev.pointerId;
105116
dragStart.current = { x: ev.clientX, y: ev.clientY };
106117
}, []);
107118

108-
const handleMouseMove = useCallback((ev: MouseEvent) => {
119+
const handlePointerMove = useCallback((ev: PointerEvent) => {
109120
const canvas = canvasRef.current;
110121
if (!canvas || ev.target !== canvas || !isDragging.current) {
111122
return;
@@ -129,40 +140,45 @@ export const Canvas: FC = () => {
129140
dragStart.current = { x: ev.clientX, y: ev.clientY };
130141
}, []);
131142

132-
const handleMouseUp = useCallback((ev: MouseEvent) => {
143+
const handlePointerUp = useCallback((ev: PointerEvent) => {
133144
const canvas = canvasRef.current;
134-
if (!canvas || ev.target !== canvas) {
145+
if (!canvas || ev.target !== canvas || ev.button !== 0) {
135146
return;
136147
}
137148

138149
isDragging.current = false;
150+
pointerCapture.current = undefined;
151+
canvas.releasePointerCapture(ev.pointerId);
139152
}, []);
140153

141154
useEffect(() => {
142155
drawTopography();
143-
144-
document.addEventListener("mousedown", handleMouseDown);
145-
document.addEventListener("mousemove", handleMouseMove);
146-
document.addEventListener("mouseup", handleMouseUp);
147-
document.addEventListener("click", handleClick);
148-
document.addEventListener("wheel", handleWheel);
156+
157+
const canvas = canvasRef.current;
158+
if (!canvas) {
159+
return;
160+
}
161+
162+
canvas.addEventListener("pointerdown", handlePointerDown);
163+
canvas.addEventListener("pointermove", handlePointerMove);
164+
canvas.addEventListener("pointerup", handlePointerUp);
165+
canvas.addEventListener("pointerup", handleClick);
166+
canvas.addEventListener("wheel", handleWheel);
149167

150168
const redraw = debounce(drawTopography, 100);
151169
document.addEventListener("resize", redraw);
152170

153171
return () => {
154172
document.removeEventListener("resize", redraw);
155-
document.removeEventListener("mousedown", handleMouseDown);
156-
document.removeEventListener("mousemove", handleMouseMove);
157-
document.removeEventListener("mouseup", handleMouseUp);
158-
document.removeEventListener("click", handleClick);
159-
document.removeEventListener("wheel", handleWheel);
173+
canvas.removeEventListener("pointerdown", handlePointerDown);
174+
canvas.removeEventListener("pointermove", handlePointerMove);
175+
canvas.removeEventListener("pointerup", handlePointerUp);
176+
canvas.removeEventListener("pointerup", handleClick);
177+
canvas.removeEventListener("wheel", handleWheel);
160178
};
161179
}, [drawTopography, handleClick, handleWheel]);
162180

163181
return (
164-
<div className="h-[80vh] border-2 border-gray-200 rounded mb-8 w-2/3">
165-
<canvas ref={canvasRef} />
166-
</div>
182+
<canvas ref={canvasRef} />
167183
);
168184
};

ui/src/components/Graph/modules/Controls.tsx

+5-5
Original file line numberDiff line numberDiff line change
@@ -8,21 +8,21 @@ export const Controls: FC = memo(() => {
88
const { startStream, streaming } = useStreamMessagesHandler();
99

1010
return (
11-
<>
11+
<div className="min-w-[200px] flex items-center justify-end gap-4">
1212
<button
13-
className="bg-blue-500 text-white rounded-md px-4 py-2"
13+
className="bg-[blue] text-white rounded-md px-4 py-2"
1414
onClick={startStream}
1515
disabled={streaming}
1616
>
17-
{streaming ? "Aggregating..." : "Aggregate"}
17+
{streaming ? "Running..." : "Run Sim"}
1818
</button>
1919
<button
2020
disabled={streaming}
21-
className="bg-blue-500 text-white w-[80px] rounded-md px-4 py-2"
21+
className="bg-gray-400 text-white w-[80px] rounded-md px-4 py-2"
2222
onClick={handleResetSim}
2323
>
2424
Reset
2525
</button>
26-
</>
26+
</div>
2727
);
2828
});

0 commit comments

Comments
 (0)