2020-07-23 17:10:27 -04:00
|
|
|
import { Component, OnDestroy, OnInit } from "@angular/core";
|
|
|
|
import { ActivatedRoute } from "@angular/router";
|
|
|
|
import { Subscription } from "rxjs/Subscription";
|
|
|
|
import { ScalarWidgetApi } from "../../shared/services/scalar/scalar-widget.api";
|
|
|
|
import { CapableWidget } from "../capable-widget";
|
|
|
|
import { DomSanitizer, SafeUrl } from "@angular/platform-browser";
|
|
|
|
import { BigBlueButtonApiService } from "../../shared/services/integrations/bigbluebutton-api.service";
|
2021-09-01 19:01:01 -04:00
|
|
|
import {
|
|
|
|
FE_BigBlueButtonCreateAndJoinMeeting,
|
|
|
|
FE_BigBlueButtonJoin,
|
|
|
|
} from "../../shared/models/integration";
|
2020-12-28 22:49:24 -05:00
|
|
|
import { TranslateService } from "@ngx-translate/core";
|
2020-07-23 17:10:27 -04:00
|
|
|
|
|
|
|
@Component({
|
2021-09-01 19:29:24 -04:00
|
|
|
selector: "my-bigbluebutton-widget-wrapper",
|
2020-07-23 17:10:27 -04:00
|
|
|
templateUrl: "bigbluebutton.component.html",
|
|
|
|
styleUrls: ["bigbluebutton.component.scss"],
|
|
|
|
})
|
2021-09-01 19:01:01 -04:00
|
|
|
export class BigBlueButtonWidgetWrapperComponent
|
|
|
|
extends CapableWidget
|
|
|
|
implements OnInit, OnDestroy {
|
2020-07-23 17:10:27 -04:00
|
|
|
public canEmbed = true;
|
|
|
|
|
|
|
|
/**
|
2021-09-01 19:01:01 -04:00
|
|
|
* User metadata passed to us by the client
|
|
|
|
*/
|
2020-07-23 17:10:27 -04:00
|
|
|
private conferenceUrl: string;
|
|
|
|
private displayName: string;
|
|
|
|
private userId: string;
|
2021-05-06 14:30:47 -04:00
|
|
|
private avatarUrl: string;
|
|
|
|
private meetingId: string;
|
|
|
|
private meetingPassword: string;
|
2020-07-23 17:10:27 -04:00
|
|
|
|
2021-05-05 15:37:19 -04:00
|
|
|
/**
|
2021-09-01 19:01:01 -04:00
|
|
|
*
|
|
|
|
* The name to join the BigBlueButton meeting with. Made up of metadata the client passes to us.
|
|
|
|
*/
|
2021-05-05 15:37:19 -04:00
|
|
|
private joinName: string;
|
|
|
|
|
2021-05-04 04:38:44 -04:00
|
|
|
/**
|
2021-09-01 19:01:01 -04:00
|
|
|
* Whether we expect the meeting to be created on command.
|
|
|
|
*
|
|
|
|
* True if we'd like the meeting to be created, false if we have a greenlight URL leading to an existing meeting
|
|
|
|
* and would like Dimension to translate that to a BigBlueButton meeting URL.
|
|
|
|
*/
|
2021-05-04 04:38:44 -04:00
|
|
|
private createMeeting: boolean;
|
|
|
|
|
|
|
|
/**
|
2021-09-01 19:01:01 -04:00
|
|
|
* The ID of the room, required if createMeeting is true.
|
|
|
|
*/
|
2021-05-04 04:38:44 -04:00
|
|
|
private roomId: string;
|
|
|
|
|
2020-07-23 17:10:27 -04:00
|
|
|
/**
|
2021-09-01 19:01:01 -04:00
|
|
|
* The poll period in ms while waiting for a meeting to start
|
|
|
|
*/
|
2020-07-23 17:10:27 -04:00
|
|
|
private pollIntervalMillis = 5000;
|
|
|
|
|
|
|
|
/**
|
2021-09-01 19:01:01 -04:00
|
|
|
* Subscriber for messages from the client via the postMessage API
|
|
|
|
*/
|
2020-07-23 17:10:27 -04:00
|
|
|
private bigBlueButtonApiSubscription: Subscription;
|
|
|
|
|
|
|
|
/**
|
2021-09-01 19:01:01 -04:00
|
|
|
* A status message to display to the user in the widget, typically for loading messages
|
|
|
|
*/
|
2020-07-23 17:10:27 -04:00
|
|
|
public statusMessage: string;
|
|
|
|
|
|
|
|
/**
|
2021-09-01 19:01:01 -04:00
|
|
|
* Whether we are currently in a meeting
|
|
|
|
*/
|
2020-12-28 23:05:45 -05:00
|
|
|
private inMeeting = false;
|
2020-07-23 17:10:27 -04:00
|
|
|
|
|
|
|
/**
|
2021-09-01 19:01:01 -04:00
|
|
|
* The URL to embed into the iframe
|
|
|
|
*/
|
2020-07-23 17:10:27 -04:00
|
|
|
public embedUrl: SafeUrl = null;
|
|
|
|
|
2021-09-01 19:01:01 -04:00
|
|
|
constructor(
|
|
|
|
activatedRoute: ActivatedRoute,
|
|
|
|
private bigBlueButtonApi: BigBlueButtonApiService,
|
|
|
|
private sanitizer: DomSanitizer,
|
|
|
|
public translate: TranslateService
|
|
|
|
) {
|
2020-07-23 17:10:27 -04:00
|
|
|
super();
|
|
|
|
this.supportsAlwaysOnScreen = true;
|
|
|
|
|
2021-09-01 19:01:01 -04:00
|
|
|
const params: any = activatedRoute.snapshot.queryParams;
|
2020-12-28 22:49:24 -05:00
|
|
|
|
2021-05-04 04:38:44 -04:00
|
|
|
this.roomId = params.roomId;
|
|
|
|
this.createMeeting = params.createMeeting;
|
2020-07-23 17:10:27 -04:00
|
|
|
this.conferenceUrl = params.conferenceUrl;
|
|
|
|
this.displayName = params.displayName;
|
2021-05-06 14:30:47 -04:00
|
|
|
this.avatarUrl = params.avatarUrl;
|
|
|
|
this.meetingId = params.meetingId;
|
|
|
|
this.meetingPassword = params.meetingPassword;
|
2020-07-23 17:10:27 -04:00
|
|
|
this.userId = params.userId || params.email; // Element uses `email` when placing a conference call
|
|
|
|
|
2021-05-05 15:37:19 -04:00
|
|
|
// Create a nick to display in the meeting
|
|
|
|
this.joinName = `${this.displayName} (${this.userId})`;
|
|
|
|
|
2021-05-04 04:38:44 -04:00
|
|
|
console.log("BigBlueButton: should create meeting: " + this.createMeeting);
|
2021-05-05 15:37:19 -04:00
|
|
|
console.log("BigBlueButton: will join as: " + this.joinName);
|
2021-05-04 04:38:44 -04:00
|
|
|
console.log("BigBlueButton: got room ID: " + this.roomId);
|
|
|
|
|
2020-07-23 17:10:27 -04:00
|
|
|
// Set the widget ID if we have it
|
|
|
|
ScalarWidgetApi.widgetId = params.widgetId;
|
|
|
|
}
|
|
|
|
|
|
|
|
public ngOnInit() {
|
|
|
|
super.ngOnInit();
|
|
|
|
}
|
2020-12-28 22:49:24 -05:00
|
|
|
|
2020-07-23 17:10:27 -04:00
|
|
|
public onIframeLoad() {
|
|
|
|
if (this.inMeeting) {
|
|
|
|
// The meeting has ended and we've come back full circle
|
|
|
|
this.inMeeting = false;
|
|
|
|
this.statusMessage = null;
|
|
|
|
this.embedUrl = null;
|
|
|
|
|
|
|
|
ScalarWidgetApi.sendSetAlwaysOnScreen(false);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Have a toggle for whether we're in a meeting. We do this as we don't have a method
|
|
|
|
// of checking which URL was just loaded in the iframe (due to different origin domains
|
|
|
|
// and browser security), so we have to guess that it'll always be the second load (the
|
|
|
|
// first being joining the meeting)
|
|
|
|
this.inMeeting = true;
|
|
|
|
|
|
|
|
// We've successfully joined the meeting
|
|
|
|
ScalarWidgetApi.sendSetAlwaysOnScreen(true);
|
|
|
|
}
|
|
|
|
|
2020-12-28 23:05:45 -05:00
|
|
|
public joinConference(updateStatusMessage = true) {
|
2020-07-23 17:10:27 -04:00
|
|
|
if (updateStatusMessage) {
|
|
|
|
// Inform the user that we're loading their meeting
|
|
|
|
this.statusMessage = "Joining conference...";
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make a request to Dimension requesting the join URL
|
2021-05-05 15:37:19 -04:00
|
|
|
if (this.createMeeting) {
|
2021-05-06 14:30:47 -04:00
|
|
|
// Ask Dimension to return a URL for joining a meeting that it created
|
|
|
|
this.joinThroughDimension();
|
2021-05-04 04:38:44 -04:00
|
|
|
} else {
|
|
|
|
// Provide Dimension with a Greenlight URL, which it will transform into
|
|
|
|
// a BBB meeting URL
|
2021-05-05 15:37:19 -04:00
|
|
|
this.joinThroughGreenlightUrl();
|
2021-05-04 04:38:44 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Ask Dimension to create a meeting (or use an existing one) for this room and return the embeddable meeting URL
|
2021-05-06 14:30:47 -04:00
|
|
|
private async joinThroughDimension() {
|
2021-09-01 19:01:01 -04:00
|
|
|
console.log(
|
|
|
|
"BigBlueButton: Joining meeting created by Dimension with meeting ID: " +
|
|
|
|
this.meetingId
|
|
|
|
);
|
|
|
|
|
|
|
|
this.bigBlueButtonApi
|
|
|
|
.getJoinUrl(
|
|
|
|
this.displayName,
|
|
|
|
this.userId,
|
|
|
|
this.avatarUrl,
|
|
|
|
this.meetingId,
|
|
|
|
this.meetingPassword
|
|
|
|
)
|
|
|
|
.then((response) => {
|
|
|
|
console.log("The response");
|
|
|
|
console.log(response);
|
|
|
|
if ("errorCode" in response) {
|
|
|
|
// This is an instance of ApiError
|
|
|
|
if (response.errorCode === "UNKNOWN_MEETING_ID") {
|
|
|
|
// This meeting ID is invalid.
|
|
|
|
// Inform the user that they should try and start a new meeting
|
|
|
|
this.statusMessage =
|
|
|
|
"This meeting has ended or otherwise does not exist.<br>Please start a new meeting.";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (response.errorCode === "MEETING_HAS_ENDED") {
|
|
|
|
// It's likely that everyone has left the meeting, and it's been garbage collected.
|
|
|
|
// Inform the user that they should try and start a new meeting
|
|
|
|
this.statusMessage =
|
|
|
|
"This meeting has ended.<br>Please start a new meeting.";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// Otherwise this is a generic error
|
|
|
|
this.statusMessage = "An error occurred while loading the meeting";
|
2021-05-06 14:30:47 -04:00
|
|
|
}
|
2021-05-04 04:38:44 -04:00
|
|
|
|
2021-09-01 19:01:01 -04:00
|
|
|
// Retrieve and embed the meeting URL
|
|
|
|
const joinUrl = (response as FE_BigBlueButtonCreateAndJoinMeeting).url;
|
|
|
|
this.embedMeetingWithUrl(joinUrl);
|
|
|
|
});
|
2021-05-04 04:38:44 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Hand Dimension a Greenlight URL and receive a translated, embeddable meeting URL in response
|
2021-05-05 15:37:19 -04:00
|
|
|
private joinThroughGreenlightUrl() {
|
2021-09-01 19:01:01 -04:00
|
|
|
console.log(
|
|
|
|
"BigBlueButton: joining via greenlight url:",
|
|
|
|
this.conferenceUrl
|
|
|
|
);
|
|
|
|
this.bigBlueButtonApi
|
|
|
|
.joinMeetingWithGreenlightUrl(this.conferenceUrl, this.joinName)
|
|
|
|
.then((response) => {
|
|
|
|
if ("errorCode" in response) {
|
|
|
|
// This is an instance of ApiError
|
|
|
|
if (response.errorCode === "WAITING_FOR_MEETING_START") {
|
|
|
|
// The meeting hasn't started yet
|
|
|
|
this.statusMessage = "Waiting for conference to start...";
|
|
|
|
|
|
|
|
// Poll until it has
|
|
|
|
setTimeout(
|
|
|
|
this.joinConference.bind(this),
|
|
|
|
this.pollIntervalMillis,
|
|
|
|
false
|
|
|
|
);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Otherwise this is a generic error
|
|
|
|
this.statusMessage = "An error occurred while loading the meeting";
|
2020-07-23 17:10:27 -04:00
|
|
|
}
|
|
|
|
|
2021-09-01 19:01:01 -04:00
|
|
|
// Retrieve and embed the meeting URL
|
|
|
|
const joinUrl = (response as FE_BigBlueButtonJoin).url;
|
|
|
|
this.embedMeetingWithUrl(joinUrl);
|
|
|
|
});
|
2021-05-04 04:38:44 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
private embedMeetingWithUrl(url: string) {
|
2021-09-01 19:01:01 -04:00
|
|
|
// Hide widget-related UI
|
2021-05-05 15:37:19 -04:00
|
|
|
this.statusMessage = null;
|
2021-05-04 04:38:44 -04:00
|
|
|
|
2021-05-06 14:30:47 -04:00
|
|
|
// Embed the return meeting URL, joining the meeting
|
|
|
|
this.canEmbed = true;
|
|
|
|
this.embedUrl = this.sanitizer.bypassSecurityTrustResourceUrl(url);
|
2020-07-23 17:10:27 -04:00
|
|
|
|
2021-05-06 14:30:47 -04:00
|
|
|
// Inform the client that we would like the meeting to remain visible for its duration
|
|
|
|
ScalarWidgetApi.sendSetAlwaysOnScreen(true);
|
2020-07-23 17:10:27 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
public ngOnDestroy() {
|
2021-09-01 19:01:01 -04:00
|
|
|
if (this.bigBlueButtonApiSubscription)
|
|
|
|
this.bigBlueButtonApiSubscription.unsubscribe();
|
2020-07-23 17:10:27 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
protected onCapabilitiesSent(): void {
|
|
|
|
super.onCapabilitiesSent();
|
2021-05-06 14:30:47 -04:00
|
|
|
|
|
|
|
// Don't set alwaysOnScreen until we start a meeting
|
2020-07-23 17:10:27 -04:00
|
|
|
ScalarWidgetApi.sendSetAlwaysOnScreen(false);
|
|
|
|
}
|
|
|
|
}
|