The Web UI

From MakeProAudio Wiki
Jump to navigation Jump to search

Overview

A ReactJS based Web UI to speak to a MakeHaus Node JS application. The application is intended and tested to be served by the web server baked into makehaus-js. For development purposes, the application is served by the bootstrapping web server (more details on the development workflow given below).

Branching

The develop branch is the default development branch.

Cloning And Initial Setup

Clone the repository and from the working directory execute

https://github.com/makeproaudio/glue-react-browser.git
npm i

This will pull all necessary dependencies as defined in package.json into the node_modules/ folder.

Repository Structure

A few files are produced by the bootstrapper. Only relevant files used in development are mentioned below

node_modules/
public/
      index.html
      manifest.json
      worker.js
src/
      components/
            layout/
            mpa-custom-ui-widgets/
            mpa-material-ui-widgets/
            mpa-widget-interface/
      style/
      App.js
      index.js
      serviceWorker.js
      style-helper.js
package.json

Typical Development Workflow

With create-react-app, a default development server is provided so that any change you make in sources is immediately deployed and reflected in the development server. To run the development server, enter:

npm start

This should launch the application on port 3000 unless another application uses that port. The development server’s caching is not 100% reliable, therefore it is recommended to adopt the following measures in your development setup. Ensure to revert before making a new build:

  • Work in incognito mode
  • Disable the service worker - Refer to public/index.html for comments on how to do this.
  • The React toolchain does not have a direct breakpoint debugger interaction with VSCode. The recommendation is to add console.log() to trace application flow.

Deployment

Making A Minified Build

To create a new deployment build, ensure that you have reverted any steps you may have taken to ease your development workflow. Execute the following command:

npm run build

This will output a minified version of the web application into a new folder called build in the root working directory folder. If the build folder existed earlier, a fresh build will replace its contents.

Deploying To makehaus-js

Copy the contents of the build folder and paste it into the webapp folder of makehaus-js.

Technology

  • ReactJS as the front end library
  • FlexBox for css based layouting
  • MaterialUI as the standard widget library
  • socket.io for network communication over websockets. Socket IO allows topic based event driven communication.
  • serviceworker.js for Progressive Web App capabilities

Notes and Caveats

  • At the time of writing the application, react 16 was the latest version.
  • create-react-app was used to bootstrap the react project.
  • Widgets have been implemented using MaterialUI but are not limited to MaterialUI. It is perfectly valid to implement widgets with a different library.
  • socket.io was chosen as the standard network communication library for its simplicity and performance.
  • Some package dependencies inside package.json can be pruned as they are probably not being used at the moment.
  • The dockerfile that you find inside the repository was a good to have feature and is not necessary in developing the application. It may not be in a valid state.

Architecture

The most important components in the application are:

  • Connection To Server
  • Layouter
  • Widget Interface
  • Widget Helper and Implementations

Connection To Server

To load the application, the Host URL must be known. This Host URL is determined by the web server baked into makehaus-js and the web app port you supply to your makehaus-js application at the time of initialization. By default, the web app port is 3000.

Once you navigate to the Host URL, you’ll be asked to enter the makehaus websocket port used for communicating to makehaus . By default, the websocket server port is 8001.

For more details on default ports and makehaus-js configuration, refer to the makehaus-js documentation.

After setting up your port correctly, your makehaus-js layout will be transferred to the web application.

MakeHaus Server Connection

The entry point of the web application is componentDidMount() inside App.js Refer to comments in App.js for application flow.

Layouter

Once the initial handshake is over and layout data has been received by the web application, the Layouter takes over. It is the responsibility of the components in /src/components/layout to layout the json received from the server. This is a simple html/css driven implementation.

Widget

The Web UI is primarily server driven and relies on events being transmitted from the server to change its own state. The “rx” topic is a fast lane for communication specifically oriented towards widgets.
The heavy lifting of the code is done by the Widget component in /src/components/mpa-widget-interface/widget.jsx. Refer to comments in this file for application flow.

For the web application, a Widget is a network abstraction layer over the different types of widgets that MakeHaus supports. A Widget can change its type, therefore the Widget component tunes into TYPECHANGE messages coming from the server on the “rx” topic.

Depending on the widget metadata as received by the server, the Widget component spawns instances of standard MPA Widget Interface components. These Widget Interface components are standardized and can be found in the src/components/mpa-widget-interface package.

Example: Slider

Here is a detailed explanation for the Slider MPA Widget Interface component located at src/components/mpa-widget-interface/slider.jsx.

The entry point is the componentDidMount() function. Here, a handler is registered on the “rx” topic to receive any necessary updates from the server. The different events that may come in from the server are:

  • TOUCH - the corresponding hardware widget was touched
  • VALUE - the value of the widget was updated
  • COLOR - a color change was triggered on the widget
  • CONTEXT - the context label of the widget was updated
  • LABEL - the main label of the widget was updated
  • TYPECHANGE - the min/max/values of the widget was updated.

