Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 4 additions & 0 deletions lang/ui.en.json
Original file line number Diff line number Diff line change
Expand Up @@ -743,6 +743,10 @@
"defaultMessage": "This graph shows movement data from the micro:bit's accelerometer in real time. Try moving your data collection micro:bit to see the X, Y and Z axes change. Each coloured line represents a different direction (dimension) you are moving the micro:bit in.",
"description": "Tooltip for live graph heading"
},
"loading": {
"defaultMessage": "Loading",
"description": "Aria label for loading spinner"
},
"main-menu": {
"defaultMessage": "Main menu",
"description": "Main menu label"
Expand Down
6 changes: 6 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,13 @@ import NewPage from "./pages/NewPage";
import TestingModelPage from "./pages/TestingModelPage";
import {
createDataSamplesPageUrl,
createCodePageUrl,
createHomePageUrl,
createImportPageUrl,
createNewPageUrl,
createTestingModelPageUrl,
} from "./urls";
import CodePage from "./pages/CodePage";

export interface ProviderLayoutProps {
children: ReactNode;
Expand Down Expand Up @@ -107,6 +109,10 @@ const createRouter = () => {
path: createTestingModelPageUrl(),
element: <TestingModelPage />,
},
{
path: createCodePageUrl(),
element: <CodePage />,
},
{
path: "*",
element: <NotFound />,
Expand Down
75 changes: 62 additions & 13 deletions src/hooks/project-hooks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,23 @@ import {
} from "../utils/fs-util";
import { useDownloadActions } from "./download-hooks";
import { useNavigate } from "react-router";
import { createDataSamplesPageUrl } from "../urls";
import {
createDataSamplesPageUrl,
createCodePageUrl,
createTestingModelPageUrl,
} from "../urls";
import { useLogging } from "../logging/logging-hooks";
import { getTotalNumSamples } from "../utils/gestures";

class CodeEditorError extends Error {}

/**
* Distinguishes the different ways to trigger the load action.
*/
export type LoadType = "drop-load" | "file-upload";

interface ProjectContext {
browserNavigationToEditor(): Promise<boolean>;
openEditor(): Promise<void>;
project: Project;
projectEdited: boolean;
Expand Down Expand Up @@ -79,27 +86,50 @@ export const ProjectProvider = ({
const intl = useIntl();
const toast = useToast();
const logging = useLogging();
const setEditorOpen = useStore((s) => s.setEditorOpen);
const projectEdited = useStore((s) => s.projectEdited);
const expectChangedHeader = useStore((s) => s.setChangedHeaderExpected);
const projectFlushedToEditor = useStore((s) => s.projectFlushedToEditor);
const checkIfProjectNeedsFlush = useStore((s) => s.checkIfProjectNeedsFlush);
const getCurrentProject = useStore((s) => s.getCurrentProject);
const navigate = useNavigate();
const doAfterEditorUpdatePromise = useRef<Promise<void>>();
const doAfterEditorUpdate = useCallback(
async (action: () => Promise<void>) => {
if (checkIfProjectNeedsFlush()) {
const project = getCurrentProject();
expectChangedHeader();
await driverRef.current!.importProject({ project });
projectFlushedToEditor();
if (!doAfterEditorUpdatePromise.current && checkIfProjectNeedsFlush()) {
doAfterEditorUpdatePromise.current = new Promise<void>(
(resolve, reject) => {
// driverRef.current is not defined on first render.
// Only an issue when navigating to code page directly.
if (!driverRef.current) {
reject(new CodeEditorError("MakeCode iframe ref is undefined"));
} else {
const project = getCurrentProject();
expectChangedHeader();
driverRef.current
.importProject({ project })
.then(() => {
projectFlushedToEditor();
resolve();
})
.catch((e) => {
reject(e);
});
}
}
);
}
try {
await doAfterEditorUpdatePromise.current;
} finally {
doAfterEditorUpdatePromise.current = undefined;
}
return action();
},
[
checkIfProjectNeedsFlush,
getCurrentProject,
driverRef,
expectChangedHeader,
driverRef,
projectFlushedToEditor,
]
);
Expand All @@ -108,14 +138,29 @@ export const ProjectProvider = ({
type: "edit-in-makecode",
});
await doAfterEditorUpdate(() => {
setEditorOpen(true);
navigate(createCodePageUrl());
return Promise.resolve();
});
}, [doAfterEditorUpdate, logging, setEditorOpen]);

}, [doAfterEditorUpdate, logging, navigate]);
const browserNavigationToEditor = useCallback(async () => {
try {
await doAfterEditorUpdate(() => {
return Promise.resolve();
});
return true;
} catch (e) {
if (e instanceof CodeEditorError) {
// In this case, doAfterEditorUpdate has failed because the app has loaded
// on the code page directly. The caller of browserNavigationToEditor redirects.
return false;
}
// Unexpected error, can't handle better than the redirect.
logging.error(e);
return false;
}
}, [doAfterEditorUpdate, logging]);
const resetProject = useStore((s) => s.resetProject);
const loadDataset = useStore((s) => s.loadDataset);
const navigate = useNavigate();
const loadFile = useCallback(
async (file: File, type: LoadType): Promise<void> => {
const fileExtension = getLowercaseFileExtension(file.name);
Expand Down Expand Up @@ -211,7 +256,9 @@ export const ProjectProvider = ({
[editorChange]
);

const onBack = useCallback(() => setEditorOpen(false), [setEditorOpen]);
const onBack = useCallback(() => {
navigate(createTestingModelPageUrl());
}, [navigate]);
const onSave = saveHex;
const downloadActions = useDownloadActions();
const onDownload = useCallback(
Expand All @@ -231,6 +278,7 @@ export const ProjectProvider = ({
() => ({
loadFile,
openEditor,
browserNavigationToEditor,
project,
projectEdited,
resetProject,
Expand All @@ -243,6 +291,7 @@ export const ProjectProvider = ({
},
}),
[
browserNavigationToEditor,
loadFile,
onBack,
onDownload,
Expand Down
6 changes: 6 additions & 0 deletions src/messages/ui.en.json
Original file line number Diff line number Diff line change
Expand Up @@ -1373,6 +1373,12 @@
"value": "This graph shows movement data from the micro:bit's accelerometer in real time. Try moving your data collection micro:bit to see the X, Y and Z axes change. Each coloured line represents a different direction (dimension) you are moving the micro:bit in."
}
],
"loading": [
{
"type": 0,
"value": "Loading"
}
],
"main-menu": [
{
"type": 0,
Expand Down
60 changes: 60 additions & 0 deletions src/pages/CodePage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { Spinner, VStack } from "@chakra-ui/react";
import { useEffect, useRef, useState } from "react";
import { useIntl } from "react-intl";
import { useNavigate } from "react-router";
import { useProject } from "../hooks/project-hooks";
import { useStore } from "../store";
import { createDataSamplesPageUrl, createTestingModelPageUrl } from "../urls";

const CodePage = () => {
const navigate = useNavigate();
const model = useStore((s) => s.model);
const setEditorOpen = useStore((s) => s.setEditorOpen);
const { browserNavigationToEditor } = useProject();
const [loading, setLoading] = useState<boolean>(true);
const intl = useIntl();
const initAsyncCalled = useRef(false);
useEffect(() => {
const initAsync = async () => {
initAsyncCalled.current = true;
if (!model) {
navigate(createDataSamplesPageUrl());
} else {
const success = await browserNavigationToEditor();
if (success) {
setLoading(false);
setEditorOpen(true);
} else {
navigate(createTestingModelPageUrl());
}
}
};

if (!initAsyncCalled.current) {
void initAsync();
}

return () => {
setEditorOpen(false);
};
}, [browserNavigationToEditor, model, navigate, setEditorOpen]);
return (
<>
{loading && (
<VStack h="100%" justifyContent="center">
<Spinner
aria-label={intl.formatMessage({ id: "loading" })}
thickness="16px"
speed="2s"
emptyColor="whitesmoke"
color="brand.600"
h="166px"
w="166px"
/>
</VStack>
)}
</>
);
};

export default CodePage;
4 changes: 2 additions & 2 deletions src/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -242,10 +242,10 @@ const createMlStore = (logging: Logging) => {

setEditorOpen(open: boolean) {
set(
({ download }) => ({
({ download, model }) => ({
isEditorOpen: open,
// We just assume its been edited as spurious changes from MakeCode happen that we can't identify
projectEdited: true,
projectEdited: model ? true : false,
download: {
...download,
usbDevice: undefined,
Expand Down
2 changes: 2 additions & 0 deletions src/urls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,5 @@ export const createImportPageUrl = () => `${basepath}import`;
export const createDataSamplesPageUrl = () => `${basepath}data-samples`;

export const createTestingModelPageUrl = () => `${basepath}testing-model`;

export const createCodePageUrl = () => `${basepath}code`;
Loading