Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,4 @@ WALLS_CDN_BASE=https://huggingface.co/datasets/setrsoft/climbing-walls/resolve/m
# --- PostHog analytics ---
# Project API Key (Project Settings → Project API Key). Leave empty to disable.
VITE_PUBLIC_POSTHOG_PROJECT_TOKEN=phc_xxxxxxxxxxxxxxxxxxxx
VITE_PUBLIC_POSTHOG_HOST=https://eu.i.posthog.com
VITE_PUBLIC_POSTHOG_HOST=https://e.setrsoft.com
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

SetRsoft is an open source community driven software. Test it live on the website http://www.setrsoft.com/

For better understanding the purpose of this project you can have a look at [one of those videos](https://www.youtube.com/results?search_query=routesetting+in+climbing+gym).
For better understanding the purpose of this project you can have a look at [this video made for 42Builders](https://e.pcloud.link/publink/show?code=XZJuQiZvUY5trMn7pXG09SRvvzBpJmPkl5k).

Feel free to create new Github Issues for new features or signal bugs.

Expand Down
107 changes: 30 additions & 77 deletions frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 1 addition & 2 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,10 @@
"posthog-js": "^1.364.7",
"react": "^19.2.0",
"react-dom": "^19.2.0",
"react-helmet-async": "^2.0.1",
"react-i18next": "^16.6.0",
"react-router-dom": "^7.13.1",
"three": "^0.177.0",
"uuid": "^11.1.0",
"uuid": "^14.0.0",
"zustand": "^5.0.4"
},
"devDependencies": {
Expand Down
49 changes: 22 additions & 27 deletions frontend/src/features/editor/EditorApp.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { useEffect, useRef, useState } from "react";
import { useEffect, useMemo, useRef, useState } from "react";
import { useGLTF } from "@react-three/drei";
import { useParams } from "react-router-dom";
import { useQuery } from "@tanstack/react-query";
import { posthog } from "@/shared/analytics/posthog";

import useWallSessionQuery from "./utils/WallSessionQuery";
import { usePlacementStore } from "./store";
import type { SessionHoldInstance } from "./store";
import { useHandleLoadSession } from "./utils/HandleLoadSession";

import MainCanvas from "./components/MainCanvas";
Expand All @@ -15,15 +16,6 @@ import FileManager from "./components/FileManager";
import { useTranslation } from "react-i18next";
import Tutorial from "./components/Tutorial";

interface HoldInstance {
id: string;
hold_instance_id?: string;
hold_type?: {
glb_url?: string;
};
[key: string]: unknown;
}

const transformTools = [
{ id: "translate", icon: "open_with", label: "Translate", hint: "Shift + Left click" },
{ id: "rotate", icon: "sync", label: "Rotate", hint: "Left click" },
Expand All @@ -46,6 +38,8 @@ function EditorApp() {
queryKey: ["wallsession", wallId],
queryFn: () => fetchWallSession(wallId as string),
enabled: !!wallId,
staleTime: Infinity,
refetchOnWindowFocus: false,
});

const session_data = data?.wall_session || data;
Expand All @@ -63,12 +57,10 @@ function EditorApp() {
}, [wallId, setObjects, setWallColors, setHoldColors, setColoredTexture, setHasUnsavedChanges]);

useEffect(() => {
if (session_data?.id && wallModels.length > 0) {
if (session_data?.id && wallModels.length > 0 && !sessionOpenedRef.current) {
sessionOpenedRef.current = true;
handleLoad();
if (!sessionOpenedRef.current) {
sessionOpenedRef.current = true;
posthog.capture('editor session opened', { wall_id: wallId, session_id: session_data.id });
}
posthog.capture('editor session opened', { wall_id: wallId, session_id: session_data.id });
}
}, [session_data?.id, wallId, wallModels.length, handleLoad]);

Expand All @@ -84,23 +76,26 @@ function EditorApp() {
return () => window.removeEventListener("beforeunload", handleBeforeUnload);
}, [hasUnsavedChanges, t]);

const holdModels: Array<Record<string, HoldInstance>> = [];
const holdModelsGLBURL: string[] = [];

useEffect(() => {
const glbUrl = session_data?.related_wall?.glb_url;
setWallModels(glbUrl ? [glbUrl] : []);
}, [session_data?.related_wall?.glb_url]);

if (session_data?.related_holds_collection) {
session_data.holds_collection_instances?.forEach((hold: any) => {
hold.hold_instance_id = hold.id;
holdModels.push(hold);
if (hold.hold_type?.glb_url) {
holdModelsGLBURL.push(hold.hold_type.glb_url);
}
});
}
const { holdModels, holdModelsGLBURL } = useMemo(() => {
const holdModels: SessionHoldInstance[] = [];
const glbUrlSet = new Set<string>();
if (session_data?.related_holds_collection) {
session_data.holds_collection_instances?.forEach((hold: SessionHoldInstance) => {
const holdWithId = { ...hold, hold_instance_id: hold.id };
holdModels.push(holdWithId);
if (holdWithId.hold_type?.glb_url) {
glbUrlSet.add(holdWithId.hold_type.glb_url);
}
});
}
const holdModelsGLBURL = Array.from(glbUrlSet);
return { holdModels, holdModelsGLBURL };
}, [session_data?.related_holds_collection, session_data?.holds_collection_instances]);

const { preload } = useGLTF;
useEffect(() => {
Expand Down
Loading
Loading