Implement UI Website (#2087)
* Implement UI Website system * Add UIWebsite documentation * Implement review fixes * Add getAll function on UIWebsite Co-authored-by: Alexis Faizeau <a.faizeau@workadventu.re>
This commit is contained in:
@@ -39,6 +39,7 @@ import type { RemotePlayerClickedEvent } from "./RemotePlayerClickedEvent";
|
||||
import { isAddActionsMenuKeyToRemotePlayerEvent } from "./AddActionsMenuKeyToRemotePlayerEvent";
|
||||
import type { ActionsMenuActionClickedEvent } from "./ActionsMenuActionClickedEvent";
|
||||
import { isRemoveActionsMenuKeyFromRemotePlayerEvent } from "./RemoveActionsMenuKeyFromRemotePlayerEvent";
|
||||
import { isCreateUIWebsiteEvent, isModifyUIWebsiteEvent, isUIWebsite } from "./ui/UIWebsite";
|
||||
|
||||
export interface TypedMessageEvent<T> extends MessageEvent {
|
||||
data: T;
|
||||
@@ -152,6 +153,10 @@ export const isIframeEventWrapper = z.union([
|
||||
type: z.literal("modifyEmbeddedWebsite"),
|
||||
data: isEmbeddedWebsiteEvent,
|
||||
}),
|
||||
z.object({
|
||||
type: z.literal("modifyUIWebsite"),
|
||||
data: isModifyUIWebsiteEvent,
|
||||
}),
|
||||
]);
|
||||
|
||||
export type IframeEvent = z.infer<typeof isIframeEventWrapper>;
|
||||
@@ -261,6 +266,18 @@ export const iframeQueryMapTypeGuards = {
|
||||
query: isMovePlayerToEventConfig,
|
||||
answer: isMovePlayerToEventAnswer,
|
||||
},
|
||||
openUIWebsite: {
|
||||
query: isCreateUIWebsiteEvent,
|
||||
answer: isUIWebsite,
|
||||
},
|
||||
closeUIWebsite: {
|
||||
query: z.string(),
|
||||
answer: z.undefined(),
|
||||
},
|
||||
getUIWebsites: {
|
||||
query: z.undefined(),
|
||||
answer: z.array(isUIWebsite),
|
||||
},
|
||||
};
|
||||
|
||||
type IframeQueryMapTypeGuardsType = typeof iframeQueryMapTypeGuards;
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
import { z } from "zod";
|
||||
|
||||
const regexUnit = /-*\d+(px|em|%|cm|in|pc|pt|mm|ex|vw|vh|rem)|auto|inherit/;
|
||||
|
||||
// Parse the string to check if is a valid CSS unit (px,%,vw,vh...)
|
||||
export const isUIWebsiteCSSValue = z.string().regex(regexUnit);
|
||||
|
||||
export type UIWebsiteCSSValue = z.infer<typeof isUIWebsiteCSSValue>;
|
||||
|
||||
export const isUIWebsiteMargin = z.object({
|
||||
top: isUIWebsiteCSSValue.optional(),
|
||||
bottom: isUIWebsiteCSSValue.optional(),
|
||||
left: isUIWebsiteCSSValue.optional(),
|
||||
right: isUIWebsiteCSSValue.optional(),
|
||||
});
|
||||
|
||||
export type UIWebsiteMargin = z.infer<typeof isUIWebsiteMargin>;
|
||||
|
||||
export const isViewportPositionVertical = z.enum(["top", "middle", "bottom"]);
|
||||
|
||||
export type ViewportPositionVertical = z.infer<typeof isViewportPositionVertical>;
|
||||
|
||||
export const isViewportPositionHorizontal = z.enum(["left", "middle", "right"]);
|
||||
|
||||
export type ViewportPositionHorizontal = z.infer<typeof isViewportPositionHorizontal>;
|
||||
|
||||
export const isUIWebsitePosition = z.object({
|
||||
vertical: isViewportPositionVertical,
|
||||
horizontal: isViewportPositionHorizontal,
|
||||
});
|
||||
|
||||
export type UIWebsitePosition = z.infer<typeof isUIWebsitePosition>;
|
||||
|
||||
export const isUIWebsiteSize = z.object({
|
||||
height: isUIWebsiteCSSValue,
|
||||
width: isUIWebsiteCSSValue,
|
||||
});
|
||||
|
||||
export type UIWebsiteSize = z.infer<typeof isUIWebsiteSize>;
|
||||
|
||||
export const isCreateUIWebsiteEvent = z.object({
|
||||
url: z.string(),
|
||||
visible: z.optional(z.boolean()),
|
||||
allowApi: z.optional(z.boolean()),
|
||||
allowPolicy: z.optional(z.string()),
|
||||
position: isUIWebsitePosition,
|
||||
size: isUIWebsiteSize,
|
||||
margin: isUIWebsiteMargin.optional(),
|
||||
});
|
||||
|
||||
export type CreateUIWebsiteEvent = z.infer<typeof isCreateUIWebsiteEvent>;
|
||||
|
||||
export const isModifyUIWebsiteEvent = z.object({
|
||||
id: z.string(),
|
||||
url: z.string().optional(),
|
||||
visible: z.boolean().optional(),
|
||||
position: isUIWebsitePosition.optional(),
|
||||
size: isUIWebsiteSize.optional(),
|
||||
margin: isUIWebsiteMargin.optional(),
|
||||
});
|
||||
|
||||
export type ModifyUIWebsiteEvent = z.infer<typeof isModifyUIWebsiteEvent>;
|
||||
|
||||
export const isUIWebsite = z.object({
|
||||
id: z.string(),
|
||||
url: z.string(),
|
||||
visible: z.boolean(),
|
||||
allowApi: z.boolean(),
|
||||
allowPolicy: z.string(),
|
||||
position: isUIWebsitePosition,
|
||||
size: isUIWebsiteSize,
|
||||
margin: isUIWebsiteMargin.optional(),
|
||||
});
|
||||
|
||||
export type UIWebsite = z.infer<typeof isUIWebsite>;
|
||||
@@ -35,6 +35,7 @@ import type { RemotePlayerClickedEvent } from "./Events/RemotePlayerClickedEvent
|
||||
import { AddActionsMenuKeyToRemotePlayerEvent } from "./Events/AddActionsMenuKeyToRemotePlayerEvent";
|
||||
import type { ActionsMenuActionClickedEvent } from "./Events/ActionsMenuActionClickedEvent";
|
||||
import { RemoveActionsMenuKeyFromRemotePlayerEvent } from "./Events/RemoveActionsMenuKeyFromRemotePlayerEvent";
|
||||
import { ModifyUIWebsiteEvent } from "./Events/ui/UIWebsite";
|
||||
|
||||
type AnswererCallback<T extends keyof IframeQueryMap> = (
|
||||
query: IframeQueryMap[T]["query"],
|
||||
@@ -112,6 +113,9 @@ class IframeListener {
|
||||
private readonly _modifyEmbeddedWebsiteStream: Subject<ModifyEmbeddedWebsiteEvent> = new Subject();
|
||||
public readonly modifyEmbeddedWebsiteStream = this._modifyEmbeddedWebsiteStream.asObservable();
|
||||
|
||||
private readonly _modifyUIWebsiteStream: Subject<ModifyUIWebsiteEvent> = new Subject();
|
||||
public readonly modifyUIWebsiteStream = this._modifyUIWebsiteStream.asObservable();
|
||||
|
||||
private readonly iframes = new Set<HTMLIFrameElement>();
|
||||
private readonly iframeCloseCallbacks = new Map<HTMLIFrameElement, (() => void)[]>();
|
||||
private readonly scripts = new Map<string, HTMLIFrameElement>();
|
||||
@@ -276,6 +280,8 @@ class IframeListener {
|
||||
this._setTilesStream.next(iframeEvent.data);
|
||||
} else if (iframeEvent.type == "modifyEmbeddedWebsite") {
|
||||
this._modifyEmbeddedWebsiteStream.next(iframeEvent.data);
|
||||
} else if (iframeEvent.type == "modifyUIWebsite") {
|
||||
this._modifyUIWebsiteStream.next(iframeEvent.data);
|
||||
} else if (iframeEvent.type == "registerMenu") {
|
||||
const dataName = iframeEvent.data.name;
|
||||
this.iframeCloseCallbacks.get(iframe)?.push(() => {
|
||||
|
||||
@@ -0,0 +1,338 @@
|
||||
import {
|
||||
CreateUIWebsiteEvent,
|
||||
UIWebsiteCSSValue,
|
||||
UIWebsiteMargin,
|
||||
UIWebsitePosition,
|
||||
UIWebsiteSize,
|
||||
ViewportPositionHorizontal,
|
||||
ViewportPositionVertical,
|
||||
UIWebsite as UIWebsiteCore,
|
||||
} from "../../Events/ui/UIWebsite";
|
||||
import { IframeApiContribution, queryWorkadventure, sendToWorkadventure } from "../IframeApiContribution";
|
||||
|
||||
class UIWebsitePositionInternal {
|
||||
private readonly website: UIWebsite;
|
||||
private _vertical: ViewportPositionVertical;
|
||||
private _horizontal: ViewportPositionHorizontal;
|
||||
|
||||
constructor(uiWebsite: UIWebsite, position: UIWebsitePosition) {
|
||||
this.website = uiWebsite;
|
||||
this._vertical = position.vertical;
|
||||
this._horizontal = position.horizontal;
|
||||
}
|
||||
|
||||
public get vertical() {
|
||||
return this._vertical;
|
||||
}
|
||||
|
||||
public set vertical(vertical: ViewportPositionVertical) {
|
||||
this._vertical = vertical;
|
||||
sendToWorkadventure({
|
||||
type: "modifyUIWebsite",
|
||||
data: {
|
||||
id: this.website.id,
|
||||
position: {
|
||||
vertical: this._vertical,
|
||||
horizontal: this._horizontal,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
public get horizontal() {
|
||||
return this._horizontal;
|
||||
}
|
||||
|
||||
public set horizontal(horizontal: ViewportPositionHorizontal) {
|
||||
this._horizontal = horizontal;
|
||||
sendToWorkadventure({
|
||||
type: "modifyUIWebsite",
|
||||
data: {
|
||||
id: this.website.id,
|
||||
position: {
|
||||
vertical: this._vertical,
|
||||
horizontal: this._horizontal,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class UIWebsiteSizeInternal {
|
||||
private readonly website: UIWebsite;
|
||||
private _height: UIWebsiteCSSValue;
|
||||
private _width: UIWebsiteCSSValue;
|
||||
|
||||
constructor(uiWebsite: UIWebsite, size: UIWebsiteSize) {
|
||||
this.website = uiWebsite;
|
||||
this._height = size.height;
|
||||
this._width = size.width;
|
||||
}
|
||||
|
||||
public get height() {
|
||||
return this._height;
|
||||
}
|
||||
|
||||
public set height(height: UIWebsiteCSSValue) {
|
||||
this._height = height;
|
||||
sendToWorkadventure({
|
||||
type: "modifyUIWebsite",
|
||||
data: {
|
||||
id: this.website.id,
|
||||
size: {
|
||||
height: this._height,
|
||||
width: this._width,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
public get width() {
|
||||
return this._height;
|
||||
}
|
||||
|
||||
public set width(width: UIWebsiteCSSValue) {
|
||||
this._width = width;
|
||||
sendToWorkadventure({
|
||||
type: "modifyUIWebsite",
|
||||
data: {
|
||||
id: this.website.id,
|
||||
size: {
|
||||
height: this._height,
|
||||
width: this._width,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class UIWebsiteMarginInternal {
|
||||
private readonly website: UIWebsite;
|
||||
private _top?: UIWebsiteCSSValue;
|
||||
private _bottom?: UIWebsiteCSSValue;
|
||||
private _left?: UIWebsiteCSSValue;
|
||||
private _right?: UIWebsiteCSSValue;
|
||||
|
||||
constructor(uiWebsite: UIWebsite, margin: UIWebsiteMargin) {
|
||||
this.website = uiWebsite;
|
||||
this._top = margin.top;
|
||||
this._bottom = margin.bottom;
|
||||
this._left = margin.left;
|
||||
this._right = margin.right;
|
||||
}
|
||||
|
||||
public get top() {
|
||||
return this._top;
|
||||
}
|
||||
|
||||
public set top(top: UIWebsiteCSSValue | undefined) {
|
||||
this._top = top;
|
||||
sendToWorkadventure({
|
||||
type: "modifyUIWebsite",
|
||||
data: {
|
||||
id: this.website.id,
|
||||
margin: {
|
||||
top: this._top,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
public get bottom() {
|
||||
return this._bottom;
|
||||
}
|
||||
|
||||
public set bottom(bottom: UIWebsiteCSSValue | undefined) {
|
||||
this._bottom = bottom;
|
||||
sendToWorkadventure({
|
||||
type: "modifyUIWebsite",
|
||||
data: {
|
||||
id: this.website.id,
|
||||
margin: {
|
||||
bottom: this._bottom,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
public get left() {
|
||||
return this._left;
|
||||
}
|
||||
|
||||
public set left(left: UIWebsiteCSSValue | undefined) {
|
||||
this._left = left;
|
||||
sendToWorkadventure({
|
||||
type: "modifyUIWebsite",
|
||||
data: {
|
||||
id: this.website.id,
|
||||
margin: {
|
||||
left: this._left,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
public get right() {
|
||||
return this._right;
|
||||
}
|
||||
|
||||
public set right(right: UIWebsiteCSSValue | undefined) {
|
||||
this._right = right;
|
||||
sendToWorkadventure({
|
||||
type: "modifyUIWebsite",
|
||||
data: {
|
||||
id: this.website.id,
|
||||
margin: {
|
||||
right: this._right,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class UIWebsite {
|
||||
public readonly id: string;
|
||||
private _url: string;
|
||||
private _visible: boolean;
|
||||
private readonly _allowPolicy: string;
|
||||
private readonly _allowApi: boolean;
|
||||
private _position: UIWebsitePositionInternal;
|
||||
private _size: UIWebsiteSizeInternal;
|
||||
private _margin: UIWebsiteMarginInternal;
|
||||
|
||||
constructor(config: UIWebsiteCore) {
|
||||
this.id = config.id;
|
||||
this._url = config.url;
|
||||
this._visible = config.visible ?? true;
|
||||
this._allowPolicy = config.allowPolicy ?? "";
|
||||
this._allowApi = config.allowApi ?? false;
|
||||
this._position = new UIWebsitePositionInternal(this, config.position);
|
||||
this._size = new UIWebsiteSizeInternal(this, config.size);
|
||||
this._margin = new UIWebsiteMarginInternal(this, config.margin ?? {});
|
||||
}
|
||||
|
||||
public get url() {
|
||||
return this._url;
|
||||
}
|
||||
|
||||
public set url(url: string) {
|
||||
this._url = url;
|
||||
sendToWorkadventure({
|
||||
type: "modifyUIWebsite",
|
||||
data: {
|
||||
id: this.id,
|
||||
url: this._url,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
public get visible() {
|
||||
return this._visible;
|
||||
}
|
||||
|
||||
public set visible(visible: boolean) {
|
||||
this._visible = visible;
|
||||
sendToWorkadventure({
|
||||
type: "modifyUIWebsite",
|
||||
data: {
|
||||
id: this.id,
|
||||
visible: this._visible,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
public get allowPolicy() {
|
||||
return this._allowPolicy;
|
||||
}
|
||||
|
||||
public get allowApi() {
|
||||
return this._allowApi;
|
||||
}
|
||||
|
||||
public get position() {
|
||||
return this._position;
|
||||
}
|
||||
|
||||
public set position(position: UIWebsitePosition) {
|
||||
this._position = new UIWebsitePositionInternal(this, position);
|
||||
sendToWorkadventure({
|
||||
type: "modifyUIWebsite",
|
||||
data: {
|
||||
id: this.id,
|
||||
position: {
|
||||
vertical: this._position.vertical,
|
||||
horizontal: this._position.horizontal,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
public get size() {
|
||||
return this._size;
|
||||
}
|
||||
|
||||
public set size(size: UIWebsiteSize) {
|
||||
this._size = new UIWebsiteSizeInternal(this, size);
|
||||
sendToWorkadventure({
|
||||
type: "modifyUIWebsite",
|
||||
data: {
|
||||
id: this.id,
|
||||
size: {
|
||||
height: this._size.height,
|
||||
width: this._size.width,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
public get margin() {
|
||||
return this._margin;
|
||||
}
|
||||
|
||||
public set margin(margin: UIWebsiteMargin) {
|
||||
this._margin = new UIWebsiteMarginInternal(this, margin);
|
||||
sendToWorkadventure({
|
||||
type: "modifyUIWebsite",
|
||||
data: {
|
||||
id: this.id,
|
||||
margin: {
|
||||
top: this._margin.top,
|
||||
bottom: this._margin.bottom,
|
||||
left: this._margin.left,
|
||||
right: this._margin.right,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
close() {
|
||||
return queryWorkadventure({
|
||||
type: "closeUIWebsite",
|
||||
data: this.id,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class UIWebsiteCommands extends IframeApiContribution<UIWebsiteCommands> {
|
||||
callbacks = [];
|
||||
|
||||
async open(createUIWebsite: CreateUIWebsiteEvent): Promise<UIWebsite> {
|
||||
const result = await queryWorkadventure({
|
||||
type: "openUIWebsite",
|
||||
data: createUIWebsite,
|
||||
});
|
||||
|
||||
return new UIWebsite(result);
|
||||
}
|
||||
|
||||
async getAll(): Promise<UIWebsite[]> {
|
||||
const result = await queryWorkadventure({
|
||||
type: "getUIWebsites",
|
||||
data: undefined,
|
||||
});
|
||||
|
||||
return result.map((current) => new UIWebsite(current));
|
||||
}
|
||||
}
|
||||
|
||||
export default new UIWebsiteCommands();
|
||||
@@ -14,6 +14,8 @@ import {
|
||||
isActionsMenuActionClickedEvent,
|
||||
} from "../Events/ActionsMenuActionClickedEvent";
|
||||
import { Observable, Subject } from "rxjs";
|
||||
import type { UIWebsiteCommands } from "./Ui/UIWebsite";
|
||||
import website from "./Ui/UIWebsite";
|
||||
|
||||
let popupId = 0;
|
||||
const popups: Map<number, Popup> = new Map<number, Popup>();
|
||||
@@ -283,6 +285,10 @@ export class WorkAdventureUiCommands extends IframeApiContribution<WorkAdventure
|
||||
actionMessages.set(actionMessage.uuid, actionMessage);
|
||||
return actionMessage;
|
||||
}
|
||||
|
||||
get website(): UIWebsiteCommands {
|
||||
return website;
|
||||
}
|
||||
}
|
||||
|
||||
export default new WorkAdventureUiCommands();
|
||||
|
||||
@@ -39,6 +39,8 @@
|
||||
import ActionsMenu from "./ActionsMenu/ActionsMenu.svelte";
|
||||
import Lazy from "./Lazy.svelte";
|
||||
import { showDesktopCapturerSourcePicker } from "../Stores/ScreenSharingStore";
|
||||
import UiWebsiteContainer from "./UI/Website/UIWebsiteContainer.svelte";
|
||||
import { uiWebsitesStore } from "../Stores/UIWebsiteStore";
|
||||
|
||||
let mainLayout: HTMLDivElement;
|
||||
|
||||
@@ -128,6 +130,10 @@
|
||||
{#if hasEmbedScreen}
|
||||
<EmbedScreensContainer />
|
||||
{/if}
|
||||
|
||||
{#if $uiWebsitesStore}
|
||||
<UiWebsiteContainer />
|
||||
{/if}
|
||||
</section>
|
||||
|
||||
<section id="main-layout-baseline">
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
<script lang="ts">
|
||||
import { uiWebsitesStore } from "../../../Stores/UIWebsiteStore";
|
||||
import UiWebsiteLayer from "./UIWebsiteLayer.svelte";
|
||||
</script>
|
||||
|
||||
<div id="ui-website-container">
|
||||
{#each $uiWebsitesStore.reverse() as uiWebsite (uiWebsite.id)}
|
||||
<UiWebsiteLayer {uiWebsite} />
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
#ui-website-container {
|
||||
position: absolute;
|
||||
z-index: 180;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,66 @@
|
||||
<script lang="ts">
|
||||
import { onDestroy, onMount } from "svelte";
|
||||
import { UIWebsite } from "../../../Api/Events/ui/UIWebsite";
|
||||
import { iframeListener } from "../../../Api/IframeListener";
|
||||
|
||||
export let uiWebsite: UIWebsite;
|
||||
|
||||
let main: HTMLDivElement;
|
||||
const iframe = document.createElement("iframe");
|
||||
|
||||
$: {
|
||||
iframe.id = `ui-website-${uiWebsite.id}`;
|
||||
iframe.src = uiWebsite.url;
|
||||
iframe.title = uiWebsite.url;
|
||||
iframe.style.border = "0";
|
||||
iframe.allow = uiWebsite.allowPolicy ?? "";
|
||||
iframe.style.height = uiWebsite.size.height;
|
||||
iframe.style.width = uiWebsite.size.width;
|
||||
iframe.style.visibility = uiWebsite.visible ? "visible" : "hidden";
|
||||
iframe.style.margin = uiWebsite.margin
|
||||
? `${uiWebsite.margin.top ? uiWebsite.margin.top : "O"} ${
|
||||
uiWebsite.margin.right ? uiWebsite.margin.right : "O"
|
||||
} ${uiWebsite.margin.bottom ? uiWebsite.margin.bottom : "O"} ${
|
||||
uiWebsite.margin.left ? uiWebsite.margin.left : "O"
|
||||
}`
|
||||
: "0";
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
main.appendChild(iframe);
|
||||
|
||||
if (uiWebsite.allowApi) {
|
||||
iframeListener.registerIframe(iframe);
|
||||
}
|
||||
});
|
||||
|
||||
onDestroy(() => {
|
||||
if (uiWebsite.allowApi) {
|
||||
iframeListener.unregisterIframe(iframe);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<div
|
||||
bind:this={main}
|
||||
class="layer"
|
||||
style:justify-content={uiWebsite.position.horizontal === "middle"
|
||||
? "center"
|
||||
: uiWebsite.position.horizontal === "right"
|
||||
? "end"
|
||||
: "start"}
|
||||
style:align-items={uiWebsite.position.vertical === "middle"
|
||||
? "center"
|
||||
: uiWebsite.position.vertical === "bottom"
|
||||
? "end"
|
||||
: "top"}
|
||||
/>
|
||||
|
||||
<style lang="scss">
|
||||
.layer {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
position: absolute;
|
||||
}
|
||||
</style>
|
||||
@@ -102,6 +102,7 @@ import CancelablePromise from "cancelable-promise";
|
||||
import { Deferred } from "ts-deferred";
|
||||
import { SuperLoaderPlugin } from "../Services/SuperLoaderPlugin";
|
||||
import { ErrorScreenMessage, PlayerDetailsUpdatedMessage } from "../../Messages/ts-proto-generated/protos/messages";
|
||||
import { uiWebsiteManager } from "./UI/UIWebsiteManager";
|
||||
export interface GameSceneInitInterface {
|
||||
initPosition: PointInterface | null;
|
||||
reconnecting: boolean;
|
||||
@@ -1298,6 +1299,18 @@ ${escapedMessage}
|
||||
return coWebsiteManager.closeCoWebsites();
|
||||
});
|
||||
|
||||
iframeListener.registerAnswerer("openUIWebsite", (websiteConfig) => {
|
||||
return uiWebsiteManager.open(websiteConfig);
|
||||
});
|
||||
|
||||
iframeListener.registerAnswerer("getUIWebsites", () => {
|
||||
return uiWebsiteManager.getAll();
|
||||
});
|
||||
|
||||
iframeListener.registerAnswerer("closeUIWebsite", (websiteId) => {
|
||||
return uiWebsiteManager.close(websiteId);
|
||||
});
|
||||
|
||||
iframeListener.registerAnswerer("getMapData", () => {
|
||||
return {
|
||||
data: this.gameMap.getMap(),
|
||||
@@ -1594,6 +1607,9 @@ ${escapedMessage}
|
||||
iframeListener.unregisterAnswerer("getCoWebsites");
|
||||
iframeListener.unregisterAnswerer("setPlayerOutline");
|
||||
iframeListener.unregisterAnswerer("setVariable");
|
||||
iframeListener.unregisterAnswerer("openUIWebsite");
|
||||
iframeListener.unregisterAnswerer("getUIWebsites");
|
||||
iframeListener.unregisterAnswerer("closeUIWebsite");
|
||||
this.sharedVariablesManager?.close();
|
||||
this.embeddedWebsiteManager?.close();
|
||||
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
import { get } from "svelte/store";
|
||||
import { CreateUIWebsiteEvent, ModifyUIWebsiteEvent, UIWebsite } from "../../../Api/Events/ui/UIWebsite";
|
||||
import { iframeListener } from "../../../Api/IframeListener";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { uiWebsitesStore } from "../../../Stores/UIWebsiteStore";
|
||||
|
||||
class UIWebsiteManager {
|
||||
constructor() {
|
||||
iframeListener.modifyUIWebsiteStream.subscribe((websiteEvent: ModifyUIWebsiteEvent) => {
|
||||
const website = get(uiWebsitesStore).find((currentWebsite) => currentWebsite.id === websiteEvent.id);
|
||||
if (!website) {
|
||||
throw new Error(`Could not find ui website with the id "${websiteEvent.id}" in your map`);
|
||||
}
|
||||
|
||||
if (websiteEvent.url) {
|
||||
website.url = websiteEvent.url;
|
||||
}
|
||||
|
||||
if (websiteEvent.visible !== undefined) {
|
||||
website.visible = websiteEvent.visible;
|
||||
}
|
||||
|
||||
if (websiteEvent.position) {
|
||||
if (websiteEvent.position.horizontal) {
|
||||
website.position.horizontal = websiteEvent.position.horizontal;
|
||||
}
|
||||
|
||||
if (websiteEvent.position.vertical) {
|
||||
website.position.vertical = websiteEvent.position.vertical;
|
||||
}
|
||||
}
|
||||
|
||||
if (websiteEvent.size) {
|
||||
if (websiteEvent.size.height) {
|
||||
website.size.height = websiteEvent.size.height;
|
||||
}
|
||||
|
||||
if (websiteEvent.size.width) {
|
||||
website.size.width = websiteEvent.size.width;
|
||||
}
|
||||
}
|
||||
|
||||
if (websiteEvent.margin) {
|
||||
website.margin = {};
|
||||
|
||||
if (websiteEvent.margin.top !== undefined) {
|
||||
website.margin.top = websiteEvent.margin.top;
|
||||
}
|
||||
|
||||
if (websiteEvent.margin.bottom !== undefined) {
|
||||
website.margin.bottom = websiteEvent.margin.bottom;
|
||||
}
|
||||
|
||||
if (websiteEvent.margin.left !== undefined) {
|
||||
website.margin.left = websiteEvent.margin.left;
|
||||
}
|
||||
|
||||
if (websiteEvent.margin.right !== undefined) {
|
||||
website.margin.right = websiteEvent.margin.right;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public open(websiteConfig: CreateUIWebsiteEvent): UIWebsite {
|
||||
const newWebsite: UIWebsite = {
|
||||
...websiteConfig,
|
||||
id: uuidv4(),
|
||||
visible: websiteConfig.visible ?? true,
|
||||
allowPolicy: websiteConfig.allowPolicy ?? "",
|
||||
allowApi: websiteConfig.allowApi ?? false,
|
||||
};
|
||||
|
||||
uiWebsitesStore.add(newWebsite);
|
||||
|
||||
return newWebsite;
|
||||
}
|
||||
|
||||
public getAll(): UIWebsite[] {
|
||||
return get(uiWebsitesStore);
|
||||
}
|
||||
|
||||
public close(websiteId: string) {
|
||||
const uiWebsite = get(uiWebsitesStore).find((currentWebsite) => currentWebsite.id === websiteId);
|
||||
|
||||
if (!uiWebsite) {
|
||||
return;
|
||||
}
|
||||
|
||||
uiWebsitesStore.remove(uiWebsite);
|
||||
}
|
||||
}
|
||||
|
||||
export const uiWebsiteManager = new UIWebsiteManager();
|
||||
@@ -0,0 +1,20 @@
|
||||
import { writable } from "svelte/store";
|
||||
import { UIWebsite } from "../Api/Events/ui/UIWebsite";
|
||||
|
||||
function createUIWebsiteStore() {
|
||||
const { subscribe, update, set } = writable(Array<UIWebsite>());
|
||||
|
||||
set(Array<UIWebsite>());
|
||||
|
||||
return {
|
||||
subscribe,
|
||||
add: (uiWebsite: UIWebsite) => {
|
||||
update((currentArray) => [...currentArray, uiWebsite]);
|
||||
},
|
||||
remove: (uiWebsite: UIWebsite) => {
|
||||
update((currentArray) => currentArray.filter((currentWebsite) => currentWebsite.id !== uiWebsite.id));
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export const uiWebsitesStore = createUIWebsiteStore();
|
||||
Reference in New Issue
Block a user