mediasfu-angular v2.1.6
MediaSFU offers a cutting-edge streaming experience that empowers users to customize their recordings and engage their audience with high-quality streams. Whether you're a content creator, educator, or business professional, MediaSFU provides the tools you need to elevate your streaming game.
MediaSFU Angular Module Documentation
Unlock the Power of MediaSFU Community Edition
MediaSFU Community Edition is free and open-source—perfect for developers who want to run their own media server without upfront costs. With robust features and simple setup, you can launch your media solution in minutes. Ready to scale? Upgrade seamlessly to MediaSFU Cloud for enterprise-grade performance and global scalability.
Table of Contents
- Features
- Getting Started
- Basic Usage Guide
- Intermediate Usage Guide
- Advanced Usage Guide
- API Reference
- Troubleshooting
- Contributing
Features
MediaSFU's Angular SDK comes with a host of powerful features out of the box:
- Screen Sharing with Annotation Support: Share your screen with participants and annotate in real-time for enhanced presentations and collaborations.
- Collaborative Whiteboards: Create and share whiteboards for real-time collaborative drawing and brainstorming sessions.
- Breakout Rooms: Create multiple sub-meetings within a single session to enhance collaboration and focus.
- Pagination: Efficiently handle large participant lists with seamless pagination.
- Polls: Conduct real-time polls to gather instant feedback from participants.
- Media Access Requests Management: Manage media access requests with ease to ensure smooth operations.
- Video Effects: Apply various video effects, including virtual backgrounds, to enhance the visual experience.
- Chat (Direct & Group): Facilitate communication with direct and group chat options.
- Cloud Recording (track-based): Customize recordings with track-based options, including watermarks, name tags, background colors, and more.
- Managed Events: Manage events with features to handle abandoned and inactive participants, as well as enforce time and capacity limits.
Getting Started
This section will guide users through the initial setup and installation of the npm module.
Documentation Reference
For comprehensive documentation on the available methods, components, and functions, please visit mediasfu.com. This resource provides detailed information for this guide and additional documentation.
Installation
Instructions on how to install the module using npm.
1. Add the package to your project
```bash
npm install mediasfu-angular
```
2. Bootstrap Integration
The `mediasfu-angular` package requires Bootstrap for styling. Bootstrap is included by default with the package, so you do not need to install it separately. Ensure that Bootstrap's CSS is correctly added to your project's styles.
1. **Check `angular.json`:**
Ensure that `node_modules/bootstrap/dist/css/bootstrap.min.css` is listed in the `styles` array of your Angular application's build options.
```json
{
"projects": {
"your-app-name": {
"architect": {
"build": {
"options": {
"styles": [
"node_modules/bootstrap/dist/css/bootstrap.min.css",
"src/styles.css"
],
// ... other configurations
}
}
}
}
}
}
```
Note: The mediasfu-angular
package should handle the Bootstrap's package installation automatically. If it's not present, you may need to add it manually install Bootstrap.
3. Configure MediaSFU's PreJoinPage Requirements
If you intend to use MediaSFU's `PreJoinPage` component, additional configuration is required. You need to provide the `HttpClient` and `CookieService` providers in your application's configuration. These packages should have been installed by default as well else add manually.
#### Update `app.config.ts`
Add the necessary providers to your `app.config.ts` file. Below is an example configuration:
```typescript
// app.config.ts
import { ApplicationConfig } from '@angular/core';
import { provideZoneChangeDetection } from '@angular/core';
import { provideClientHydration } from '@angular/platform-browser';
import { provideHttpClient } from '@angular/common/http';
import { CookieService } from 'ngx-cookie-service';
export const appConfig: ApplicationConfig = {
providers: [
provideZoneChangeDetection({ eventCoalescing: true }),
provideClientHydration(),
provideHttpClient(),
CookieService
],
};
```
4. Obtain an API Key (If Required)
You can get your API key by signing up or logging into your account at mediasfu.com.
Self-Hosting MediaSFU
If you plan to self-host MediaSFU or use it without MediaSFU Cloud services, you don't need an API key. You can access the open-source version of MediaSFU from the MediaSFU Open Repository.
This setup allows full flexibility and customization while bypassing the need for cloud-dependent credentials.
Basic Usage Guide
A basic guide on how to use the module for common tasks.
This section will guide users through the initial setup and installation of the npm module.
Introduction
MediaSFU is a 2-page application consisting of a prejoin/welcome page and the main events room page. This guide will walk you through the basic usage of the module for setting up these pages.
Documentation Reference
For comprehensive documentation on the available methods, components, and functions, please visit mediasfu.com. This resource provides detailed information for this guide and additional documentation.
Prebuilt Event Rooms
MediaSFU provides prebuilt event rooms for various purposes. These rooms are rendered as full pages and can be easily imported and used in your application. Here are the available prebuilt event rooms:
- MediasfuGeneric: A generic event room suitable for various types of events.
- MediasfuBroadcast: A room optimized for broadcasting events.
- MediasfuWebinar: Specifically designed for hosting webinars.
- MediasfuConference: Ideal for hosting conferences.
- MediasfuChat: A room tailored for interactive chat sessions.
Users can easily pick an interface and render it in their app.
If no API credentials are provided, a default home page will be displayed where users can scan or manually enter the event details.
To use these prebuilt event rooms, simply import them into your application:
## Simplest Usage
The simplest way to use MediaSFU in your Angular application is by directly rendering the prebuilt event room component, such as `MediasfuGeneric`.
```typescript
// app.component.ts
import { Component } from '@angular/core';
import { MediasfuGeneric } from 'mediasfu-angular';
@Component({
selector: 'app-root',
standalone: true,
imports: [MediasfuGeneric],
template: `
<app-mediasfu-generic></app-mediasfu-generic>
`,
})
export class AppComponent { }
Programmatically Fetching Tokens
If you prefer to fetch the required tokens programmatically without visiting MediaSFU's website, you can use the PreJoinPage
component and pass your credentials as inputs.
// app.component.ts
import { Component } from '@angular/core';
import { MediasfuGeneric, PreJoinPage } from 'mediasfu-angular';
@Component({
selector: 'app-root',
standalone: true,
imports: [MediasfuGeneric],
template: `
<div class="container">
<app-mediasfu-generic
[PrejoinPage]="PreJoinPage"
[credentials]="credentials">
</app-mediasfu-generic>
</div>
`,
})
export class AppComponent {
// Reference to the PreJoinPage component
PreJoinPage = PreJoinPage;
// MediaSFU account credentials
credentials = {
apiUserName: 'yourAPIUserName',
apiKey: 'yourAPIKey',
};
}
Preview of Welcome Page
Preview of Prejoin Page
Custom Welcome/Prejoin Page
Alternatively, you can design your own welcome/prejoin page. The core function of this page is to fetch user tokens from MediaSFU's API and establish a connection with the returned link if valid.
Parameters Passed to Custom Page
MediaSFU passes relevant parameters to the custom welcome/prejoin page:
let { showAlert, updateIsLoadingModalVisible, connectSocket, updateSocket, updateValidated,
updateApiUserName, updateApiToken, updateLink, updateRoomName, updateMember } = parameters;
Ensure that your custom page implements the following updates:
updateSocket(socket);
updateApiUserName(apiUserName);
updateApiToken(apiToken);
updateLink(link);
updateRoomName(apiUserName);
updateMember(userName);
updateValidated(true);
See the following code for the PreJoinPage page logic:
import { Component, Inject, Input, OnInit, Optional } from '@angular/core';
import { FormBuilder, FormGroup, Validators, ReactiveFormsModule } from '@angular/forms';
import { CommonModule } from '@angular/common';
import { Socket } from 'socket.io-client';
import {
ConnectSocketType, ShowAlert,
ConnectLocalSocketType, ResponseLocalConnection,
ResponseLocalConnectionData, RecordingParams, MeetingRoomParams,
CreateMediaSFURoomOptions,JoinMediaSFURoomOptions,
} from '../../../@types/types';
import { CheckLimitsAndMakeRequest } from '../../../methods/utils/check-limits-and-make-request.service';
import { CreateRoomOnMediaSFU } from '../../../methods/utils/create-room-on-media-sfu.service';
import { CreateRoomOnMediaSFUType, JoinRoomOnMediaSFUType, JoinRoomOnMediaSFU } from '../../../methods/utils/join-room-on-media-sfu.service';
import { BehaviorSubject } from 'rxjs/internal/BehaviorSubject';
export interface JoinLocalEventRoomParameters {
eventID: string;
userName: string;
secureCode?: string;
videoPreference?: string | null;
audioPreference?: string | null;
audioOutputPreference?: string | null;
}
export interface JoinLocalEventRoomOptions {
joinData: JoinLocalEventRoomParameters;
link?: string;
}
export interface CreateLocalRoomParameters {
eventID: string;
duration: number;
capacity: number;
userName: string;
scheduledDate: Date;
secureCode: string;
waitRoom?: boolean;
recordingParams?: RecordingParams;
eventRoomParams?: MeetingRoomParams;
videoPreference?: string | null;
audioPreference?: string | null;
audioOutputPreference?: string | null;
mediasfuURL?: string;
}
export interface CreateLocalRoomOptions {
createData: CreateLocalRoomParameters;
link?: string;
}
export interface CreateJoinLocalRoomResponse {
success: boolean;
secret: string;
reason?: string;
url?: string;
}
// Type definitions for parameters and credentials
export interface PreJoinPageParameters {
imgSrc?: string;
showAlert?: ShowAlert;
updateIsLoadingModalVisible: (visible: boolean) => void;
connectSocket: ConnectSocketType;
connectLocalSocket?: ConnectLocalSocketType;
updateSocket: (socket: Socket) => void;
updateLocalSocket?: (socket: Socket) => void;
updateValidated: (validated: boolean) => void;
updateApiUserName: (userName: string) => void;
updateApiToken: (token: string) => void;
updateLink: (link: string) => void;
updateRoomName: (roomName: string) => void;
updateMember: (member: string) => void;
}
export interface Credentials {
apiUserName: string;
apiKey: string;
}
export interface PreJoinPageOptions {
localLink?: string;
connectMediaSFU?: boolean;
parameters: PreJoinPageParameters;
credentials?: Credentials;
returnUI?: boolean;
noUIPreJoinOptions?: CreateMediaSFURoomOptions | JoinMediaSFURoomOptions;
createMediaSFURoom?: CreateRoomOnMediaSFUType;
joinMediaSFURoom?: JoinRoomOnMediaSFUType;
}
export type PreJoinPageType = (options: PreJoinPageOptions) => HTMLElement;
/**
* @fileoverview PreJoinPage component for handling room creation and joining on MediaSFU.
*
* @component
* @selector app-pre-join-page
* @standalone true
* @templateUrl ./pre-join-page.component.html
* @styleUrls ./pre-join-page.component.css
* @imports [CommonModule, ReactiveFormsModule]
*
* @description
* This component provides functionality for users to create or join a room on MediaSFU.
* It includes form validation, error handling, and API requests to the MediaSFU service.
*
* @property {any} parameters - Input parameters for the component.
* @property {Object} credentials - API credentials for MediaSFU.
* @property {string} credentials.apiUserName - API username.
* @property {string} credentials.apiKey - API key.
* @property {boolean} isCreateMode - Flag to toggle between create and join modes.
* @property {FormGroup} preJoinForm - Form group for pre-join form.
* @property {string} error - Error message to display.
*
* @constructor
* @param {FormBuilder} fb - FormBuilder service for creating form groups.
* @param {HttpClient} http - HttpClient service for making HTTP requests.
* @param {CookieService} cookieService - CookieService for managing cookies.
*
* @method ngOnInit
* @description Lifecycle hook that is called after data-bound properties are initialized.
*
* @method toggleMode
* @description Toggles between create and join modes and resets the error message.
*
* @method handleCreateRoom
* @description Handles the creation of a room on MediaSFU. Validates form inputs, sends a request to create a room, and handles the response.
*
* @method handleJoinRoom
* @description Handles joining a room on MediaSFU. Validates form inputs, sends a request to join a room, and handles the response.
*
* @method checkLimitsAndMakeRequest
* @description Checks rate limits and makes a request to connect to a room. Handles unsuccessful attempts and updates the state accordingly.
*
* @method createRoomOnMediaSFU
* @description Sends a request to create a room on MediaSFU.
* @param {Object} params - Parameters for the request.
* @param {any} params.payload - Payload for the request.
* @param {string} params.apiUserName - API username.
* @param {string} params.apiKey - API key.
* @returns {Promise<{ data: CreateJoinRoomResponse | CreateJoinRoomError | null; success: boolean }>} Response from the API.
*
* @method joinRoomOnMediaSFU
* @description Sends a request to join a room on MediaSFU.
* @param {Object} params - Parameters for the request.
* @param {any} params.payload - Payload for the request.
* @param {string} params.apiUserName - API username.
* @param {string} params.apiKey - API key.
* @returns {Promise<{ data: CreateJoinRoomResponse | CreateJoinRoomError | null; success: boolean }>} Response from the API.
*
* @example
* ```html
* <app-pre-join-page
* [parameters]="preJoinPageParameters"
* [credentials]="{ apiUserName: 'username', apiKey: 'apiKey' }"
* [localLink]="'http://localhost:3000'"
* [connectMediaSFU]="false"
* ></app-pre-join-page>
* ```
*/
@Component({
selector: 'app-pre-join-page',
templateUrl: './pre-join-page.component.html',
styleUrls: ['./pre-join-page.component.css'],
imports: [CommonModule, ReactiveFormsModule]
})
export class PreJoinPage implements OnInit {
@Input() parameters: PreJoinPageParameters = {} as PreJoinPageParameters;
@Input() credentials: Credentials = { apiUserName: 'yourAPIUSERNAME', apiKey: 'yourAPIKEY' };
@Input() localLink: string | undefined = "";
@Input() connectMediaSFU: boolean | undefined = true;
@Input() returnUI?: boolean;
@Input() noUIPreJoinOptions?: CreateMediaSFURoomOptions | JoinMediaSFURoomOptions;
@Input() createMediaSFURoom?: CreateRoomOnMediaSFUType;
@Input() joinMediaSFURoom?: JoinRoomOnMediaSFUType;
isCreateMode = false;
preJoinForm: FormGroup;
error = '';
imgSrc: string = this.parameters.imgSrc || '';
localConnected = false;
localData: ResponseLocalConnectionData | undefined = undefined;
initSocket: Socket | undefined = undefined;
pending = new BehaviorSubject<boolean>(false);
constructor(
private fb: FormBuilder,
@Optional() @Inject('parameters') injectedParameters: PreJoinPageParameters,
@Optional() @Inject('credentials') injectedCredentials: Credentials,
@Optional() @Inject('localLink') injectedLocalLink: string,
@Optional() @Inject('connectMediaSFU') injectedConnectMediaSFU: boolean,
@Optional() @Inject('returnUI') injectedReturnUI: boolean,
@Optional() @Inject('noUIPreJoinOptions') injectedNoUIPreJoinOptions: CreateMediaSFURoomOptions | JoinMediaSFURoomOptions,
@Optional() @Inject('createMediaSFURoom') injectedCreateMediaSFURoom: CreateRoomOnMediaSFUType,
@Optional() @Inject('joinMediaSFURoom') injectedJoinMediaSFURoom: JoinRoomOnMediaSFUType,
private checkLimitsService: CheckLimitsAndMakeRequest,
private createRoomService: CreateRoomOnMediaSFU,
private joinRoomService: JoinRoomOnMediaSFU
) {
this.preJoinForm = this.fb.group({
name: ['', Validators.required],
duration: [''],
eventType: [''],
capacity: [''],
eventID: [''],
});
this.parameters = injectedParameters || this.parameters;
this.credentials = injectedCredentials || this.credentials;
this.localLink = injectedLocalLink || this.localLink;
this.connectMediaSFU = injectedConnectMediaSFU !== undefined ? injectedConnectMediaSFU : this.connectMediaSFU;
this.returnUI = injectedReturnUI !== undefined ? injectedReturnUI : this.returnUI;
this.noUIPreJoinOptions = injectedNoUIPreJoinOptions || this.noUIPreJoinOptions;
this.createMediaSFURoom = injectedCreateMediaSFURoom || this.createMediaSFURoom;
this.joinMediaSFURoom = injectedJoinMediaSFURoom || this.joinMediaSFURoom;
}
ngOnInit(): void {
// If we have a localLink and not connected yet, try to connect
if (this.localLink && !this.localConnected && !this.initSocket) {
this.connectLocalSocket().then(() => {
this.checkProceed();
});
} else {
// If no localLink or already connected, try to proceed
this.checkProceed();
}
}
private async connectLocalSocket(): Promise<void> {
try {
const response = await this.parameters.connectLocalSocket?.({ link: this.localLink! }) as ResponseLocalConnection;
if (response) {
this.localData = response.data;
this.initSocket = response.socket;
this.localConnected = true;
}
} catch (error: any) {
this.parameters.showAlert?.({
message: `Unable to connect to ${this.localLink}. ${error}`,
type: 'danger',
duration: 3000,
});
}
}
private async checkProceed(): Promise<void> {
// If we do not need to return UI and we have noUIPreJoinOptions, proceed like in the React code
if (!this.returnUI && this.noUIPreJoinOptions) {
if ('action' in this.noUIPreJoinOptions && this.noUIPreJoinOptions.action === 'create') {
const createOptions = this.noUIPreJoinOptions as CreateMediaSFURoomOptions;
if (!createOptions.userName || !createOptions.duration || !createOptions.eventType || !createOptions.capacity) {
throw new Error('Please provide all the required parameters: userName, duration, eventType, capacity');
}
await this.handleCreateRoom();
} else if ('action' in this.noUIPreJoinOptions && this.noUIPreJoinOptions.action === 'join') {
const joinOptions = this.noUIPreJoinOptions as JoinMediaSFURoomOptions;
if (!joinOptions.userName || !joinOptions.meetingID) {
throw new Error('Please provide all the required parameters: userName, meetingID');
}
await this.handleJoinRoom();
} else {
throw new Error('Invalid options provided for creating/joining a room without UI.');
}
}
}
toggleMode(): void {
this.isCreateMode = !this.isCreateMode;
this.error = '';
}
async joinLocalRoom(options: JoinLocalEventRoomOptions): Promise<void> {
this.initSocket?.emit('joinEventRoom', options.joinData, (response: CreateJoinLocalRoomResponse) => {
if (response.success) {
this.parameters.updateSocket(this.initSocket!);
this.parameters.updateApiUserName(this.localData?.apiUserName || '');
this.parameters.updateApiToken(response.secret);
this.parameters.updateLink(options.link || '');
this.parameters.updateRoomName(options.joinData.eventID);
this.parameters.updateMember(options.joinData.userName);
this.parameters.updateIsLoadingModalVisible(false);
this.parameters.updateValidated(true);
} else {
this.parameters.updateIsLoadingModalVisible(false);
this.error = `Unable to join room. ${response.reason}`;
}
});
}
async createLocalRoom(options: CreateLocalRoomOptions): Promise<void> {
this.initSocket?.emit('createRoom', options.createData, (response: CreateJoinLocalRoomResponse) => {
if (response.success) {
this.parameters.updateSocket(this.initSocket!);
this.parameters.updateApiUserName(this.localData?.apiUserName || '');
this.parameters.updateApiToken(response.secret);
this.parameters.updateLink(options.link || '');
this.parameters.updateRoomName(options.createData.eventID);
// Update member as `userName` + "_2" to split in the room
this.parameters.updateMember(`${options.createData.userName}_2`);
this.parameters.updateIsLoadingModalVisible(false);
this.parameters.updateValidated(true);
} else {
this.parameters.updateIsLoadingModalVisible(false);
this.error = `Unable to create room. ${response.reason}`;
}
});
}
async roomCreator(options: { payload: any; apiUserName: string; apiKey: string; validate?: boolean }): Promise<any> {
const { payload, apiUserName, apiKey, validate = true } = options;
if (!this.createMediaSFURoom) {
this.createMediaSFURoom = this.createRoomService.createRoomOnMediaSFU;
}
const response = await this.createMediaSFURoom({
payload,
apiUserName,
apiKey,
localLink: this.localLink,
});
if (response.success && response.data && 'roomName' in response.data) {
await this.checkLimitsService.checkLimitsAndMakeRequest({
apiUserName: response.data.roomName,
apiToken: response.data.secret,
link: response.data.link,
userName: payload.userName,
parameters: this.parameters,
validate: validate,
});
return response;
} else {
this.parameters.updateIsLoadingModalVisible(false);
this.error = `Unable to create room. ${
response.data
? 'error' in response.data
? response.data.error
: ''
: ''
}`;
}
}
async handleCreateRoom(): Promise<void> {
if (this.pending.value) {
return;
}
this.pending.next(true);
let payload = {} as CreateMediaSFURoomOptions;
if (this.returnUI) {
const { name, duration, eventType, capacity } = this.preJoinForm.value;
if (!name || !duration || !eventType || !capacity) {
this.error = 'Please fill all the fields.';
return;
}
payload = {
action: 'create',
duration: parseInt(duration),
capacity: parseInt(capacity),
eventType,
userName: name,
recordOnly: false,
};
} else {
if (this.noUIPreJoinOptions && 'action' in this.noUIPreJoinOptions && this.noUIPreJoinOptions.action === 'create') {
payload = this.noUIPreJoinOptions as CreateMediaSFURoomOptions;
} else {
this.error = 'Invalid options provided for creating a room without UI.';
this.pending.next(false);
return;
}
}
this.parameters.updateIsLoadingModalVisible(true);
if (this.localLink) {
const secureCode =
Math.random().toString(30).substring(2, 14) +
Math.random().toString(30).substring(2, 14);
let eventID =
new Date().getTime().toString(30) +
new Date().getUTCMilliseconds() +
Math.floor(10 + Math.random() * 99).toString();
eventID = 'm' + eventID;
const eventRoomParams = this.localData?.meetingRoomParams_;
eventRoomParams!.type = payload.eventType as 'chat' | 'broadcast' | 'webinar' | 'conference';
const createData: CreateLocalRoomParameters = {
eventID: eventID,
duration: payload.duration,
capacity: payload.capacity,
userName: payload.userName,
scheduledDate: new Date(),
secureCode: secureCode,
waitRoom: false,
recordingParams: this.localData?.recordingParams_,
eventRoomParams: eventRoomParams,
videoPreference: null,
audioPreference: null,
audioOutputPreference: null,
mediasfuURL: '',
};
if (
this.connectMediaSFU &&
this.initSocket &&
this.localData &&
this.localData.apiUserName &&
this.localData.apiKey
) {
payload.recordOnly = true; // allow production to MediaSFU only; no consumption
const response = await this.roomCreator({
payload,
apiUserName: this.localData.apiUserName,
apiKey: this.localData.apiKey,
validate: false,
});
if (response && response.success && response.data && 'roomName' in response.data) {
createData.eventID = response.data.roomName;
createData.secureCode = response.data.secureCode;
createData.mediasfuURL = response.data.publicURL;
await this.createLocalRoom({ createData: createData, link: response.data.link });
} else {
this.pending.next(false);
this.parameters.updateIsLoadingModalVisible(false);
this.error = 'Unable to create room on MediaSFU.';
try {
this.parameters.updateSocket(this.initSocket!);
await this.createLocalRoom({ createData: createData });
} catch (error: any) {
this.pending.next(false);
this.parameters.updateIsLoadingModalVisible(false);
this.error = `Unable to create room. ${error}`;
}
}
} else {
try {
this.parameters.updateSocket(this.initSocket!);
await this.createLocalRoom({ createData: createData });
} catch (error: any) {
this.pending.next(false);
this.parameters.updateIsLoadingModalVisible(false);
this.error = `Unable to create room. ${error}`;
}
}
} else {
await this.roomCreator({
payload,
apiUserName: this.credentials.apiUserName,
apiKey: this.credentials.apiKey,
validate: true,
});
this.pending.next(false);
}
}
async handleJoinRoom(): Promise<void> {
if (this.pending.value) {
return;
}
this.pending.next(true);
let payload = {} as JoinMediaSFURoomOptions;
if (this.returnUI) {
const { name, eventID } = this.preJoinForm.value;
if (!name || !eventID) {
this.error = 'Please fill all the fields.';
return;
}
payload = {
action: 'join',
meetingID: eventID,
userName: name,
};
} else {
if (this.noUIPreJoinOptions && 'action' in this.noUIPreJoinOptions && this.noUIPreJoinOptions.action === 'join') {
payload = this.noUIPreJoinOptions as JoinMediaSFURoomOptions;
} else {
this.error = 'Invalid options provided for joining a room without UI.';
this.pending.next(false);
return;
}
}
if (this.localLink && !this.localLink.includes('mediasfu.com')) {
const joinData: JoinLocalEventRoomParameters = {
eventID: payload.meetingID,
userName: payload.userName,
secureCode: '',
videoPreference: null,
audioPreference: null,
audioOutputPreference: null,
};
await this.joinLocalRoom({ joinData: joinData });
this.pending.next(false);
return;
}
this.parameters.updateIsLoadingModalVisible(true);
try {
if (!this.joinMediaSFURoom) {
this.joinMediaSFURoom = this.joinRoomService.joinRoomOnMediaSFU;
}
const response = await this.joinMediaSFURoom({
payload,
apiUserName: this.credentials.apiUserName,
apiKey: this.credentials.apiKey,
localLink: this.localLink,
});
if (response.success && response.data && 'roomName' in response.data) {
await this.checkLimitsService.checkLimitsAndMakeRequest({
apiUserName: response.data.roomName,
apiToken: response.data.secret,
link: response.data.link,
userName: payload.userName,
parameters: this.parameters,
validate: true,
});
this.error = '';
this.pending.next(false);
} else {
this.parameters.updateIsLoadingModalVisible(false);
this.pending.next(false);
this.error = `Unable to connect to room. ${
response.data ? ('error' in response.data ? response.data.error : '') : ''
}`;
}
} catch (error) {
this.parameters.updateIsLoadingModalVisible(false);
this.error = `Unable to connect. ${(error as any).message}`;
}
}
}
IP Blockage Warning And Local UI Development
Note: Local UI Development Mode is deprecated. Rather use your own Community Edition (CE) server for UI development and testing. You can later switch to MediaSFU Cloud for production. Nothing changes in the codebase, and you can use the same code for both environments.
Entering the event room without the correct credentials may result in IP blockage, as the page automatically attempts to connect with MediaSFU servers, which rate limit bad requests based on IP address.
If users attempt to enter the event room without valid credentials or tokens, it may lead to IP blockage due to MediaSFU servers' rate limiting mechanism. To avoid unintentional connections to MediaSFU servers during UI development, users can pass the useLocalUIMode
parameter as true
.
In this mode, the module will operate locally without making requests to MediaSFU servers. However, to render certain UI elements such as messages, participants, requests, etc., users may need to provide seed data. They can achieve this by importing random data generators and passing the generated data to the event room component.
Example for Generic UI to Render Broadcast Room
import { Component, OnInit } from '@angular/core';
import {
MediasfuBroadcast,
GenerateRandomParticipants,
GenerateRandomMessages,
GenerateRandomRequestList,
GenerateRandomWaitingRoomList
} from 'mediasfu-angular';
@Component({
selector: 'app-root',
standalone: true,
imports: [MediasfuBroadcast],
template: `
<div class="container">
<app-mediasfu-broadcast
[useLocalUIMode]="useLocalUIMode"
[useSeed]="useSeed"
[seedData]="seedData">
</app-mediasfu-broadcast>
</div>
`,
providers: [
GenerateRandomParticipants,
GenerateRandomMessages,
],
})
export class AppComponent implements OnInit {
// Whether to use seed data for generating random participants and messages
useSeed = true;
seedData: any = {};
eventType = 'broadcast';
// Whether to use local UI mode (prevents requests to MediaSFU servers)
useLocalUIMode = false;
constructor(
private generateRandomParticipants: GenerateRandomParticipants,
private generateRandomMessages: GenerateRandomMessages,
private generateRandomRequestList: GenerateRandomRequestList,
private generateRandomWaitingRoomList: GenerateRandomWaitingRoomList
) { }
ngOnInit(): void {
if (this.useSeed) {
const memberName = 'Alice';
const hostName = 'Fred';
// Generate random participants
const participants_ =
this.generateRandomParticipants.generateRandomParticipants({
member: memberName,
coHost: '',
host: hostName,
forChatBroadcast:
this.eventType === 'broadcast' || this.eventType === 'chat',
});
// Generate random messages
const messages_ = this.generateRandomMessages.generateRandomMessages({
participants: participants_,
member: memberName,
host: hostName,
forChatBroadcast:
this.eventType === 'broadcast' || this.eventType === 'chat',
});
// Generate random request list
const requests_ =
this.generateRandomRequestList.generateRandomRequestList({
participants: participants_,
hostName: memberName,
coHostName: '',
numberOfRequests: 3,
});
// Generate random waiting room list
const waitingList_ =
this.generateRandomWaitingRoomList.generateRandomWaitingRoomList();
// Assign generated data to seedData
this.seedData = {
participants: participants_,
messages: messages_,
requests: requests_,
waitingList: waitingList_,
member: memberName,
host: hostName,
eventType: this.eventType,
};
}
// Determine whether to use local UI mode
this.useLocalUIMode = this.useSeed;
}
}
Example for Generic View
import { Component, OnInit } from '@angular/core';
import {
GenerateRandomParticipants,
GenerateRandomMessages,
GenerateRandomRequestList,
GenerateRandomWaitingRoomList,
MediasfuGeneric,
MediasfuBroadcast,
MediasfuChat,
MediasfuWebinar,
MediasfuConference,
PreJoinPage,
} from 'mediasfu-angular';
// Assume all missing imports are similar to the previous example
/**
* AppComponent
*
* This component demonstrates how to:
* - Configure credentials for MediaSFU Cloud and/or Community Edition (CE).
* - Use MediaSFU with or without a custom server.
* - Integrate a pre-join page.
* - Disable the default MediaSFU UI and manage state through `sourceParameters` for a fully custom frontend.
*
* Basic instructions:
* 1. Set `localLink` to your CE server if you have one, or leave it blank to use MediaSFU Cloud.
* 2. Set `connectMediaSFU` to determine whether you're connecting to MediaSFU Cloud services.
* 3. Provide credentials if using MediaSFU Cloud (dummy credentials are acceptable in certain scenarios).
* 4. If you prefer a custom UI, set `returnUI` to false and handle all interactions via `sourceParameters` and `updateSourceParameters`.
* 5. For secure production usage, use custom `createMediaSFURoom` and `joinMediaSFURoom` functions to forward requests through your backend.
*/
@Component({
selector: 'app-root',
imports: [MediasfuGeneric],
template: `
<app-mediasfu-generic
[PrejoinPage]="PreJoinPage"
[credentials]="credentials"
[localLink]="localLink"
[connectMediaSFU]="connectMediaSFU"
[returnUI]="returnUI"
[noUIPreJoinOptions]="!returnUI ? noUIPreJoinOptions : undefined"
[sourceParameters]="!returnUI ? sourceParameters : undefined"
[updateSourceParameters]="!returnUI ? updateSourceParameters : undefined"
[createMediaSFURoom]="createRoomOnMediaSFU.createRoomOnMediaSFU"
[joinMediaSFURoom]="joinRoomOnMediaSFU.joinRoomOnMediaSFU">
</app-mediasfu-generic>
`,
providers: [
GenerateRandomParticipants,
GenerateRandomMessages,
GenerateRandomRequestList,
GenerateRandomWaitingRoomList
],
})
export class AppComponent implements OnInit {
// =========================================================
// API CREDENTIALS CONFIGURATION
// =========================================================
//
// Scenario A: Not using MediaSFU Cloud at all.
// - No credentials needed. Just set localLink to your CE server.
// Example:
/*
credentials = {};
localLink = 'http://your-ce-server.com'; // e.g., 'http://localhost:3000'
connectMediaSFU = localLink.trim() !== '';
*/
// Scenario B: Using MediaSFU CE + MediaSFU Cloud for Egress only.
// - Use dummy credentials (8 characters for userName, 64 characters for apiKey).
// - Your CE backend will forward requests with your real credentials.
/*
credentials = {
apiUserName: 'dummyUsr', // 8 characters
apiKey: '1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef', // 64 characters
};
localLink = 'http://your-ce-server.com'; // e.g., 'http://localhost:3000'
connectMediaSFU = localLink.trim() !== '';
*/
// Scenario C: Using MediaSFU Cloud without your own server.
// - For development, use your actual or dummy credentials.
// - In production, securely handle credentials server-side and use custom room functions.
credentials = {
apiUserName: 'yourDevUser', // 8 characters recommended for dummy
apiKey: 'yourDevApiKey1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef', // 64 characters
};
localLink = ''; // Leave empty if not using your own server
connectMediaSFU = true; // Set to true if using MediaSFU Cloud since localLink is empty
// =========================================================
// UI RENDERING OPTIONS
// =========================================================
//
// If you want a fully custom UI (e.g., a custom layout inspired by WhatsApp):
// 1. Set `returnUI = false` to prevent the default MediaSFU UI from rendering.
// 2. Provide `noUIPreJoinOptions` to simulate what would have been entered on a pre-join page.
// 3. Use `sourceParameters` and `updateSourceParameters` to access and update state/actions.
// 4. No need for any of the above if you're using the default MediaSFU UI.
//
// Example noUIPreJoinOptions for creating a room:
noUIPreJoinOptions: CreateMediaSFURoomOptions | JoinMediaSFURoomOptions = {
action: 'create',
capacity: 10,
duration: 15,
eventType: 'broadcast',
userName: 'Prince',
};
// Example noUIPreJoinOptions for joining a room:
// noUIPreJoinOptions: CreateMediaSFURoomOptions | JoinMediaSFURoomOptions = {
// action: 'join',
// userName: 'Prince',
// meetingID: 'yourMeetingID'
// };
returnUI = true; // Set to false for custom UI, true for default MediaSFU UI
sourceParameters: { [key: string]: any } = {};
/**
* Function to update sourceParameters state.
*
* @param data - The data to update sourceParameters with.
*/
updateSourceParameters = (data: { [key: string]: any }) => {
this.sourceParameters = data;
};
// =========================================================
// CUSTOM ROOM FUNCTIONS (OPTIONAL)
// =========================================================
//
// To securely forward requests to MediaSFU:
// - Implement custom `createMediaSFURoom` and `joinMediaSFURoom` functions.
// - These functions send requests to your server, which then communicates with MediaSFU Cloud.
//
// The imported `createRoomOnMediaSFU` and `joinRoomOnMediaSFU` are examples.
//
// If using MediaSFU CE backend, ensure your server endpoints:
// - Validate dummy credentials.
// - Forward requests to mediasfu.com with real credentials.
// =========================================================
// COMPONENT SELECTION AND RENDERING
// =========================================================
//
// Multiple components are available depending on your event type:
// MediasfuBroadcast, MediasfuChat, MediasfuWebinar, MediasfuConference
//
// By default, we'll use MediasfuGeneric with custom settings.
//
// Uncomment the desired component below and comment out the others as needed.
//
// Example of MediaSFU CE with no MediaSFU Cloud
// return (
// <app-mediasfu-generic
// [PrejoinPage]="PreJoinPage"
// [localLink]="localLink">
// </app-mediasfu-generic>
// );
// Example of MediaSFU CE + MediaSFU Cloud for Egress only
// return (
// <app-mediasfu-generic
// [PrejoinPage]="PreJoinPage"
// [credentials]="credentials"
// [localLink]="localLink"
// [connectMediaSFU]="connectMediaSFU">
// </app-mediasfu-generic>
// );
// Example of MediaSFU Cloud only
// return (
// <app-mediasfu-generic
// [PrejoinPage]="PreJoinPage"
// [credentials]="credentials"
// [connectMediaSFU]="connectMediaSFU">
// </app-mediasfu-generic>
// );
// Example of MediaSFU CE + MediaSFU Cloud for Egress only with custom UI
// return (
// <app-mediasfu-generic
// [PrejoinPage]="PreJoinPage"
// [credentials]="credentials"
// [localLink]="localLink"
// [connectMediaSFU]="connectMediaSFU"
// [returnUI]="false"
// [noUIPreJoinOptions]="noUIPreJoinOptions"
// [sourceParameters]="sourceParameters"
// [updateSourceParameters]="updateSourceParameters"
// [createMediaSFURoom]="createRoomOnMediaSFU"
// [joinMediaSFURoom]="joinRoomOnMediaSFU">
// </app-mediasfu-generic>
// );
// Example of MediaSFU Cloud only with custom UI
// return (
// <app-mediasfu-generic
// [PrejoinPage]="PreJoinPage"
// [credentials]="credentials"
// [connectMediaSFU]="connectMediaSFU"
// [returnUI]="false"
// [noUIPreJoinOptions]="noUIPreJoinOptions"
// [sourceParameters]="sourceParameters"
// [updateSourceParameters]="updateSourceParameters"
// [createMediaSFURoom]="createRoomOnMediaSFU"
// [joinMediaSFURoom]="joinRoomOnMediaSFU">
// </app-mediasfu-generic>
// );
// Example of using MediaSFU CE only with custom UI
// return (
// <app-mediasfu-generic
// [PrejoinPage]="PreJoinPage"
// [localLink]="localLink"
// [connectMediaSFU]="false"
// [returnUI]="false"
// [noUIPreJoinOptions]="noUIPreJoinOptions"
// [sourceParameters]="sourceParameters"
// [updateSourceParameters]="updateSourceParameters">
// </app-mediasfu-generic>
// );
/**
* Default Rendering: MediasfuGeneric with Updated Configuration
*
* Renders the MediasfuGeneric component with specified server and cloud connection settings.
* Configured for custom UI usage if `returnUI` is set to false.
*/
ngOnInit(): void {
// Deprecated Feature: useSeed and seedData for generating random participants and messages
// Uncomment and configure the following section if you intend to use seed data
/*
if (this.useSeed) {
const memberName = 'Alice';
const hostName = 'Fred';
// Generate random participants
const participants_ = this.generateRandomParticipants.generateRandomParticipants({
member: memberName,
coHost: '',
host: hostName,
forChatBroadcast: this.eventType === 'broadcast' || this.eventType === 'chat',
});
// Generate random messages
const messages_ = this.generateRandomMessages.generateRandomMessages({
participants: participants_,
member: memberName,
host: hostName,
forChatBroadcast: this.eventType === 'broadcast' || this.eventType === 'chat',
});
// Generate random request list
const requests_ = this.generateRandomRequestList.generateRandomRequestList({
participants: participants_,
hostName: memberName,
coHostName: '',
numberOfRequests: 3,
});
// Generate random waiting room list
const waitingList_ = this.generateRandomWaitingRoomList.generateRandomWaitingRoomList();
// Assign generated data to seedData
this.seedData = {
participants: participants_,
messages: messages_,
requests: requests_,
waitingList: waitingList_,
member: memberName,
host: hostName,
eventType: this.eventType,
};
}
// Determine whether to use local UI mode
this.useLocalUIMode = this.useSeed;
*/
}
// ========================
// ====== COMPONENT SELECTION ======
// ========================
/**
* Choose the Mediasfu component based on the event type and use case.
* Uncomment the component corresponding to your specific use case.
*/
// ------------------------
// ====== SIMPLE USE CASE ======
// ------------------------
/**
* **Simple Use Case (Welcome Page)**
*
* Renders the default welcome page.
* No additional inputs required.
*/
// return <MediasfuWebinar />;
// ------------------------
// ====== PRE-JOIN USE CASE ======
// ------------------------
/**
* **Use Case with Pre-Join Page (Credentials Required)**
*
* Uses a pre-join page that requires users to enter credentials.
*/
// return <MediasfuWebinar PrejoinPage={PreJoinPage} credentials={credentials} />;
// ------------------------
// ====== SEED DATA USE CASE ======
// ------------------------
/**
* **Use Case with Seed Data (Deprecated)**
*
* Runs the application using seed data.
*
* @deprecated Due to updates for strong typing, this feature is deprecated.
*/
// return <MediasfuWebinar useSeed={useSeed} seedData={useSeed ? seedData : {}} />;
// ------------------------
// ====== WEBINAR EVENT TYPE ======
// ------------------------
/**
* **MediasfuWebinar Component**
*
* Uncomment to use the webinar event type.
*/
/*
return (
<MediasfuWebinar
credentials={credentials}
localLink={localLink}
connectMediaSFU={connectMediaSFU}
// seedData={useSeed ? seedData : {}}
/>
);
*/
// ========================
// ====== DEFAULT COMPONENT ======
// ========================
/**
* **Default to MediasfuWebinar with Updated Configuration**
*
* Renders the MediasfuWebinar component with specified server and cloud connection settings.
* This is the default use case if no specific event type is selected.
*/
// Reference to the PreJoinPage component
PreJoinPage = PreJoinPage;
constructor(
private generateRandomParticipants: GenerateRandomParticipants,
private generateRandomMessages: GenerateRandomMessages,
private generateRandomRequestList: GenerateRandomRequestList,
private generateRandomWaitingRoomList: GenerateRandomWaitingRoomList,
public createRoomOnMediaSFU: CreateRoomOnMediaSFU,
public joinRoomOnMediaSFU: JoinRoomOnMediaSFU
) { }
// Deprecated Feature: useSeed and seedData for generating random participants and messages
// Uncomment and configure the following section if you intend to use seed data
// useSeed = false;
// eventType = 'webinar';
// useLocalUIMode = false;
}
export default AppComponent;
/**
* =========================================================
* ADDITIONAL NOTES
* =========================================================
*
* Handling Core Methods:
* Once `sourceParameters` is populated, you can call core methods like `clickVideo` or `clickAudio` directly:
* Example:
* sourceParameters.clickVideo({ ...sourceParameters });
* sourceParameters.clickAudio({ ...sourceParameters });
*
* This allows your custom UI to directly interact with MediaSFU functionalities.
*
* Deprecated Features (Seed Data):
* The seed data generation feature is deprecated. Avoid using `useLocalUIMode` or `useSeed` in new implementations.
*
* Security Considerations:
* - **Protect API Credentials:** Ensure that API credentials are not exposed in the frontend. Use environment variables and secure backend services to handle sensitive information.
* - **Use HTTPS:** Always use HTTPS to secure data transmission between the client and server.
* - **Validate Inputs:** Implement proper validation and error handling on both client and server sides to prevent malicious inputs.
*
* Example CE Backend Setup:
* If using MediaSFU CE + MediaSFU Cloud, your backend might look like this:
*
* ```javascript
* // Endpoint for `createRoom`
* app.post("/createRoom", async (req, res) => {
* try {
* const payload = req.body;
* const [apiUserName, apiKey] = req.headers.authorization
* .replace("Bearer ", "")
* .split(":");
*
* // Verify temporary credentials
* if (!apiUserName || !apiKey || !verifyCredentials(apiUserName, apiKey)) {
* return res.status(401).json({ error: "Invalid or expired credentials" });
* }
*
* const response = await fetch("https://mediasfu.com/v1/rooms/", {
* method: "POST",
* headers: {
* "Content-Type": "application/json",
* Authorization: `Bearer ${actualApiUserName}:${actualApiKey}`,
* },
* body: JSON.stringify(payload),
* });
*
* const result = await response.json();
* res.status(response.status).json(result);
* } catch (error) {
* console.error("Error creating room:", error);
* res.status(500).json({ error: "Internal server error" });
* }
* });
*
* // Endpoint for `joinRoom`
* app.post("/joinRoom", async (req, res) => {
* try {
* const payload = req.body;
* const [apiUserName, apiKey] = req.headers.authorization
* .replace("Bearer ", "")
* .split(":");
*
* // Verify temporary credentials
* if (!apiUserName || !apiKey || !verifyCredentials(apiUserName, apiKey)) {
* return res.status(401).json({ error: "Invalid or expired credentials" });
* }
*
* const response = await fetch("https://mediasfu.com/v1/rooms", {
* method: "POST",
* headers: {
* "Content-Type": "application/json",
* Authorization: `Bearer ${actualApiUserName}:${actualApiKey}`,
* },
* body: JSON.stringify(payload),
* });
*
* const result = await response.json();
* res.status(response.status).json(result);
* } catch (error) {
* console.error("Error joining room:", error);
* res.status(500).json({ error: "Internal server error" });
* }
* });
* ```
*
* Custom Room Function Implementation:
* Below are examples of how to implement custom functions for creating and joining rooms securely:
*
* ```typescript
* import { CreateJoinRoomError, CreateJoinRoomResponse, CreateJoinRoomType, CreateMediaSFURoomOptions, JoinMediaSFURoomOptions } from '../../@types/types';
*
*
* * Async function to create a room on MediaSFU.
* *
* * @param {object} options - The options for creating a room.
* * @param {CreateMediaSFURoomOptions} options.payload - The payload for the API request.
* * @param {string} options.apiUserName - The API username.
* * @param {string} options.apiKey - The API key.
* * @param {string} options.localLink - The local link.
* * @returns {Promise<{ data: CreateJoinRoomResponse | CreateJoinRoomError | null; success: boolean; }>} The response from the API.
*
* export const createRoomOnMediaSFU: CreateJoinRoomType = async ({
* payload,
* apiUserName,
* apiKey,
* localLink = '',
* }) => {
* try {
* let finalLink = 'https://mediasfu.com/v1/rooms/';
*
* // Update finalLink if using a local server
* if (localLink) {
* finalLink = `${localLink}/createRoom`;
* }
*
* const response = await fetch(finalLink, {
* method: 'POST',
* headers: {
* 'Content-Type': 'application/json',
* Authorization: `Bearer ${apiUserName}:${apiKey}`,
* },
* body: JSON.stringify(payload),
* });
*
* if (!response.ok) {
* throw new Error(`HTTP error! Status: ${response.status}`);
* }
*
* const data: CreateJoinRoomResponse = await response.json();
* return { data, success: true };
* } catch (error) {
* const errorMessage = (error as Error).message || 'unknown error';
* return {
* data: { error: `Unable to create room, ${errorMessage}` },
* success: false,
* };
* }
* };
*
*
* * Async function to join a room on MediaSFU.
* *
* * @param {object} options - The options for joining a room.
* * @param {JoinMediaSFURoomOptions} options.payload - The payload for the API request.
* * @param {string} options.apiUserName - The API username.
* * @param {string} options.apiKey - The API key.
* * @param {string} options.localLink - The local link.
* * @returns {Promise<{ data: CreateJoinRoomResponse | CreateJoinRoomError | null; success: boolean; }>} The response from the API.
*
* export const joinRoomOnMediaSFU: JoinRoomOnMediaSFUType = async ({
* payload,
* apiUserName,
* apiKey,
* localLink = '',
* }) => {
* try {
* let finalLink = 'https://mediasfu.com/v1/rooms/join';
*
* // Update finalLink if using a local server
* if (localLink) {
* finalLink = `${localLink}/joinRoom`;
* }
*
* const response = await fetch(finalLink, {
* method: 'POST',
* headers: {
* 'Content-Type': 'application/json',
* Authorization: `Bearer ${apiUserName}:${apiKey}`,
* },
* body: JSON.stringify(payload),
* });
*
* if (!response.ok) {
* throw new Error(`HTTP error! Status: ${response.status}`);
* }
*
* const data: CreateJoinRoomResponse = await response.json();
* return { data, success: true };
* } catch (error) {
* const errorMessage = (error as Error).message || 'unknown error';
* return {
* data: { error: `Unable to join room, ${errorMessage}` },
* success: false,
* };
* }
* };
* ```
*
* =======================
* ====== END OF GUIDE ======
* =======================
*/
In the provided examples, users can set useLocalUIMode
to true
during UI development to prevent unwanted connections to MediaSFU servers. Additionally, they can generate seed data for rendering UI components locally by using random data generators provided by the module.
Local UI Development in MediaSFU Angular Module
During local UI development, the MediaSFU view is designed to be responsive to changes in screen size and orientation, adapting its layout accordingly. However, since UI changes are typically linked to communication with servers, developing the UI locally might result in less responsiveness due to the lack of real-time data updates. To mitigate this, users can force trigger changes in the UI by rotating the device, resizing the window, or simulating server responses by clicking on buttons within the page.
While developing locally, users may encounter occasional error warnings as the UI attempts to communicate with the server. These warnings can be safely ignored, as they are simply indicative of unsuccessful server requests in the local development environment.
If users experience responsiveness issues, whether during local development or in production, they can optimize their HTML configuration to ensure proper scaling and viewport settings. By adding the following meta tag to the HTML <head>
section, users can specify viewport settings for optimal display:
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" />
Intermediate Usage Guide
Expands on the basic usage, covering more advanced features and scenarios.
Intermediate Usage Guide
In the Intermediate Usage Guide, we'll explore the core components and functionalities of the MediaSFU Angular module, focusing on media display, controls, and modal interactions. Click on any listed component/method to open the full documentation.
Core Components Overview
The main items displayed on an event page are media components (such as video, audio, and blank cards) and control components (for pagination, navigation, etc.).
Media Display Components
Component Name | Description |
---|---|
MainAspectComponent | Serves as a container for the primary aspect of the user interface, typically containing the main content or focus of the application. |
MainScreenComponent | Responsible for rendering the main screen layout of the application, providing the foundation for displaying various elements and content. |
MainGridComponent | Crucial part of the user interface, organizing and displaying primary content or elements in a grid layout format. |
SubAspectComponent | Acts as a secondary container within the user interface, often housing additional elements or controls related to the main aspect. |
MainContainerComponent | Primary container for the application's content, encapsulating all major components and providing structural organization. |
OtherGridComponent | Complements the Main Grid Component by offering additional grid layouts, typically used for displaying secondary or auxiliary content. |
Control Components
Component Name | Description |
---|---|
ControlButtonsComponent | Comprises a set of buttons or controls used for navigating, interacting, or managing various aspects of the application's functionality. |
ControlButtonsAltComponent | Provides alternative button configurations or styles for controlling different aspects of the application. |
ControlButtonsComponentTouch | Specialized component designed for touch-enabled devices, offering floating buttons or controls for intuitive interaction with the application's features. |
These components collectively contribute to the overall user interface, facilitating navigation, interaction, and content display within the application.
Modal Components
Modal Component | Description |
---|---|
LoadingModal | Modal for displaying loading indicator during data fetching or processing. |
MainAspectComponent | Component responsible for displaying the main aspect of the event page. |
ControlButtonsComponent | Component for displaying control buttons such as pagination controls. |
ControlButtonsAltComponent | Alternate control buttons component for specific use cases. |
ControlButtonsComponentTouch | Touch-enabled control buttons component for mobile devices. |
OtherGridComponent | Component for displaying additional grid elements on the event page. |
MainScreenComponent | Component for rendering the main screen content of the event. |
MainGridComponent | Main grid component for displaying primary event content. |
SubAspectComponent | Component for displaying secondary aspects of the event page. |
MainContainerComponent | Main container component for the event page content. |
AlertComponent | Modal for displaying alert messages to the user. |
MenuModal | Modal for displaying a menu with various options. |
RecordingModal | Modal for managing recording functionality during the event. |
RequestsModal | Modal for handling requests from participants during the event. |
WaitingRoomModal | Modal for managing waiting room functionality during the event. |
DisplaySettingsModal | Modal for adjusting display s |