ui/scene: fix syncing remote and local state
This commit is contained in:
@@ -304,20 +304,25 @@ const ClanSceneController = (props: RouteSectionProps) => {
|
||||
onCreate={onCreate}
|
||||
clanURI={ctx.clanURI}
|
||||
sceneStore={() => store.sceneData?.[ctx.clanURI]}
|
||||
setMachinePos={(machineId: string, pos: [number, number]) => {
|
||||
setMachinePos={(machineId: string, pos: [number, number] | null) => {
|
||||
console.log("calling setStore", machineId, pos);
|
||||
setStore(
|
||||
produce((s) => {
|
||||
if (!s.sceneData) {
|
||||
s.sceneData = {};
|
||||
}
|
||||
if (!s.sceneData[ctx.clanURI]) {
|
||||
s.sceneData[ctx.clanURI] = {};
|
||||
}
|
||||
if (!s.sceneData[ctx.clanURI][machineId]) {
|
||||
s.sceneData[ctx.clanURI][machineId] = { position: pos };
|
||||
if (!s.sceneData) s.sceneData = {};
|
||||
|
||||
if (!s.sceneData[ctx.clanURI]) s.sceneData[ctx.clanURI] = {};
|
||||
|
||||
if (pos === null) {
|
||||
// Remove the machine entry if pos is null
|
||||
delete s.sceneData[ctx.clanURI][machineId];
|
||||
|
||||
// Optional: cleanup empty clan entries
|
||||
if (Object.keys(s.sceneData[ctx.clanURI]).length === 0) {
|
||||
delete s.sceneData[ctx.clanURI];
|
||||
}
|
||||
} else {
|
||||
s.sceneData[ctx.clanURI][machineId].position = pos;
|
||||
// Set or update the machine position
|
||||
s.sceneData[ctx.clanURI][machineId] = { position: pos };
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -25,50 +25,71 @@ export class MachineManager {
|
||||
machinePositionsSignal: Accessor<SceneData>,
|
||||
machinesQueryResult: MachinesQueryResult,
|
||||
selectedIds: Accessor<Set<string>>,
|
||||
setMachinePos: (id: string, position: [number, number]) => void,
|
||||
setMachinePos: (id: string, position: [number, number] | null) => void,
|
||||
) {
|
||||
this.machinePositionsSignal = machinePositionsSignal;
|
||||
|
||||
this.disposeRoot = createRoot((disposeEffects) => {
|
||||
createEffect(() => {
|
||||
const machines = machinePositionsSignal();
|
||||
|
||||
Object.entries(machines).forEach(([id, data]) => {
|
||||
const machineRepr = new MachineRepr(
|
||||
scene,
|
||||
registry,
|
||||
new THREE.Vector2(data.position[0], data.position[1]),
|
||||
id,
|
||||
selectedIds,
|
||||
);
|
||||
this.machines.set(id, machineRepr);
|
||||
scene.add(machineRepr.group);
|
||||
});
|
||||
renderLoop.requestRender();
|
||||
});
|
||||
|
||||
// Push positions of previously existing machines to the scene
|
||||
// TODO: Maybe we should do this in some post query hook?
|
||||
//
|
||||
// Effect 1: sync query → store (positions)
|
||||
//
|
||||
createEffect(() => {
|
||||
if (!machinesQueryResult.data) return;
|
||||
|
||||
const actualMachines = Object.keys(machinesQueryResult.data);
|
||||
const actualIds = Object.keys(machinesQueryResult.data);
|
||||
const machinePositions = machinePositionsSignal();
|
||||
const placed: Set<string> = machinePositions
|
||||
? new Set(Object.keys(machinePositions))
|
||||
: new Set();
|
||||
|
||||
const nonPlaced = actualMachines.filter((m) => !placed.has(m));
|
||||
|
||||
// Push not explizitly placed machines to the scene
|
||||
// TODO: Make the user place them manually
|
||||
// We just calculate some next free position
|
||||
for (const id of nonPlaced) {
|
||||
console.log("adding", id);
|
||||
const position = this.nextGridPos();
|
||||
|
||||
setMachinePos(id, position);
|
||||
// Remove stale
|
||||
for (const id of Object.keys(machinePositions)) {
|
||||
if (!actualIds.includes(id)) {
|
||||
setMachinePos(id, null);
|
||||
}
|
||||
}
|
||||
|
||||
// Add missing
|
||||
for (const id of actualIds) {
|
||||
if (!machinePositions[id]) {
|
||||
const pos = this.nextGridPos();
|
||||
setMachinePos(id, pos);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
//
|
||||
// Effect 2: sync store → scene
|
||||
//
|
||||
createEffect(() => {
|
||||
const positions = machinePositionsSignal();
|
||||
|
||||
// Remove machines from scene
|
||||
for (const [id, repr] of this.machines) {
|
||||
if (!(id in positions)) {
|
||||
repr.dispose(scene);
|
||||
this.machines.delete(id);
|
||||
}
|
||||
}
|
||||
|
||||
// Add or update machines
|
||||
for (const [id, data] of Object.entries(positions)) {
|
||||
let repr = this.machines.get(id);
|
||||
if (!repr) {
|
||||
repr = new MachineRepr(
|
||||
scene,
|
||||
registry,
|
||||
new THREE.Vector2(data.position[0], data.position[1]),
|
||||
id,
|
||||
selectedIds,
|
||||
);
|
||||
this.machines.set(id, repr);
|
||||
scene.add(repr.group);
|
||||
} else {
|
||||
repr.setPosition(
|
||||
new THREE.Vector2(data.position[0], data.position[1]),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
renderLoop.requestRender();
|
||||
});
|
||||
|
||||
return disposeEffects;
|
||||
|
||||
@@ -121,6 +121,11 @@ export class MachineRepr {
|
||||
});
|
||||
}
|
||||
|
||||
public setPosition(position: THREE.Vector2) {
|
||||
this.group.position.set(position.x, 0, position.y);
|
||||
renderLoop.requestRender();
|
||||
}
|
||||
|
||||
private createCubeBase(
|
||||
color: THREE.ColorRepresentation,
|
||||
emissive: THREE.ColorRepresentation,
|
||||
@@ -154,6 +159,14 @@ export class MachineRepr {
|
||||
|
||||
this.geometry.dispose();
|
||||
this.material.dispose();
|
||||
for (const child of this.cubeMesh.children) {
|
||||
if (child instanceof THREE.Mesh)
|
||||
(child.material as THREE.Material).dispose();
|
||||
|
||||
if (child instanceof CSS2DObject) child.element.remove();
|
||||
|
||||
if (child instanceof THREE.Object3D) child.remove();
|
||||
}
|
||||
(this.baseMesh.material as THREE.Material).dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ export function CubeScene(props: {
|
||||
selectedIds: Accessor<Set<string>>;
|
||||
onSelect: (v: Set<string>) => void;
|
||||
sceneStore: Accessor<SceneData>;
|
||||
setMachinePos: (machineId: string, pos: [number, number]) => void;
|
||||
setMachinePos: (machineId: string, pos: [number, number] | null) => void;
|
||||
isLoading: boolean;
|
||||
clanURI: string;
|
||||
}) {
|
||||
@@ -552,22 +552,10 @@ export function CubeScene(props: {
|
||||
description="Add new Service"
|
||||
name="modules"
|
||||
icon="Modules"
|
||||
onClick={() => {
|
||||
if (positionMode() === "grid") {
|
||||
setPositionMode("circle");
|
||||
setWorldMode("view");
|
||||
grid.visible = false;
|
||||
} else {
|
||||
setPositionMode("grid");
|
||||
grid.visible = true;
|
||||
}
|
||||
renderLoop.requestRender();
|
||||
}}
|
||||
/>
|
||||
<ToolbarButton
|
||||
icon="Reload"
|
||||
name="Reload"
|
||||
title="Reload"
|
||||
description="Reload machines"
|
||||
onClick={() => machinesQuery.refetch()}
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user