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

Add default extensions for testing inter widget communication #7

Open
itechify opened this issue Aug 24, 2023 · 1 comment
Open

Add default extensions for testing inter widget communication #7

itechify opened this issue Aug 24, 2023 · 1 comment

Comments

@itechify
Copy link
Collaborator

Add two new default extensions to es-home (or es-common-extensions?) for testing iwc channels through shouting (channelShouter) and listening (channelListener).

The channel shouter extension could be based on something like the following but it should be defined like an extension:

import React, { useState, useRef, useEffect } from 'react';
import ReactDOM from "react-dom";
import 'primeicons/primeicons.css';
import 'primereact/resources/themes/lara-dark-indigo/theme.css';
import 'primereact/resources/primereact.css';
//import 'primeflex/primeflex.css';
import "./App.css";
import JSONInput from "react-json-editor-ajrm/index";
import locale from "react-json-editor-ajrm/locale/en";
import { InputText } from 'primereact/inputtext';
import { Button } from 'primereact/button';
import { Toast } from 'primereact/toast';
import {
  initialize,
  publishJson
} from "@moesol/inter-widget-communication";


const mode = import.meta.env.NODE_ENV;

export default function App() {
  useEffect(() => {
    const provider = new URLSearchParams(window.location.search).get('iwc') || 'broadcast';
    const busUrl = new URLSearchParams(window.location.search).get('busUrl') || '/es/ui/bcst-bus/';

    if (provider && busUrl) {
      initialize({
        busUrl,
        provider
      });
    }

  }, []);
  const toast = useRef(null);
  const [payload, setPayload] = useState();
  const [channel, setChannel] = useState('');
  const showError = (error, content) => {
    toast.current.show({ severity: 'error', summary: error });
  }
  const onBroadcast = () => {
    if (channel.length === 0) {
      showError('Channel not specified');
      return;
    }
    if (!payload) {
      showError('Payload not specified');
      return;
    }
    if (payload.error) {
      showError('Error in payloads format');
    }
    // all is ok, lets try to broadcast.
    publishJson(channel, payload.jsObject);


  }

  /**
   * Rendering this JSONInput component with some properties
   */
  return (
    <div>
      <Toast ref={toast} position="bottom-right" />

      <h5>Channel</h5>
      <div className="grid">
      <InputText className="col-fixed" style={{ width: '20em', height: '2.25em', marginLeft: '1em' }}  value={channel} onChange={(e) => setChannel(e.target.value)} />
      <Button className="col-fixed" style={{   marginLeft: '0.5em', height: '2.25em', marginBottom: '0.15em' }} label="Broadcast" className="p-button-sm" onClick={onBroadcast} />
      </div>
      <h5>Payload</h5>
      <div className="grid" style={{ marginLeft: '1em' }} >
      <JSONInput

        locale={locale}
        colors={{
          string: "#DAA520" // overrides theme colors with whatever color value you want
        }}
        height="30em"
        width="45em"

        onChange={setPayload}
      />
    </div>

    </div>
  );
}

ReactDOM.render(<App />, document.querySelector("#root"));

Similarly for the channel listener:

