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

WIP: Added network error handling #159

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
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
48 changes: 28 additions & 20 deletions components/HooksEditor.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,29 @@
import React, { useEffect, useRef } from "react";
import { useSnapshot, ref } from "valtio";
import { listen } from "@codingame/monaco-jsonrpc";
import { MonacoServices } from "@codingame/monaco-languageclient";
import Editor, { loader } from "@monaco-editor/react";
import uniqBy from "lodash.uniqby";
import type monaco from "monaco-editor";
import { ArrowBendLeftUp } from "phosphor-react";
import { useTheme } from "next-themes";
import { useRouter } from "next/router";
import uniqBy from "lodash.uniqby";

import Box from "./Box";
import Container from "./Container";
import dark from "../theme/editor/amy.json";
import light from "../theme/editor/xcode_default.json";
import { ArrowBendLeftUp } from "phosphor-react";
import React, { useEffect, useRef } from "react";
import toast from "react-hot-toast";
import ReconnectingWebSocket from "reconnecting-websocket";
import { ref, useSnapshot } from "valtio";
import state from "../state";
import { saveFile } from "../state/actions";
import { apiHeaderFiles } from "../state/constants";
import state from "../state";

import dark from "../theme/editor/amy.json";
import light from "../theme/editor/xcode_default.json";
import { createLanguageClient, createWebSocket } from "../utils/languageClient";
import docs from "../xrpl-hooks-docs/docs";
import Box from "./Box";
import Container from "./Container";
import EditorNavigation from "./EditorNavigation";
import Text from "./Text";
import { MonacoServices } from "@codingame/monaco-languageclient";
import { createLanguageClient, createWebSocket } from "../utils/languageClient";
import { listen } from "@codingame/monaco-jsonrpc";
import ReconnectingWebSocket from "reconnecting-websocket";

import docs from "../xrpl-hooks-docs/docs";



