Merge pull request 'ui/scene: add reload button' (#4962) from fixes-ui into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/4962
This commit is contained in:
@@ -121,7 +121,6 @@ export const useMachineStateQuery = (clanURI: string, machineName: string) => {
|
||||
const client = useApiClient();
|
||||
return useQuery<MachineState>(() => ({
|
||||
queryKey: ["clans", encodeBase64(clanURI), "machine", machineName, "state"],
|
||||
refetchInterval: 1000 * 60, // poll every 60 seconds
|
||||
queryFn: async () => {
|
||||
const apiCall = client.fetch("get_machine_state", {
|
||||
machine: {
|
||||
|
||||
@@ -302,21 +302,26 @@ const ClanSceneController = (props: RouteSectionProps) => {
|
||||
isLoading={ctx.isLoading()}
|
||||
cubesQuery={ctx.machinesQuery}
|
||||
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
|
||||
Reflect.deleteProperty(s.sceneData[ctx.clanURI], machineId);
|
||||
|
||||
if (Object.keys(s.sceneData[ctx.clanURI]).length === 0) {
|
||||
Reflect.deleteProperty(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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import { CSS2DRenderer } from "three/examples/jsm/renderers/CSS2DRenderer.js";
|
||||
import { Toolbar } from "../components/Toolbar/Toolbar";
|
||||
import { ToolbarButton } from "../components/Toolbar/ToolbarButton";
|
||||
import { Divider } from "../components/Divider/Divider";
|
||||
import { MachinesQueryResult } from "../hooks/queries";
|
||||
import { MachinesQueryResult, useMachinesQuery } from "../hooks/queries";
|
||||
import { SceneData } from "../stores/clan";
|
||||
import { Accessor } from "solid-js";
|
||||
import { renderLoop } from "./RenderLoop";
|
||||
@@ -37,8 +37,9 @@ 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;
|
||||
}) {
|
||||
let container: HTMLDivElement;
|
||||
let scene: THREE.Scene;
|
||||
@@ -524,6 +525,8 @@ export function CubeScene(props: {
|
||||
}
|
||||
};
|
||||
|
||||
const machinesQuery = useMachinesQuery(props.clanURI);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div class="cubes-scene-container" ref={(el) => (container = el)} />
|
||||
@@ -549,23 +552,13 @@ 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
|
||||
description="Delete Machine"
|
||||
name="delete"
|
||||
icon="Trash"
|
||||
/> */}
|
||||
<ToolbarButton
|
||||
icon="Reload"
|
||||
name="Reload"
|
||||
description="Reload machines"
|
||||
onClick={() => machinesQuery.refetch()}
|
||||
/>
|
||||
</Toolbar>
|
||||
</div>
|
||||
</>
|
||||
|
||||
Reference in New Issue
Block a user