Skip to content

Commit 5897856

Browse files
committed
Add user parameter and connect button
1 parent 0d3a457 commit 5897856

9 files changed

Lines changed: 224 additions & 57 deletions

File tree

academy/views.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,12 +201,16 @@ def get_file_list(fal, request):
201201
Query params: project (str).
202202
"""
203203
project = request.GET.get("project")
204+
user = request.GET.get("user", None)
204205

205206
base_group = "Code"
206207

207208
path = fal.exercise_path(project)
208209

209-
file_list = fal.list_formatted(path, base_group)
210+
try:
211+
file_list = fal.list_formatted(path, base_group)
212+
except Exception:
213+
return Response({"file_list": EntryEncoder().encode([])})
210214

211215
return Response({"file_list": EntryEncoder().encode(file_list)})
212216

@@ -360,6 +364,7 @@ def get_file(fal, request):
360364
"""
361365
project_id = request.GET.get("project", None)
362366
filename = request.GET.get("filename", None)
367+
user = request.GET.get("user", None)
363368

364369
binary = request.GET.get("binary", None)
365370

react_frontend/src/api/api.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -154,14 +154,14 @@ const getHelperFileList = async (
154154
}
155155
};
156156

157-
const getFileList = async (project: string) => {
157+
const getFileList = async (project: string, user?: string) => {
158158
if (!project) throw new Error("Current Project id is not set");
159159

160160
// TODO:add whitelist parameter
161161

162-
const apiUrl = `/academy/get_file_list?project=${encodeURIComponent(
163-
project
164-
)}`;
162+
let apiUrl = `/academy/get_file_list?project=${encodeURIComponent(project)}`;
163+
164+
if (user !== undefined) apiUrl += `&user=${encodeURIComponent(user)}`;
165165

166166
try {
167167
const response = await axios.get(apiUrl);
@@ -172,14 +172,20 @@ const getFileList = async (project: string) => {
172172
}
173173
};
174174

175-
const getFile = async (project: string, fileName: string, binary?: boolean) => {
175+
const getFile = async (
176+
project: string,
177+
fileName: string,
178+
user?: string,
179+
binary?: boolean
180+
) => {
176181
if (!project) throw new Error("Project name is not set");
177182
if (!fileName) throw new Error("File name is not set");
178183

179184
let apiUrl = `/academy/get_file?project=${encodeURIComponent(
180185
project
181186
)}&filename=${encodeURIComponent(fileName)}`;
182187

188+
if (user !== undefined) apiUrl += `&user=${encodeURIComponent(user)}`;
183189
if (binary) apiUrl += `&binary=true`;
184190

185191
try {
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import React, { useState } from "react";
2+
import { StyledHeaderConnectButton } from "Styles/headers/HeaderMenu.styles";
3+
import { states } from "jderobot-commsmanager";
4+
import { useAcademyTheme } from "Contexts/AcademyThemeContext";
5+
6+
const ConnectButton = ({
7+
connectManager,
8+
}: {
9+
connectManager: (
10+
desiredState?: string,
11+
callback?: () => void
12+
) => Promise<void>;
13+
}) => {
14+
const theme = useAcademyTheme();
15+
const [loading, setLoading] = useState<boolean>(false);
16+
17+
const handleConnect = async () => {
18+
setLoading(true);
19+
connectManager(states.CONNECTED, () => {
20+
setLoading(false);
21+
close();
22+
});
23+
};
24+
25+
const msg = loading ? "Conecting ..." : "Click to connect";
26+
27+
return (
28+
<StyledHeaderConnectButton
29+
color={theme.palette.text}
30+
bgColor={theme.palette.secondary}
31+
hoverColor={theme.palette.secondary}
32+
roundness={theme.roundness}
33+
id="connect-with-rb"
34+
onClick={handleConnect}
35+
title="Connect to the Robotics Backend"
36+
disabled={loading}
37+
>
38+
{msg}
39+
</StyledHeaderConnectButton>
40+
);
41+
};
42+
43+
export default ConnectButton;

react_frontend/src/components/buttons/PlayPause.tsx

Lines changed: 17 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import React, { MutableRefObject, useEffect, useRef, useState } from "react";
12
import { StyledHeaderButton } from "Styles/headers/HeaderMenu.styles";
23
import { Entry, useError } from "jderobot-ide-interface";
34
import {
@@ -9,10 +10,8 @@ import {
910
} from "Helpers/utils";
1011
import { CommsManager, states } from "jderobot-commsmanager";
1112
import JSZip from "jszip";
12-
import { useEffect, useRef, useState } from "react";
1313
import commons from "../../common.zip";
1414
import { useAcademyTheme } from "Contexts/AcademyThemeContext";
15-
import React from "react";
1615

1716
import PlayArrowRoundedIcon from "@mui/icons-material/PlayArrowRounded";
1817
import PauseRoundedIcon from "@mui/icons-material/PauseRounded";
@@ -22,19 +21,17 @@ import { getFileList, getHelperFileList } from "Api";
2221
const PlayPauseButton = ({
2322
project,
2423
supportedLanguages,
25-
connectManager,
24+
userRef,
25+
entrypointRef,
2626
}: {
2727
project: string;
2828
supportedLanguages: string[];
29-
connectManager: (
30-
desiredState?: string,
31-
callback?: () => void
32-
) => Promise<void>;
29+
userRef: MutableRefObject<string | undefined>;
30+
entrypointRef: MutableRefObject<Entry | undefined>;
3331
}) => {
3432
const theme = useAcademyTheme();
35-
const { warning, error, info, close } = useError();
33+
const { warning, error } = useError();
3634
const filesRef = useRef<Entry[]>([]);
37-
const entrypointRef = useRef<Entry | undefined>(undefined);
3835
const runningFilesRef = useRef<JSZip>(JSZip);
3936
const runningEntrypointRef = useRef<Entry | undefined>(undefined);
4037
const runningContentRef = useRef<string | undefined>(undefined);
@@ -55,24 +52,15 @@ const PlayPauseButton = ({
5552
}
5653
};
5754

58-
const updateCurrent = (e: unknown) => {
59-
const T = CustomEvent<{ detail: { file?: Entry } }>;
60-
if (e instanceof T) {
61-
entrypointRef.current = e.detail.file;
62-
}
63-
};
64-
6555
useEffect(() => {
6656
subscribe("autoSaveCompleted", () => {
6757
updateCode(true);
6858
});
6959
subscribe("CommsManagerStateChange", updateState);
70-
subscribe("currentFile", updateCurrent);
7160

7261
return () => {
7362
unsubscribe("autoSaveCompleted", () => {});
7463
unsubscribe("CommsManagerStateChange", () => {});
75-
unsubscribe("currentFile", () => {});
7664
};
7765
}, []);
7866

@@ -134,22 +122,12 @@ const PlayPauseButton = ({
134122

135123
// App handling
136124

137-
const onAppStateChange = async (save?: boolean) => {
125+
const onAppStateChange = async (save?: boolean): Promise<void> => {
138126
const manager = CommsManager.getInstance();
139127
const state = manager.getState();
140128

141129
setLoading(true);
142130

143-
if (state === states.IDLE) {
144-
info("Connecting with the Robotics Backend ...");
145-
connectManager(states.TOOLS_READY, () => {
146-
setLoading(false);
147-
close();
148-
onAppStateChange();
149-
});
150-
return;
151-
}
152-
153131
if (state === states.WORLD_READY || state === states.CONNECTED) {
154132
console.error("Simulation is not ready!");
155133
warning(
@@ -197,16 +175,20 @@ const PlayPauseButton = ({
197175
}
198176

199177
if (!isCodeUpdatedRef.current) {
200-
return setTimeout(onAppStateChange, 100, true);
178+
setTimeout(onAppStateChange, 100, true);
179+
return;
201180
}
202181

203-
const files = await getFileList(project);
182+
const files = await getFileList(project, userRef.current);
204183
filesRef.current = JSON.parse(files);
205-
const userZip = await loadFiles(entrypointRef.current, JSON.parse(files));
184+
const userZip = await loadFiles(
185+
entrypointRef.current,
186+
filesRef.current,
187+
userRef.current
188+
);
206189

207190
if (state === states.PAUSED) {
208191
const sameZips = await compareZips(userZip, runningFilesRef.current);
209-
210192
if (sameZips && runningEntrypointRef.current === entrypointRef.current) {
211193
try {
212194
await manager.resume();
@@ -283,10 +265,10 @@ const PlayPauseButton = ({
283265
}
284266
}
285267

286-
async function loadFiles(entrypoint: Entry, files: Entry[]) {
268+
async function loadFiles(entrypoint: Entry, files: Entry[], user?: string) {
287269
const zip = new JSZip();
288270

289-
await zipCodeFiles(zip, files, project);
271+
await zipCodeFiles(zip, files, project, user);
290272

291273
zip.files[entrypoint.path]._data.then(
292274
(value: string) => (runningContentRef.current = value)

react_frontend/src/components/buttons/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@ export { default as LayoutButton } from "./Layout";
99
export { default as TerminateUniverseButton } from "./TerminateUniverse";
1010
export { default as SearchButton } from "./Search";
1111
export { default as ThemeButton } from "./Theme";
12+
export { default as ConnectButton } from "./Connect";

react_frontend/src/components/headers/ExerciseHeader.tsx

Lines changed: 76 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React from "react";
1+
import React, { MutableRefObject, useEffect, useRef, useState } from "react";
22
import AppBar from "@mui/material/AppBar";
33
import Toolbar from "@mui/material/Toolbar";
44
import { Link } from "react-router-dom";
@@ -21,8 +21,11 @@ import {
2121
LayoutButton,
2222
TerminateUniverseButton,
2323
ThemeButton,
24+
ConnectButton,
2425
} from "Components/buttons";
25-
import { Layout } from "jderobot-ide-interface";
26+
import { Entry, Layout } from "jderobot-ide-interface";
27+
import { CommsManager } from "jderobot-commsmanager";
28+
import { subscribe, unsubscribe } from "Helpers/utils";
2629

2730
const ExerciseHeader = ({
2831
project,
@@ -31,19 +34,22 @@ const ExerciseHeader = ({
3134
url,
3235
setLayout,
3336
connectManager,
37+
commsManager,
38+
userRef,
3439
}: {
3540
project: string;
3641
name: string;
3742
supportedLanguages: string[];
3843
url?: string;
3944
setLayout: (layout: Layout) => void;
45+
commsManager: CommsManager | null;
46+
userRef: MutableRefObject<string | undefined>;
4047
connectManager: (
4148
desiredState?: string,
4249
callback?: () => void
4350
) => Promise<void>;
4451
}) => {
4552
const theme = useAcademyTheme();
46-
4753
return (
4854
<AppBar position="static">
4955
<Toolbar
@@ -70,13 +76,13 @@ const ExerciseHeader = ({
7076
<ThemeButton />
7177
<DownloadButton project={project} />
7278
<LayoutButton setLayout={setLayout} />
73-
<PlayPauseButton
79+
<ExecutionControl
7480
project={project}
7581
supportedLanguages={supportedLanguages}
82+
commsManager={commsManager}
7683
connectManager={connectManager}
84+
userRef={userRef}
7785
/>
78-
<ResetButton />
79-
<TerminateUniverseButton />
8086
<TheoryButton url={url} />
8187
<InfoButton />
8288
<ForumButton />
@@ -86,4 +92,68 @@ const ExerciseHeader = ({
8692
);
8793
};
8894

95+
const ExecutionControl = ({
96+
project,
97+
supportedLanguages,
98+
commsManager,
99+
connectManager,
100+
userRef,
101+
}: {
102+
project: string;
103+
supportedLanguages: string[];
104+
commsManager: CommsManager | null;
105+
userRef: MutableRefObject<string | undefined>;
106+
connectManager: (
107+
desiredState?: string,
108+
callback?: () => void
109+
) => Promise<void>;
110+
}) => {
111+
const [state, setState] = useState<string | undefined>(
112+
commsManager?.getState()
113+
);
114+
const entrypointRef = useRef<Entry | undefined>(undefined);
115+
116+
const updateState = (e: any) => {
117+
setState(e.detail.state);
118+
};
119+
120+
const updateCurrent = (e: unknown) => {
121+
const T = CustomEvent<{ detail: { file?: Entry } }>;
122+
if (e instanceof T) {
123+
entrypointRef.current = e.detail.file;
124+
}
125+
};
126+
127+
useEffect(() => {
128+
subscribe("CommsManagerStateChange", updateState);
129+
subscribe("currentFile", updateCurrent);
130+
131+
return () => {
132+
unsubscribe("CommsManagerStateChange", () => {});
133+
unsubscribe("currentFile", () => {});
134+
};
135+
}, []);
136+
137+
const viewConnect = state === undefined || state === "idle";
138+
139+
return (
140+
<>
141+
{viewConnect ? (
142+
<ConnectButton connectManager={connectManager} />
143+
) : (
144+
<>
145+
<PlayPauseButton
146+
project={project}
147+
supportedLanguages={supportedLanguages}
148+
userRef={userRef}
149+
entrypointRef={entrypointRef}
150+
/>
151+
<ResetButton />
152+
<TerminateUniverseButton />
153+
</>
154+
)}
155+
</>
156+
);
157+
};
158+
89159
export default ExerciseHeader;

0 commit comments

Comments
 (0)