Skip to content

Commit

Permalink
Swtich from dilation webservice to web assembly
Browse files Browse the repository at this point in the history
* Initialized wasm submodule
* Add npm scripts and webpack config to compile wasm and include to assets
* Update express to fix mime type issue with wasm
* Update react to fix async act (see facebook/react#14853)
* Changed error message component props for less tight coupling
  • Loading branch information
acra5y committed May 7, 2020
1 parent dae8f64 commit 24a73f1
Show file tree
Hide file tree
Showing 21 changed files with 1,085 additions and 289 deletions.
1 change: 1 addition & 0 deletions .eslintignore
@@ -1 +1,2 @@
dist/
wasm/
2 changes: 1 addition & 1 deletion .github/workflows/nodejs.yml
Expand Up @@ -21,7 +21,7 @@ jobs:
run: |
npm ci
npm run build
npm run build-server
npm run build:server
npm test
env:
CI: true
1 change: 1 addition & 0 deletions .gitignore
@@ -1,2 +1,3 @@
coverage/
dist/
main.wasm
3 changes: 3 additions & 0 deletions .gitmodules
@@ -0,0 +1,3 @@
[submodule "n-dilation-webassembly"]
path = n-dilation-webassembly
url = git@github.com:acra5y/n-dilation-webassembly.git
31 changes: 31 additions & 0 deletions app/components/WasmLoader.jsx
@@ -0,0 +1,31 @@
import { useEffect } from "react";

import { useWindowContext } from "./WindowContext";

const WasmLoader = () => {
const window = useWindowContext();

useEffect(() => {
if (!window.Go) {
const script = document.createElement("script");
script.onload = async function() {
const go = new window.Go();
const webAssembly = await window.WebAssembly.instantiateStreaming(
window.fetch("/public/wasm/main.wasm"),
go.importObject
);
go.run(webAssembly.instance); // populates window.UnitaryNDilation
};
script.src = "/public/wasm/wasm_exec.js";
script.type = "text/javascript";

document.head.appendChild(script);
}
}, []);

return null;
};

WasmLoader.displayName = "WasmLoader";

export default WasmLoader;
104 changes: 74 additions & 30 deletions app/components/nDilation/Calculator.jsx
@@ -1,61 +1,105 @@
import React, { useReducer } from "react";
import React, { useReducer, useEffect, useRef } from "react";

import fetchNDilation from "../../lib/fetchNDilation";
import { useWindowContext } from "../WindowContext";
import DilationForm from "./DilationForm";
import Result from "./Result";
import WasmLoader from "../WasmLoader";

const initialState = { isLoading: false, dilation: null, error: null };
const initialState = {
isLoading: false,
dilation: null,
validationError: false,
runtimeError: false,
ready: false,
};

function reducer(state, action) {
switch (action.type) {
case "FETCH_START":
return { isLoading: true, dilation: null, error: null };
case "FETCH_OK":
return { isLoading: false, dilation: action.payload, error: null };
case "FETCH_NOT_OK":
return { isLoading: false, dilation: null, error: action.payload };
case "FETCH_ERROR":
return { isLoading: false, dilation: null, error: action.payload };
case "WASM_INITIALIZED":
return { ...state, ready: true };
case "CALCULATE_START":
return {
...state,
isLoading: true,
dilation: null,
runtimeError: false,
validationError: false,
};
case "CALCULATE_OK":
return {
...state,
isLoading: false,
dilation: action.payload,
error: null,
};
case "VALIDATION_ERROR":
return {
...state,
isLoading: false,
dilation: null,
runtimeError: false,
validationError: true,
};
case "CALCULATE_ERROR":
return {
...state,
ready: false,
isLoading: false,
dilation: null,
runtimeError: true,
validationError: false,
};
default:
return state;
}
}

const createOnSubmitHandler = (fetch, dispatch) => async (matrix, degree) => {
const createOnSubmitHandler = (unitaryNDilationAsync, dispatch) => async (
matrix,
degree
) => {
try {
dispatch({ type: "FETCH_START" });
const response = await fetchNDilation(fetch, matrix, degree);

const body = await response.json();
dispatch({ type: "CALCULATE_START" });
const { error, value } = await unitaryNDilationAsync(matrix, degree);

if (response.ok) {
dispatch({ type: "FETCH_OK", payload: body.value });
if (!error) {
dispatch({ type: "CALCULATE_OK", payload: value });
} else {
dispatch({ type: "FETCH_NOT_OK", payload: body });
dispatch({ type: "VALIDATION_ERROR", payload: error });
}
} catch (e) {
dispatch({ type: "FETCH_ERROR", payload: e });
dispatch({ type: "CALCULATE_ERROR", payload: e });
}
};

const Calculator = () => {
const [{ isLoading, dilation, error }, dispatch] = useReducer(
reducer,
initialState
);
const [
{ isLoading, dilation, validationError, runtimeError },
dispatch,
] = useReducer(reducer, initialState);
const onSubmitHandler = useRef(null);
const window = useWindowContext();

useEffect(() => {
if (!onSubmitHandler.current) {
const unitaryNDilationAsync = (matrix, degree) =>
Promise.resolve(window.UnitaryNDilation(matrix, degree));
onSubmitHandler.current = createOnSubmitHandler(
unitaryNDilationAsync,
dispatch
);
dispatch({ type: "WASM_INITIALIZED" });
}
}, [window && window.UnitaryNDilation]);

return (
<div>
<DilationForm
onSubmit={
window && createOnSubmitHandler(window.fetch, dispatch)
}
/>
<WasmLoader />
<DilationForm onSubmit={onSubmitHandler.current} />
<Result
isLoading={isLoading}
errorDetails={error}
validationError={validationError}
runtimeError={runtimeError}
dilation={dilation}
/>
</div>
Expand Down
11 changes: 8 additions & 3 deletions app/components/nDilation/ErrorMessage.jsx
@@ -1,8 +1,13 @@
import React from "react";

function ErrorMessage({ errorDetails }) {
if (!errorDetails.validationError) {
return <div>Ooops, something went terribly wrong 🤯</div>;
function ErrorMessage({ validationError }) {
if (!validationError) {
return (
<div>
Ooops, something went terribly wrong 🤯. You will probably have
to reload the page now.
</div>
);
}

return (
Expand Down
10 changes: 6 additions & 4 deletions app/components/nDilation/Result.jsx
Expand Up @@ -15,7 +15,8 @@ const AnimatedContentTransition = styled.div`
export const Result = ({
isLoading,
dilation,
errorDetails,
validationError,
runtimeError,
opacity,
animationTimeInSeconds,
}) => {
Expand All @@ -29,9 +30,10 @@ export const Result = ({
) : (
<>
{dilation && <Matrix matrixInRowMajorOrder={dilation} />}
{errorDetails && (
<ErrorMessage errorDetails={errorDetails} />
)}
{validationError ||
(runtimeError && (
<ErrorMessage validationError={validationError} />
))}
</>
)}
</AnimatedContentTransition>
Expand Down

0 comments on commit 24a73f1

Please sign in to comment.