loader.config({
paths: {
Expand Down Expand Up @@ -123,6 +124,7 @@ const HooksEditor = () => {
setMarkers(monacoRef.current);
}
}, [snap.active]);

return (
<Box
css={{
Expand Down Expand Up @@ -155,7 +157,7 @@ const HooksEditor = () => {
);
}

// create the web socket
// create the websocket
if (!subscriptionRef.current) {
monaco.languages.register({
id: "c",
Expand All @@ -164,11 +166,12 @@ const HooksEditor = () => {
mimetypes: ["text/plain"],
});
MonacoServices.install(monaco);
const webSocket = createWebSocket(

const webSocket: ReconnectingWebSocket = createWebSocket(
process.env.NEXT_PUBLIC_LANGUAGE_SERVER_API_ENDPOINT || ""
);
subscriptionRef.current = webSocket;
// listen when the web socket is opened
// listen when the websocket is opened
listen({
webSocket: webSocket as WebSocket,
onConnection: (connection) => {
Expand All @@ -180,9 +183,14 @@ const HooksEditor = () => {
// disposable.stop();
disposable.dispose();
} catch (err) {
console.log("err", err);
toast.error('Connection to language server lost!')
console.error("Couldn't dispose the language server connection! ", err);
}
});
connection.onDispose(() => {
toast.error('Connection to language server lost!')
})
// TODO: Check if we need to listen to more connection events
},
});
}
Expand Down
23 changes: 9 additions & 14 deletions components/LogBox.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,17 @@
import NextLink from "next/link";
import { Notepad, Prohibit } from "phosphor-react";
import {
useRef,
useLayoutEffect,
ReactNode,
FC,
useState,
useCallback,
FC, ReactNode, useCallback, useLayoutEffect, useRef, useState
} from "react";
import { Notepad, Prohibit } from "phosphor-react";
import useStayScrolled from "react-stay-scrolled";
import NextLink from "next/link";

import Container from "./Container";
import LogText from "./LogText";
import state, { ILog } from "../state";
import { Pre, Link, Heading, Button, Text, Flex, Box } from ".";
import regexifyString from "regexify-string";
import { useSnapshot } from "valtio";
import { Box, Button, Flex, Heading, Link, Pre, Text } from ".";
import state, { ILog } from "../state";
import { AccountDialog } from "./Accounts";
import Container from "./Container";
import LogText from "./LogText";


interface ILogBox {
title: string;
Expand Down Expand Up @@ -160,7 +155,7 @@ export const Log: FC<ILog> = ({

const enrichAccounts = useCallback(
(str?: string): ReactNode => {
if (!str || !accounts.length) return null;
if (!str) return null;

const pattern = `(${accounts.map((acc) => acc.address).join("|")})`;
const res = regexifyString({
Expand Down
5 changes: 1 addition & 4 deletions state/actions/addFaucetAccount.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,6 @@ export const addFaucetAccount = async (showToast: boolean = false) => {
return toast.error("You can only have maximum 6 accounts");
}
if (typeof window !== 'undefined') {


const toastId = showToast ? toast.loading("Creating account") : "";
const res = await fetch(`${window.location.origin}/api/faucet`, {
method: "POST",
Expand Down Expand Up @@ -92,5 +90,4 @@ export const addFunds = async (address: string) => {
currAccount.xrp = (Number(currAccount.xrp) + (json.xrp * 1000000)).toString();
}
}

}
}
39 changes: 29 additions & 10 deletions state/actions/compileCode.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import toast from "react-hot-toast";
import Router from 'next/router';

import toast from "react-hot-toast";
import { ref } from "valtio";
import { decodeBinary } from "../../utils/decodeBinary";
import state from "../index";
import { saveFile } from "./saveFile";
import { decodeBinary } from "../../utils/decodeBinary";
import { ref } from "valtio";


/* compileCode sends the code of the active file to compile endpoint
* If all goes well you will get base64 encoded wasm file back with
Expand All @@ -15,17 +15,22 @@ import { ref } from "valtio";
export const compileCode = async (activeId: number) => {
// Save the file to global state
saveFile(false);

if (!process.env.NEXT_PUBLIC_COMPILE_API_ENDPOINT) {
throw Error("Missing env!");
}

// Bail out if we're already compiling
if (state.compiling) {
// if compiling is ongoing return
return;
}

// Set loading state to true
state.compiling = true;
// Reset development log
state.logs = []

try {
const res = await fetch(process.env.NEXT_PUBLIC_COMPILE_API_ENDPOINT, {
method: "POST",
Expand All @@ -46,6 +51,7 @@ export const compileCode = async (activeId: number) => {
});
const json = await res.json();
state.compiling = false;

if (!json.success) {
state.logs.push({ type: "error", message: json.message });
if (json.tasks && json.tasks.length > 0) {
Expand All @@ -57,16 +63,19 @@ export const compileCode = async (activeId: number) => {
}
return toast.error(`Couldn't compile!`, { position: "bottom-center" });
}

state.logs.push({
type: "success",
message: `File ${state.files?.[activeId]?.name} compiled successfully. Ready to deploy.`,
link: Router.asPath.replace("develop", "deploy"),
linkText: "Go to deploy",
});

// Decode base64 encoded wasm that is coming back from the endpoint
const bufferData = await decodeBinary(json.output);
state.files[state.active].compiledContent = ref(bufferData);
state.files[state.active].lastCompiled = new Date();

// Import wabt from and create human readable version of wasm file and
// put it into state
import("wabt").then((wabt) => {
Expand All @@ -80,12 +89,22 @@ export const compileCode = async (activeId: number) => {
state.files[state.active].compiledWatContent = wast;
toast.success("Compiled successfully!", { position: "bottom-center" });
});
} catch (err) {
console.log(err);
state.logs.push({
type: "error",
message: "Error occured while compiling!",
});
} catch (err: any) {
const error = err as Error

// TODO: Centralized error handling? Just a thought
if(error.message.includes("Failed to fetch")) {
state.logs.push({
type: "error",
message: "No connection to the compiler server!",
});
} else {
state.logs.push({
type: "error",
message: "Error occured while compiling!",
});
}

state.compiling = false;
}
};
17 changes: 11 additions & 6 deletions state/actions/deployHook.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { derive, sign } from "xrpl-accountlib";
import toast from "react-hot-toast";

import state, { IAccount } from "../index";
import calculateHookOn, { TTS } from "../../utils/hookOnCalculator";
import { derive, sign } from "xrpl-accountlib";
import { AnyJson } from "xrpl-client";
import { SetHookData } from "../../components/SetHookDialog";
import calculateHookOn, { TTS } from "../../utils/hookOnCalculator";
import state, { IAccount } from "../index";


const hash = async (string: string) => {
const utf8 = new TextEncoder().encode(string);
Expand Down Expand Up @@ -51,19 +52,22 @@ function arrayBufferToHex(arrayBuffer?: ArrayBuffer | null) {
* hex string, signs the transaction and deploys it to
* Hooks testnet.
*/
export const deployHook = async (account: IAccount & { name?: string }, data: SetHookData) => {
export const deployHook = async (account: IAccount & { name?: string }, data: SetHookData): Promise<AnyJson | undefined> => {
if (
!state.files ||
state.files.length === 0 ||
!state.files?.[state.active]?.compiledContent
) {
console.log("ebin1")
return;
}

if (!state.files?.[state.active]?.compiledContent) {
console.log("ebin2")
return;
}
if (!state.client) {
console.log("ebin3")
return;
}
const HookNamespace = await hash(arrayBufferToHex(
Expand Down Expand Up @@ -136,6 +140,7 @@ export const deployHook = async (account: IAccount & { name?: string }, data: Se
});
}
} catch (err) {
console.log("Ebin")
console.log(err);
state.deployLogs.push({
type: "error",
Expand Down Expand Up @@ -220,4 +225,4 @@ export const deleteHook = async (account: IAccount & { name?: string }) => {
}
return submitRes;
}
};
};
6 changes: 3 additions & 3 deletions utils/languageClient.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { MessageConnection } from "@codingame/monaco-jsonrpc";
import { MonacoLanguageClient, ErrorAction, CloseAction, createConnection } from "@codingame/monaco-languageclient";
import { CloseAction, createConnection, ErrorAction, MonacoLanguageClient } from "@codingame/monaco-languageclient";
import Router from "next/router";
import normalizeUrl from "normalize-url";
import ReconnectingWebSocket from "reconnecting-websocket";
Expand Down Expand Up @@ -37,7 +37,7 @@ export function createUrl(path: string): string {
return normalizeUrl(`${protocol}://${location.host}${location.pathname}${path}`);
}

export function createWebSocket(url: string) {
export function createWebSocket(url: string): ReconnectingWebSocket {
const socketOptions = {
maxReconnectionDelay: 10000,
minReconnectionDelay: 1000,
Expand All @@ -47,4 +47,4 @@ export function createWebSocket(url: string) {
debug: false
};
return new ReconnectingWebSocket(url, [], socketOptions);
}
}