import React, { useState, useRef, useEffect } from 'react';
import ReactDOM from "react-dom";
import 'primeicons/primeicons.css';
import 'primereact/resources/themes/lara-dark-indigo/theme.css';
import 'primereact/resources/primereact.css';
import 'primeflex/primeflex.css';
import "./App.css";
import JSONInput from "react-json-editor-ajrm/index";
import locale from "react-json-editor-ajrm/locale/en";
import { InputText } from 'primereact/inputtext';
import { Button } from 'primereact/button';
import { ListBox } from 'primereact/listbox';
import { DataTable } from 'primereact/datatable';
import { Column } from 'primereact/column';
import { Toast } from 'primereact/toast';
import {
  initialize,
  subscribe,
  unsubscribe
} from "@moesol/inter-widget-communication";
export default function App() {
  const toast = useRef(null);
  const [channel, setChannel] = useState('');
  const [channels, setChannels] = useState([]);
  const [messages, setMessages] = useState([]);
  const [payload, setPayload] = useState();
  const showError = (error, content) => {
    toast.current.show({ severity: 'error', summary: error });
  }

  useEffect(() => {
    const provider = new URLSearchParams(window.location.search).get('iwc') || 'broadcast';
    const busUrl = new URLSearchParams(window.location.search).get('busUrl') || '/es/ui/bcst-bus/';

    if (provider && busUrl) {
      initialize({
        busUrl,
        provider
      });
    }
  },[]);

  const onAdd = () => {
    if (channel.length === 0) {
      showError('Channel not specified');
      return;
    }
    if (channels.find(element => element === channel)) {
      showError('Channel is already specified specified');
      return;
    }
    setChannels(channels => {
      return [...channels, channel];
    });

    subscribe(channel, (sender, payload, channel) => {
      const msg = { channel, payload };
      setMessages(messages => {
        return [msg, ...messages];
      });
      //setPayload(JSON.parse(payload));
    });
  }

  const onClear = () => {
    // lets unscribe all the channels
    channels.forEach((channel) => {
      unsubscribe(channel);
    });
    setChannels([]);
  }
  const onClearMsgs = () => {
    setMessages([]);
  }
  const setSelected = (e) => {
    const payload = JSON.parse(e.value.payload);
    setPayload(payload);
  }
  /**
   * Rendering this JSONInput component with some properties
   */
  return (
    <div>
      <Toast ref={toast} position="bottom-right" />
      <div className="grid" >
        <InputText className="col-fixed" style={{ width: '20em', marginLeft: '1em' }} value={channel} onChange={(e) => setChannel(e.target.value)} />
        <Button style={{ marginLeft: '1em' }} label="Add Channel" className="col-fixed p-button-sm" onClick={onAdd} />
        <Button style={{ marginLeft: '1em' }} label="Clear Channels" className="col-fixed p-button-sm" onClick={onClear} />
        <Button style={{ marginLeft: '1em' }} label="Clear Messages" className="col-fixed p-button-sm" onClick={onClearMsgs} />
      </div>
      <ListBox options={channels} style={{ width: '20em', marginLeft: '0.5em' }} />
      <div className="grid" style={{ marginLeft: '0.20em' }} >
        <DataTable className="col" value={messages} onSelectionChange={e => setSelected(e)} style={{ width: '60em' }} selectionMode="single" scrollable scrollHeight="39.5em" virtualScrollerOptions={{ itemSize: 20 }} responsiveLayout="scroll">
          <Column field="channel" header="Channel"></Column>
          <Column field="payload" header="Payload" ></Column>
        </DataTable>
        <JSONInput
          className="col" 
          placeholder={payload}
          locale={locale}
          colors={{
            string: "#DAA520" // overrides theme colors with whatever color value you want
          }}
          height="40em"
          width="45em"
        />
      </div>
    </div>
  );
}


ReactDOM.render(<App />, document.querySelector("#root"));

For both, we would probably want to simplify the dependencies (no using primereact/icons/flex?). If additional css information is required for the references, I can provide the imported css files too.

Once completed, these extensions should be included in the example application "example" that is brought up from es-compose.

@ldosmorbrian
Copy link

The es-channel-panels repository provides the channelshouter and channellistener application.
Written by Namejs, these have been useful for testing, observing and debugging applications integrated into the Extension Scaffold with the noowf pub/sub library.

I want to comment that we like having these as two separately runnable application panels because that separation helps when debugging various issues in development environments.

  • A typical production environment would typically be behind the same host URL or have other trusts implemented, but for local development and testing, treating these as separate apps is useful.

These are runnable as docker images or as node development servers, and confirming that these can send/receive messages while running on separate ports form the scaffold helps with figuring out and confirming the bus configuration is correct.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants