Realtime Web Suite Client package

Realtime Web Suit client setup and configuration guide

Realtime Web Suit is a web-component powered, MS FAST powered fullstack-oriented framework that you can use to create domain-agnostic modular asynchoronous components with intershared authorized states.

Table of Contents

  1. Overview
  2. Getting Started
  3. Key Components: RWSClient & RoutingService
  4. Component Initialization
  5. DI
  6. Frontend routes
  7. Backend Imports
  8. Utilizing APIService
  9. Notifier
  10. Service Worker
  11. Example: WebChat Component
  12. Other configs
  13. Plugins
  14. Links

Overview

The RWS Frontend Framework is designed to create dynamic and responsive web applications. It integrates seamlessly with the backend and provides a robust set of tools for developing comprehensive web solutions.

Getting Started

To get started with the RWS Frontend Framework, ensure you have the necessary environment set up, including Node.js and any other dependencies specific to the framework.

from your project dir do:

yarn

Initiate cfg files and webpack build:

rws-client init

to install once and then to build after preparing components:

yarn build

or to watch for dev

yarn watch

or to just start server

yarn server

then start engine in the site javascript (can be inline):

window.RWS.client.start(CFG); // it is async function

or for initial setup then start on certain event (example)

window.RWS.client.setup(CFG).then(() => {     // it is async function
$.on('loaded', function(data){
const optionalNewCfg = { backendRoutes: data.backendRoutes };
window.RWSClient.start(optionalNewCfg).then();
})
});

default config for RWS:

const _DEFAULT_CONFIG_VARS = {
//Build configs
dev: false,
hot: false,
report: false,
publicDir: './public',
publicIndex: 'index.html',
outputFileName: 'client.rws.js',
outputDir: process.cwd() + '/build',
//Frontend RWS client configs
backendUrl: null,
wsUrl: null,
partedDirUrlPrefix: '/lib/rws',
partedPrefix: 'rws',
pubUrlFilePrefix: '/',
//Universal configs
transports: ['websocket'],
parted: false,
}

The options description:

Option Description Default
backendUrl Url for backend integration (API calls) null
wsUrl Url for backend integration (Websocket calls) null
backendRoutes Backend routes object imported from backend node for integration with API calls null
apiPrefix Prefix for API calls /
routes Routes for frontend routing {}
transports Websockets transports method ['websockets']
user User object for backend auth / frontend data source null
ignoreRWSComponents Do not declare base RWS components (uploader, progress) false
pubUrlFilePrefix the url for accessing files from browser URL /
pubUrl the url for accessing public dir from browser URL /
outputDir build dir ./build
outputFileName output file name rws.client.js
publicDir public dir for HTML serving ./public
tsConfigPath tsconfig.json path ./tsconfig.njson
entry default TS entry for transpilation ./src/index.ts
parted "Parted" mode if enabled. "Monolith" if disabled. Parted mode outputs every component as separate js file and asynchronously adds them to browser. Monolith is single file js build. false
partedPrefix parted file prefix ([prefix].[cmp name].js) rws
partedDirUrlPrefix URL for generated js parted files directory /
copyAssets An option for defining structure that will be copied after build {}

copyAssets example

"copyAssets": {
"./public/js/": [ // target directory
"./build/", // copy this directory to target
"./src/styles/compiled/main.css" //copy this file to target
]
}

The FRONT config TS interface:

interface IRWSConfig {
defaultLayout?: typeof RWSViewComponent
backendUrl?: string
wsUrl?: string
backendRoutes?: any[]
apiPrefix?: string
routes?: IFrontRoutes
transports?: string[]
user?: any
ignoreRWSComponents?: boolean
pubUrl?: string
pubUrlFilePrefix?: string
partedDirUrlPrefix?: string
dontPushToSW?: boolean
parted?: boolean
partedFileDir?: string
partedPrefix?: string
routing_enabled?: boolean
_noLoad?: boolean
}

The FRONT webpack config:

const path = require('path');

const RWSWebpackWrapper = require('@rws-framework/client/rws.webpack.config');


const executionDir = process.cwd();

module.exports = RWSWebpackWrapper({
tsConfigPath: executionDir + '/tsconfig.json',
entry: `${executionDir}/src/index.ts`,
publicDir: path.resolve(executionDir, 'public'),
outputDir: path.resolve(executionDir, 'build'),
outputFileName: 'jtrainer.client.js'
});

Key Components

RWSClient

RWS.client is the heart of the framework, managing configuration and initialization. It sets up routes, backend connections, and other essential framework services.

