Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: add feedback for errors and fix some misc issues #131

Merged
merged 10 commits into from
Oct 7, 2024
9 changes: 8 additions & 1 deletion src/assets/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -138,5 +138,12 @@
"CLEAR_PROJECT": "Clear project",
"CLEAR_PROJECT_DESC": "Selecting this robot will clear your project, are you sure?",
"DRIVER_INSTALL_TITLE": "How to install drivers?",
"DRIVER_INSTALL_TEXT": "Please follow the steps in the instruction video below to install the drivers."
"DRIVER_INSTALL_TEXT": "Please follow the steps in the instruction video below to install the drivers.",
"SEND_FEEDBACK": "Send feedback",
"INVALID_WORKSPACE": "Invalid project",
"INVALID_WORKSPACE_MESSAGE": "That doesn't look like a Leaphy Project, please verify that you have selected the correct file",
"UNDEFINED_ROBOT": "Project name invalid",
"UNDEFINED_ROBOT_MESSAGE": "This does seem like a valid Leaphy Project, but the robot type was not included in the name, we've kept your previous robot selection for now.",
"ROBOT_RESERVED": "Unable to connect to robot",
"ROBOT_RESERVED_MESSAGE": "It appears that we're unable to connect to the robot, the port might be in use by another program or another tab"
}
9 changes: 8 additions & 1 deletion src/assets/translations/nl.json
Original file line number Diff line number Diff line change
Expand Up @@ -138,5 +138,12 @@
"CLEAR_PROJECT": "Wis project",
"CLEAR_PROJECT_DESC": "Door deze robot te selecteren wis je je project, weet je het zeker?",
"DRIVER_INSTALL_TITLE": "Hoe installeer je drivers?",
"DRIVER_INSTALL_TEXT": "Bekijk het filmpje voor uitleg hoe je de drivers kunt installeren."
"DRIVER_INSTALL_TEXT": "Bekijk het filmpje voor uitleg hoe je de drivers kunt installeren.",
"SEND_FEEDBACK": "Verstuur feedback",
"INVALID_WORKSPACE": "Ongeldig project",
"INVALID_WORKSPACE_MESSAGE": "Dat ziet er niet uit als een kloppend Leaphy Project, controleer of je het goede bestand hebt geselecteerd",
"UNDEFINED_ROBOT": "Ongeldige project naam",
"UNDEFINED_ROBOT_MESSAGE": "Dit project ziet er goed uit, maar het type robot staat niet in de naam, je vorige robot selectie is behouden",
"ROBOT_RESERVED": "Verbinding onmogelijk",
"ROBOT_RESERVED_MESSAGE": "Het lijkt erop dat we niet kunnen verbinden met de robot, de poort zou in gebruik kunnen zijn door een ander programma of een andere app"
}
20 changes: 18 additions & 2 deletions src/lib/components/core/header/Header.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { _, locale } from "svelte-i18n";
import block from "$assets/block.svg";
import leaphyLogo from "$assets/leaphy-logo.svg";
import Connect from "$components/core/popups/popups/Connect.svelte";
import ErrorPopup from "$components/core/popups/popups/Error.svelte";
import Button from "$components/ui/Button.svelte";
import ContextItem from "$components/ui/ContextItem.svelte";
import { loadWorkspaceFromString } from "$domain/blockly/blockly";
Expand Down Expand Up @@ -78,7 +79,7 @@ async function upload() {
}