Note that the TYPECHANGE here does not have the same meaning as the TYPECHANGE for the base Widget component.

Not every Widget may support each and every one of these events. Each MPA Widget Interface then chooses to do whatever it pleases with the event.

The render() function is where the actual UI implementation of the Widget is rendered.

Widget Helper and Implementations

The ideology of this architecture is to keep the MPA Widget Interface decoupled from the UI Widget implementation. Currently, most of the UI Widget implementations make use of the Material UI library. However, in the future, if we wish to change from Material UI to another library, the MPA Widget interfaces provide the flexibility to do so without altering the core network communication between the web application and the server.

The UI Widget implementations can be found in the following packages:

src/components/mpa-custom-ui-widgets
src/components/mpa-material-ui-widgets

Supporting A New Widget

Support a new Widget may mean one of two different things:

  1. Are you supporting a new UI implementation of an existing MPA Widget Interface
  2. Are you building a new MPA Widget Interface from scratch?

To create a new UI implementation of an existing MPA Widget Interface, check off the following items:

  • Create a new component for the UI Widget. If you’re implementing a new Material UI based widget, place it under src/components/mpa-material-ui-widgets otherwise if it’s a custom widget, place it under src/components/mpa-custom-ui-widgets. As a convention, if you’re implementing a new widget from an existing baseline UI library, create a new folder under src/components.
  • Let’s assume you’d like to replace the existing UI Button with a different Button. In the render() function of src/components/mpa-widget-interface/button.jsx, replace the return section to return your new component that you’ve just created.
  • For any property of the component that is rendered by a server change(eg. color, label, context, min, max, etc) make sure you reference the correct state property variable of the MPA Widget Interface defined in the constructor in the component that you return in the render() function.

To create a new MPA Widget Interface, check off the following items:

  • Create a new component for the MPA Widget Interface and place it under src/components/mpa-widget-interface
  • In the constructor() function of the new MPA Widget Interface, destructure the props that you want to get from metadata.stack and pass these initial values to the state of the component.
  • In the componentWillUnmount() function of the new MPA Widget Interface, ensure to deregister to the “rx” topic from the socket to avoid memory leaks.
  • In the componentDidMount() function of the new MPA Widget Interface, register a new handler on the “rx” topic and implement the events you wish to listen to. The list of events is specified in the Slider example mentioned above.
  • In the render() function of the new MPA Widget Interface, return the UI Widget Implementation you wish to render. Refer to the section above for details on how to create a new UI Widget implementation.
  • Inside the Widget component under src/components/mpa-widget-interface/widget.jsx, navigate to the widgetForMeta property and add a new condition which catches the name of the widget you send from the server.
  • In the return section of the new conditional clause, return the new MPA Widget Interface you created wrapped inside a div tag.

Reporting Widget Events To The Server

For certain widgets, you may wish to send event callbacks from the UI widget to the server. An example of this can be found in the handleClick() function of the Button component under src/components/mpa-widget-interface/button.jsx.The topic used here is called “tx” and socket.emit() is the method to send data to the server. There is no specific structure to the data that you pass to the server on the “tx” topic, so take sufficient caution to implement the data format appropriately on the server.

Progressive Web App Support

PWAs allow a web application to be run in a native sandbox inside supported mobile browsers without having to install an application via the native application store. Although PWAs are a huge box, the MakeHaus Web UI uses minimal features of PWAs. The relevant files to modify are public/manifest.json, public/worker.js and src/serviceWorker.js. The setup required for making an application a PWA is a one time task and the only relevant changes to be made are in the manifest.json file, which contains metadata regarding what the application name should be, the icon to be displayed, the splash screen, theming on the splash screen, orientation etc. All other files shouldn’t be touched unless a new PWA feature is needed to be implemented.

Outlook

  • Network Discovery: To truly make the application mobile, create a react native wrapper which can do basic discovery of makehaus-js web servers in the network. This will save the user from having to navigate to the Host URL. The UI application can continue to be shown using a WebView widget.
  • Layouter New Feature: Currently, only Rows are supported from the server. If Columns are also to be implemented, the Layouter will need to be modified to become a matrix.
  • New Widgets: Label / Heading, Text Input Field
  • Code Cleanup: Add a prettier config file to implement a standard code formatter
  • Code Cleanup: Map all events from the server to a standardized enum
  • Code Cleanup: Replace React Class Components with pure function components.
  • Code Cleanup: Replace socket.io with useSocket hooks
  • Code Cleanup: Create custom React hooks to implement events in MPA Widget Interface components. In that case, the componentDidMount() function can be split to more atomic functions such as onColor, onLabel, onContext etc.