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

View file

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

View file

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

View file

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

View file

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

View file

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