RoutingService

RoutingService handles the navigation and routing within your application. It ensures that URL changes reflect the correct component rendering.

Depreciation Notice

RoutingService will be moved to @rws-framework/browser-router near future

WSService

WSService handles Websockets messenging to the backend.

Depreciation Notice WSService will be moved to @rws-framework/nest-interconnectors in near future

APIService

APIService handles API requests to the backend.

Implementing the Framework

Main File:

The main file (index.ts) is where you initialize the RWSClient. Here, you configure your routes, backend routes, and component initializations.

Following is example of full usage of the framework

async function initializeApp() {           
const theClient = RWSContainer().get(RWSClient);

theClient.addRoutes(frontendRoutes);
theClient.setBackendRoutes(backendRoutes());

theClient.enableRouting();

theClient.onInit(async () => {

// For single file output:
initComponents(theClient.appConfig.get('parted')); // start components for monolith mode
theClient.defineComponents(); // start RWS conponents

//custom outside components registering
provideFASTDesignSystem()
.register(fastButton())
.register(fastTab())
.register(fastSlider())
.register(fastSelect())
.register(fastDivider())
.register(fastMenu())
.register(fastMenuItem())
;

// Service worker code
// const swFilePath: string = `${theClient.appConfig.get('pubUrl')}/service_worker.js`;

// await theClient.swService.registerServiceWorker();

//if(theClient.getUser()){
// theClient.pushUserToServiceWorker({...theClient.getUser(), instructor: false});
//}

});

theClient.setNotifier((message: string, logType: NotifyLogType, uiType: NotifyUiType = 'notification', onConfirm: (params: any) => void, notifierOptions: any = {}) => {
switch(uiType){
case 'notification':
let notifType = 'success';

if(logType === 'error'){
notifType = 'error';
}

if(logType === 'warning'){
notifType = 'warning';
}

return alertify.notify(message, notifType, 5, onConfirm);

case 'alert':
const alertObj = alertify.alert('Junction AI Notification', message, onConfirm);

Object.keys(notifierOptions).forEach(key => {
const optionValue = notifierOptions[key];

if(key === 'width'){

alertObj.elements.dialog.style = `max-width: ${optionValue};`;

return;
}

alertObj.set(key, optionValue);
});

alertObj.show();

return alertObj;
case 'silent':
if(logType == 'warning'){
console.warn(message);
}else if(logType == 'error'){
console.error(message);
}else{
console.log(message);
}
return;
}
});

theClient.assignClientToBrowser();
}

initializeApp().catch(console.error);

Component Initialization

In application/_initComponents.ts, you initialize the custom components used in your application. If components added in here will include other components they dont need to be listed here. A component imported in this mode needs to be imported once.

This should be conditioned not to execute imported code when using parted mode.

Default component structure

component-dir/
component.ts
template.html
styles/
layout.scss

WARNING All html templates refer to variable "T" as to FASTElement templating html scope. It contains all the functions FAST templates uses in html. F.e: T.html, T.when, T.repeat

<div class="convo-area-wrap">
<header>
<div class="header-inner"></div>
${T.when(x => x.noChoose === 'false', (item, index) => T.html`<div>
<chat-convo-models :chosenModel="${x => x.chosenModel}"></chat-convo-models>
</div>`)}
<div>
<h2>${ x => x.chatContext ? x.chatContext.label : 'loading...' }</h2>
<h3><strong>${ x => x.messageList.length }</strong> messages in total</h3>
</div>
<fast-divider></fast-divider>
</header>
<section>
<div class="scroll-area">
<div class="scroll-content">
${T.repeat(x => x.messageList, (item, index) => T.html`
<chat-convo-message :contentReturn="${item => item}" :item="${item => item}"/>
`)}

${T.when(x => !x.messageList.length, (item, index) => T.html`
<p class="no-chat">No messages</p>
`)}
</div>
</div>
</section>

</div>

application/_initComponents.ts

Only if parted mode is false.

import { ChatNav } from '../components/chat-nav/component';
import { DefaultLayout } from '../components/default-layout/component';
import { RWSIcon } from '../components/rws-icon/component';
import { LineSplitter } from '../components/line-splitter/component';
import { WebChat } from '../components/webchat/component';

export default (partedMode: boolean = false) => {
if(!partedMode){
WebChat;
LineSplitter;
DefaultLayout;
ChatNav;
RWSIcon;
}
};

**Component needs to extend RWSViewComponent and use

Generated using TypeDoc