Merge pull request 'ui/scene: add loading splash screen' (#4400) from scene-progress into main

Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/4400
This commit is contained in:
hsjobeki
2025-07-18 17:42:15 +00:00
7 changed files with 95 additions and 9 deletions

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 13 KiB

View File

@@ -0,0 +1,4 @@
.fade-out {
opacity: 0;
transition: opacity 0.5s ease;
}

View File

@@ -1,5 +1,5 @@
import { RouteSectionProps } from "@solidjs/router";
import { Component, createEffect, JSX } from "solid-js";
import { Component, JSX } from "solid-js";
import { useClanURI } from "@/src/hooks/clan";
import { CubeScene } from "@/src/scene/cubes";
import { MachinesQueryResult, useMachinesQuery } from "@/src/queries/queries";
@@ -7,6 +7,9 @@ import { callApi } from "@/src/hooks/api";
import { store, setStore } from "@/src/stores/clan";
import { produce } from "solid-js/store";
import { Button } from "@/src/components/Button/Button";
import { Splash } from "@/src/scene/splash";
import cx from "classnames";
import "./Clan.css";
export const Clan: Component<RouteSectionProps> = (props) => {
return (
@@ -49,10 +52,6 @@ const ClanSceneController = () => {
return;
};
createEffect(() => {
console.log("sceneData changed:", store.sceneData);
});
return (
<SceneDataProvider clanURI={clanURI}>
{({ query }) => {
@@ -87,7 +86,12 @@ const ClanSceneController = () => {
Refetch API
</Button>
</div>
{/* TODO: Add minimal display time */}
<div class={cx({ "fade-out": !query.isLoading })}>
<Splash />
</div>
<CubeScene
isLoading={query.isLoading}
cubesQuery={query}
onCreate={onCreate}
sceneStore={() => {

View File

@@ -69,6 +69,7 @@ export function CubeScene(props: {
onCreate?: (id: string) => Promise<void>;
sceneStore: Accessor<SceneData>;
setMachinePos: (machineId: string, pos: [number, number]) => void;
isLoading: boolean;
}) {
// sceneData.cubesQuer
let container: HTMLDivElement;
@@ -78,7 +79,7 @@ export function CubeScene(props: {
let floor: THREE.Mesh;
let controls: MapControls;
// Raycaster for clicking
let raycaster = new THREE.Raycaster();
const raycaster = new THREE.Raycaster();
let needsRender = false; // Flag to control rendering
@@ -181,6 +182,7 @@ export function CubeScene(props: {
requestAnimationFrame(renderScene);
}
}
function renderScene() {
if (!isAnimating) return;
needsRender = false;
@@ -587,9 +589,6 @@ export function CubeScene(props: {
BASE_SIZE,
);
// Basic OrbitControls implementation (simplified)
let isDragging = false;
let previousMousePosition = { x: 0, y: 0 };
// const spherical = new THREE.Spherical();
// spherical.setFromVector3(camera.position);

View File

@@ -0,0 +1,45 @@
#splash {
position: fixed;
inset: 0;
background: linear-gradient(to top, #e3e7e7, #edf1f1);
display: flex;
justify-content: center;
align-items: center;
z-index: 9999;
pointer-events: none;
}
#splash .content {
display: flex;
flex-direction: column;
align-items: center;
}
.title {
@apply h-8 mb-8;
}
.loader {
@apply h-3 w-60 mb-3;
width: 18rem;
background: repeating-linear-gradient(
-45deg,
#bfd0d2 0px,
#bfd0d2 10px,
#f7f9fa 10px,
#f7f9fa 20px
);
animation: stripe-move 1s linear infinite;
background-size: 28px 28px; /* Sqrt(20^2 + 20^2) ~= 28 */
@apply border-2 border-solid rounded-[3px] border-bg-def-1;
}
@keyframes stripe-move {
0% {
background-position: 0 0;
}
100% {
background-position: 28px 0;
}
}

View File

@@ -0,0 +1,15 @@
import { Meta, StoryObj } from "@kachurun/storybook-solid";
import { Splash } from "./splash";
const meta: Meta = {
title: "scene/splash",
component: Splash,
};
export default meta;
type Story = StoryObj;
export const Default: Story = {
args: {},
};

View File

@@ -0,0 +1,18 @@
import Logo from "@/logos/darknet-builder-logo.svg";
import "./splash.css";
import { Typography } from "../components/Typography/Typography";
export const Splash = () => (
<div id="splash">
<div class="content">
<span class="title">
<Logo />
</span>
<div class="loader"></div>
<Typography hierarchy="label" size="xs" weight="medium">
Loading new Clan
</Typography>
</div>
</div>
);