Refacto insert cowebsite

This commit is contained in:
Alexis Faizeau 2021-10-25 18:42:51 +02:00
parent 251d14a470
commit f84c4b3276
6 changed files with 98 additions and 110 deletions

View file

@ -5,7 +5,7 @@ import type { ClosePopupEvent } from "./ClosePopupEvent";
import type { EnterLeaveEvent } from "./EnterLeaveEvent";
import type { GoToPageEvent } from "./GoToPageEvent";
import type { LoadPageEvent } from "./LoadPageEvent";
import { coWebsite, isOpenCoWebsiteEvent } from "./OpenCoWebsiteEvent";
import { isCoWebsite, isOpenCoWebsiteEvent } from "./OpenCoWebsiteEvent";
import type { OpenPopupEvent } from "./OpenPopupEvent";
import type { OpenTabEvent } from "./OpenTabEvent";
import type { UserInputChatEvent } from "./UserInputChatEvent";
@ -114,11 +114,11 @@ export const iframeQueryMapTypeGuards = {
},
openCoWebsite: {
query: isOpenCoWebsiteEvent,
answer: coWebsite
answer: isCoWebsite
},
getCoWebsites: {
query: tg.isUndefined,
answer: tg.isArray(coWebsite)
answer: tg.isArray(isCoWebsite)
},
closeCoWebsite: {
query: tg.isString,

View file

@ -9,7 +9,7 @@ export const isOpenCoWebsiteEvent = new tg.IsInterface()
})
.get();
export const coWebsite = new tg.IsInterface()
export const isCoWebsite = new tg.IsInterface()
.withProperties({
id: tg.isString,
position: tg.isNumber,

View file

@ -1,5 +1,16 @@
import { IframeApiContribution, sendToWorkadventure, queryWorkadventure } from "./IframeApiContribution";
export class CoWebsite {
constructor(private readonly id: string, public readonly position: number) {}
close() {
return queryWorkadventure({
type: "closeCoWebsite",
data: this.id,
});
}
}
export class WorkadventureNavigationCommands extends IframeApiContribution<WorkadventureNavigationCommands> {
callbacks = [];
@ -45,8 +56,8 @@ export class WorkadventureNavigationCommands extends IframeApiContribution<Worka
});
}
openCoWebsite(url: string, allowApi?: boolean, allowPolicy?: string, position?: number) {
return queryWorkadventure({
async openCoWebsite(url: string, allowApi?: boolean, allowPolicy?: string, position?: number): Promise<CoWebsite> {
const result = await queryWorkadventure({
type: "openCoWebsite",
data: {
url,
@ -55,13 +66,15 @@ export class WorkadventureNavigationCommands extends IframeApiContribution<Worka
position,
},
});
return new CoWebsite(result.id, result.position);
}
getCoWebsites() {
return queryWorkadventure({
async getCoWebsites(): Promise<CoWebsite[]> {
const result = await queryWorkadventure({
type: "getCoWebsites",
data: undefined
});
return result.map((cowebsiteEvent) => new CoWebsite(cowebsiteEvent.id, cowebsiteEvent.position));
}
/**

View file

@ -442,138 +442,102 @@ class CoWebsiteManager {
widthPercent?: number,
position?: number
): Promise<CoWebsite> {
return new Promise((resolve, reject) => {
if (this.coWebsites.length < 1) {
this.loadMain();
} else if (this.coWebsites.length === 5) {
return reject();
}
return this.addCoWebsite((iframeBuffer) => {
const iframe = document.createElement("iframe");
iframe?.classList.add("pixel");
do {
iframe.id = "cowebsite-iframe-" + (Math.random() + 1).toString(36).substring(7);
} while (iframe.id.toLowerCase().includes('jitsi') || this.getCoWebsiteById(iframe.id));
iframe.src = new URL(url, base).toString()
if (allowPolicy) {
iframe.allow = allowPolicy;
}
const onloadPromise = new Promise<void>((resolve) => {
iframe.onload = () => resolve();
});
if (allowApi) {
iframeListener.registerIframe(iframe);
}
const icon = this.generateCoWebsiteIcon(iframe);
iframeBuffer.appendChild(iframe);
const coWebsite = {
iframe,
icon,
position: position ?? this.coWebsites.length,
};
// Iframe management on mobile
icon.addEventListener("click", () => {
if (this.isSmallScreen()) {
this.moveRightPreviousCoWebsite(coWebsite, 0);
}
});
this.coWebsites.push(coWebsite);
this.cowebsiteSubIconsDom.appendChild(icon);
this.cowebsiteBufferDom.appendChild(coWebsite.iframe);
const onTimeoutPromise = new Promise<void>((resolve) => {
setTimeout(() => resolve(), 2000);
});
this.currentOperationPromise = this.currentOperationPromise
.then(() => Promise.race([onloadPromise, onTimeoutPromise]))
.then(() => {
if (coWebsite.position === 0) {
this.openMain();
if (widthPercent) {
this.widthPercent = widthPercent;
}
setTimeout(() => {
this.fire();
position ?
this.moveRightPreviousCoWebsite(coWebsite, coWebsite.position) :
this.moveCoWebsite(coWebsite, coWebsite.position);
}, animationTime);
} else {
position ?
this.moveRightPreviousCoWebsite(coWebsite, coWebsite.position) :
this.moveCoWebsite(coWebsite, coWebsite.position);
}
return resolve(coWebsite);
})
.catch((err) => {
console.error("Error loadCoWebsite => ", err);
this.removeCoWebsiteFromStack(coWebsite);
return reject();
});
});
return iframe;
}, widthPercent, position);
}
/**
* Just like loadCoWebsite but the div can be filled by the user.
*/
public insertCoWebsite(callback: (jitsiBuffer: HTMLDivElement) => Promise<void>, widthPercent?: number): void {
if (this.coWebsites.length < 1) {
this.loadMain();
} else if (this.coWebsites.length === 5) {
return;
}
public async addCoWebsite(
callback: (iframeBuffer: HTMLDivElement) => PromiseLike<HTMLIFrameElement>|HTMLIFrameElement,
widthPercent?: number,
position?: number
): Promise<CoWebsite> {
return new Promise((resolve, reject) => {
if (this.coWebsites.length < 1) {
this.loadMain();
} else if (this.coWebsites.length === 5) {
throw new Error('Too many we')
}
this.currentOperationPromise = this.currentOperationPromise
.then(() => callback(this.cowebsiteBufferDom))
.then(() => {
const iframe = this.cowebsiteBufferDom.querySelector<HTMLIFrameElement>('[id*="jitsi" i]');
Promise.resolve(callback(this.cowebsiteBufferDom)).then(iframe =>{
iframe?.classList.add("pixel");
if (!iframe) {
console.error("Error insertCoWebsite => Cannot find Iframe Element on Jitsi DOM");
return;
if (!iframe.id) {
do {
iframe.id = "cowebsite-iframe-" + (Math.random() + 1).toString(36).substring(7);
} while (this.getCoWebsiteById(iframe.id));
}
const onloadPromise = new Promise<void>((resolve) => {
iframe.onload = () => resolve();
});
const icon = this.generateCoWebsiteIcon(iframe);
const coWebsite = {
iframe,
icon,
position: 0,
position: position ?? this.coWebsites.length,
};
// Iframe management on mobile
icon.addEventListener("click", () => {
if (this.isSmallScreen()) {
this.moveRightPreviousCoWebsite(coWebsite, 0);
}
});
this.coWebsites.push(coWebsite);
this.cowebsiteSubIconsDom.appendChild(icon);
if (coWebsite.position === 0) {
this.openMain();
const onTimeoutPromise = new Promise<void>((resolve) => {
setTimeout(() => resolve(), 2000);
});
if (widthPercent) {
this.widthPercent = widthPercent;
}
this.currentOperationPromise = this.currentOperationPromise
.then(() => Promise.race([onloadPromise, onTimeoutPromise]))
.then(() => {
if (coWebsite.position === 0) {
this.openMain();
if (widthPercent) {
this.widthPercent = widthPercent;
}
setTimeout(() => {
this.fire();
}, animationTime);
}
setTimeout(() => {
this.fire();
position !== undefined ?
this.moveRightPreviousCoWebsite(coWebsite, coWebsite.position) :
this.moveCoWebsite(coWebsite, coWebsite.position);
}, animationTime);
} else {
position !== undefined ?
this.moveRightPreviousCoWebsite(coWebsite, coWebsite.position) :
this.moveCoWebsite(coWebsite, coWebsite.position);
}
this.moveRightPreviousCoWebsite(coWebsite, coWebsite.position);
})
.catch((err) => {
console.error("Error insertCoWebsite => ", err);
return resolve(coWebsite);
})
.catch((err) => {
console.error("Error loadCoWebsite => ", err);
this.removeCoWebsiteFromStack(coWebsite);
return reject();
});
});
});
}
public closeCoWebsite(coWebsite: CoWebsite): Promise<void> {

View file

@ -141,7 +141,7 @@ class JitsiFactory {
jitsiUrl?: string,
jitsiWidth?: number
): void {
coWebsiteManager.insertCoWebsite(async (cowebsiteDiv) => {
coWebsiteManager.addCoWebsite(async (cowebsiteDiv) => {
// Jitsi meet external API maintains some data in local storage
// which is sent via the appData URL parameter when joining a
// conference. Problem is that this data grows indefinitely. Thus
@ -170,15 +170,22 @@ class JitsiFactory {
}
return new Promise((resolve, reject) => {
options.onload = () => resolve(); //we want for the iframe to be loaded before triggering animations.
setTimeout(() => resolve(), 2000); //failsafe in case the iframe is deleted before loading or too long to load
const doResolve = (): void => {
const iframe = cowebsiteDiv.querySelector<HTMLIFrameElement>('[id*="jitsi" i]');
if (iframe === null) {
throw new Error("Could not find Jitsi Iframe");
}
resolve(iframe);
}
options.onload = () => doResolve(); //we want for the iframe to be loaded before triggering animations.
setTimeout(() => doResolve(), 2000); //failsafe in case the iframe is deleted before loading or too long to load
this.jitsiApi = new window.JitsiMeetExternalAPI(domain, options);
this.jitsiApi.executeCommand("displayName", playerName);
this.jitsiApi.addListener("audioMuteStatusChanged", this.audioCallback);
this.jitsiApi.addListener("videoMuteStatusChanged", this.videoCallback);
});
}, jitsiWidth);
}, jitsiWidth, 0);
}
public stop() {

View file

@ -170,6 +170,10 @@
.sub-main {
pointer-events: all !important;
}
.thumbnail {
transform: scale(0.5, 0.5);
}
}
.pixel {