async function connect() {
if (getSelector($robot))
if ($mode === Mode.ADVANCED)
popups.open({
component: Connect,
data: {},
Expand Down Expand Up @@ -147,11 +148,26 @@ async function openProject() {
code.set(await content.text());
} else {
if (get(mode) === Mode.BLOCKS) {
loadWorkspaceFromString(await content.text(), $workspace);
if (!loadWorkspaceFromString(await content.text(), $workspace)) {
return;
}
} else {
restore.set(JSON.parse(await content.text()));
mode.set(Mode.BLOCKS);
}

if (!robots[file.name.split(".").at(-1)]) {
popups.open({
component: ErrorPopup,
data: {
title: "UNDEFINED_ROBOT",
message: "UNDEFINED_ROBOT_MESSAGE",
},
allowInteraction: false,
});
return;
}

robot.set(robots[file.name.split(".").at(-1)]);
}
}
Expand Down
64 changes: 64 additions & 0 deletions src/lib/components/core/popups/popups/Error.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<script lang="ts">
import Feedback from "$components/core/popups/popups/Feedback.svelte";
import Button from "$components/ui/Button.svelte";
import { type PopupState, popups } from "$state/popup.svelte";
import { faExclamationTriangle } from "@fortawesome/free-solid-svg-icons";
import { getContext } from "svelte";
import Fa from "svelte-fa";
import { _ } from "svelte-i18n";
import type { Writable } from "svelte/store";

interface Props {
title: string;
message: string;
}

const { title, message }: Props = $props();

const popupState = getContext<Writable<PopupState>>("state");
function ok() {
popups.close($popupState.id);
}

function feedback() {
popups.close($popupState.id);
popups.open({
component: Feedback,
data: {},
allowInteraction: false,
});
}
</script>

<div class="content">
<h2 class="title">
<Fa icon="{faExclamationTriangle}" />
{$_(title)}
</h2>
<div class="text">{$_(message)}</div>
<div class="actions">
<Button name={$_("SEND_FEEDBACK")} mode="secondary" onclick={feedback} />
<Button name={$_("OK")} mode={"primary"} onclick={ok}/>
</div>
</div>

<style>
.content {
padding: 20px;
display: flex;
flex-direction: column;
min-width: 400px;
text-align: center;
}

.title {
color: salmon;
}

.actions {
display: flex;
justify-content: center;
margin-top: 20px;
gap: 10px;
}
</style>
16 changes: 15 additions & 1 deletion src/lib/components/core/popups/popups/SerialMonitor.svelte
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
<script lang="ts">
import ErrorPopup from "$components/core/popups/popups/Error.svelte";
import Button from "$components/ui/Button.svelte";
import Chart from "$components/ui/Chart.svelte";
import TextInput from "$components/ui/TextInput.svelte";
import WindowButton from "$components/ui/WindowButton.svelte";
import { popups } from "$state/popup.svelte";
import { Prompt, log, port } from "$state/workspace.svelte";
import {
faArrowDown,
Expand Down Expand Up @@ -46,7 +48,19 @@ log.subscribe(async () => {
let value = $state("");
function send(event: SubmitEvent) {
event.preventDefault();
log.write(`${value}\n`);
try {
log.write(`${value}\n`);
} catch {
popups.open({
component: ErrorPopup,
data: {
title: "ROBOT_RESERVED",
message: "ROBOT_RESERVED_MESSAGE",
},
allowInteraction: false,
});
}

value = "";
}

Expand Down
26 changes: 20 additions & 6 deletions src/lib/components/core/popups/popups/Uploader.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import { _ } from "svelte-i18n";

import DriverInstall from "$components/core/popups/popups/DriverInstall.svelte";
import ErrorPopup from "$components/core/popups/popups/Error.svelte";
import Button from "$components/ui/Button.svelte";
import ProgressBar from "$components/ui/ProgressBar.svelte";
import { type PopupState, popups } from "$state/popup.svelte";
Expand Down Expand Up @@ -30,7 +31,7 @@ let error = $state<string | null>(null);
let done = $state(false);
let failed = $state(false);

class UploadError extends Error {
class UploadError extends ErrorPopup {
constructor(
public name: string,
public description: string,
Expand Down Expand Up @@ -65,12 +66,25 @@ async function compile() {

async function upload(res: Record<string, string>) {
try {
currentState = "WAITING_FOR_PORT";
await port.ready;
progress += 100 / 4;
try {
currentState = "WAITING_FOR_PORT";
await port.ready;
progress += 100 / 4;

currentState = "UPDATE_STARTED";
await port.reserve();
} catch {
popups.close($popupState.id);
return popups.open({
component: ErrorPopup,
data: {
title: "ROBOT_RESERVED",
message: "ROBOT_RESERVED_MESSAGE",
},
allowInteraction: false,
});
}

currentState = "UPDATE_STARTED";
await port.reserve();
await $robot.programmer.upload($port, res);
} catch (e) {
console.log(e);
Expand Down
25 changes: 20 additions & 5 deletions src/lib/domain/blockly/blockly.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as Blockly from "blockly";
import "@blockly/field-bitmap";
import defaultProgram from "$assets/default-program.json?raw";
import ErrorPopup from "$components/core/popups/popups/Error.svelte";
import Explanation from "$components/core/popups/popups/Explanation.svelte";
import Prompt from "$components/core/popups/popups/Prompt.svelte";
import { type RobotDevice, inFilter } from "$domain/robots";
Expand Down Expand Up @@ -163,11 +164,25 @@ export function loadWorkspaceFromString(content: string, workspace: Workspace) {
const json = JSON.parse(content);
serialization.workspaces.load(json, workspace);
} catch {
// It's not JSON, maybe it's XML
const xml = Blockly.utils.xml.textToDom(content);
workspace.clear();
Blockly.Xml.domToWorkspace(xml, workspace);
try {
// It's not JSON, maybe it's XML
const xml = Blockly.utils.xml.textToDom(content);
workspace.clear();
Blockly.Xml.domToWorkspace(xml, workspace);
} catch {
popups.open({
component: ErrorPopup,
data: {
title: "INVALID_WORKSPACE",
message: "INVALID_WORKSPACE_MESSAGE",
},
allowInteraction: false,
});
return false;
}
}

return true;
}

export function setupWorkspace(
Expand Down Expand Up @@ -319,7 +334,7 @@ export async function explain(block: Blockly.BlockSvg) {
model: "Llama3-70b-8192",
}),
}).then(async (res) => {
if (!res.ok) throw new Error(res.statusText);
if (!res.ok) throw new ErrorPopup(res.statusText);
return JSON.parse(await res.text());
}),
},
Expand Down
18 changes: 18 additions & 0 deletions src/lib/state/workspace.svelte.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import ErrorPopup from "$components/core/popups/popups/Error.svelte";
import Advanced from "$components/workspace/advanced/Advanced.svelte";
import Blocks from "$components/workspace/blocks/Blocks.svelte";
import Python from "$components/workspace/python/Python.svelte";
Expand Down Expand Up @@ -120,13 +121,26 @@ function createPortState() {

let onReady: () => void;
let onFailure: () => void;
let showFeedback = false;
subscribe(async (port) => {
if (!port || reserved) return;

if (!port.readable || !port.writable) {
try {
await port.open({ baudRate: 115200 });
} catch (e) {
set(undefined);
if (showFeedback) {
popups.open({
component: ErrorPopup,
data: {
title: "ROBOT_RESERVED",
message: "ROBOT_RESERVED_MESSAGE",
},
allowInteraction: false,
});
}

onFailure();
throw e;
}
Expand Down Expand Up @@ -198,6 +212,7 @@ function createPortState() {
onFailure = reject;
}),
async requestPort(prompt: Prompt): Promise<SerialPort | USBDevice> {
showFeedback = prompt === Prompt.ALWAYS;
if (navigator.serial) {
if (prompt !== Prompt.ALWAYS) {
const [port] = await navigator.serial.getPorts();
Expand Down Expand Up @@ -233,7 +248,10 @@ function createPortState() {
onReady = resolve;
onFailure = reject;
});

const port = await this.requestPort(prompt);
if (get({ subscribe }) === port) return onReady();

if ("addEventListener" in port) {
port.addEventListener("disconnect", async () => {
reserved = false;
Expand Down
2 changes: 1 addition & 1 deletion tests/language.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,5 @@ test("Language", async ({ page }) => {
// Prevent it from opening a popup requesting the port, act as if nothing gets selected
await page.evaluate("navigator.serial.requestPort = function() {}");
await page.getByRole("button", { name: "Upload naar robot" }).click();
await page.getByRole("button", { name: "Ga terug naar code scherm" }).click();
await page.getByRole("button", { name: "Ik begrijp het" }).click();
});