Merge branch 'develop' of github.com:thecodingmachine/workadventure into develop
This commit is contained in:
commit
1f11ab57f3
@ -570,6 +570,7 @@ export class GameRoom {
|
||||
mapUrl,
|
||||
authenticationMandatory: null,
|
||||
group: null,
|
||||
showPoweredBy: true,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -14,7 +14,7 @@ When controls are disabled, the user cannot move anymore using keyboard input. T
|
||||
|
||||
Example:
|
||||
|
||||
```javascript
|
||||
```ts
|
||||
WA.room.onEnterLayer('myZone').subscribe(() => {
|
||||
WA.controls.disablePlayerControls();
|
||||
WA.ui.openPopup("popupRectangle", 'This is an imporant message!', [{
|
||||
@ -25,5 +25,28 @@ WA.room.onEnterLayer('myZone').subscribe(() => {
|
||||
popup.close();
|
||||
}
|
||||
}]);
|
||||
})
|
||||
});
|
||||
```
|
||||
|
||||
### Disabling / restoring proximity meeting
|
||||
|
||||
```
|
||||
WA.controls.disablePlayerProximityMeeting(): void
|
||||
WA.controls.restorePlayerProximityMeeting(): void
|
||||
```
|
||||
|
||||
These 2 methods can be used to completely disable player proximity meeting and to enable them again.
|
||||
|
||||
When proximity meeting are disabled, the user cannot speak with anyone.
|
||||
|
||||
Example:
|
||||
|
||||
```ts
|
||||
WA.room.onEnterLayer('myZone').subscribe(() => {
|
||||
WA.controls.disablePlayerProximityMeeting();
|
||||
});
|
||||
|
||||
WA.room.onLeaveLayer('myZone').subscribe(() => {
|
||||
WA.controls.restorePlayerProximityMeeting();
|
||||
});
|
||||
```
|
||||
|
@ -34,7 +34,7 @@ Please note that `openPopup` returns an object of the `Popup` class. Also, the c
|
||||
|
||||
The `Popup` class that represents an open popup contains a single method: `close()`. This will obviously close the popup when called.
|
||||
|
||||
```javascript
|
||||
```ts
|
||||
class Popup {
|
||||
/**
|
||||
* Closes the popup
|
||||
@ -45,7 +45,7 @@ class Popup {
|
||||
|
||||
Example:
|
||||
|
||||
```javascript
|
||||
```ts
|
||||
let helloWorldPopup;
|
||||
|
||||
// Open the popup when we enter a given zone
|
||||
@ -90,8 +90,8 @@ Custom menu exist only until the map is unloaded, or you leave the iframe zone o
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Example:
|
||||
```javascript
|
||||
Example:
|
||||
```ts
|
||||
const menu = WA.ui.registerMenuCommand('menu test',
|
||||
{
|
||||
callback: () => {
|
||||
@ -107,7 +107,7 @@ Please note that `registerMenuCommand` returns an object of the `Menu` class.
|
||||
|
||||
The `Menu` class contains a single method: `remove(): void`. This will obviously remove the menu when called.
|
||||
|
||||
```javascript
|
||||
```ts
|
||||
class Menu {
|
||||
/**
|
||||
* Remove the menu
|
||||
@ -120,7 +120,7 @@ class Menu {
|
||||
|
||||
### Awaiting User Confirmation (with space bar)
|
||||
|
||||
```
|
||||
```ts
|
||||
WA.ui.displayActionMessage({
|
||||
message: string,
|
||||
callback: () => void,
|
||||
@ -136,7 +136,7 @@ Displays a message at the bottom of the screen (that will disappear when space b
|
||||
|
||||
Example:
|
||||
|
||||
```javascript
|
||||
```ts
|
||||
const triggerMessage = WA.ui.displayActionMessage({
|
||||
message: "press 'space' to confirm",
|
||||
callback: () => {
|
||||
@ -154,7 +154,7 @@ Please note that `displayActionMessage` returns an object of the `ActionMessage`
|
||||
|
||||
The `ActionMessage` class contains a single method: `remove(): Promise<void>`. This will obviously remove the message when called.
|
||||
|
||||
```javascript
|
||||
```ts
|
||||
class ActionMessage {
|
||||
/**
|
||||
* Hides the message
|
||||
@ -173,7 +173,7 @@ When clicking on other player's WOKA, the contextual menu (we call it ActionsMen
|
||||
|
||||
To do that, we need to listen for the `onRemotePlayerClicked` stream and make use of the `remotePlayer` object that is passed by as a payload.
|
||||
|
||||
```javascript
|
||||
```ts
|
||||
WA.ui.onRemotePlayerClicked.subscribe((remotePlayer) => {
|
||||
remotePlayer.addAction('Ask to tell a joke', () => {
|
||||
console.log('I am NOT telling you a joke!');
|
||||
@ -182,7 +182,7 @@ WA.ui.onRemotePlayerClicked.subscribe((remotePlayer) => {
|
||||
```
|
||||
|
||||
`remotePlayer.addAction(actionName, callback)` returns an Action object, which can remove itself from ActionsMenu:
|
||||
```javascript
|
||||
```ts
|
||||
const action = remotePlayer.addAction('This will disappear!', () => {
|
||||
console.log('You managed to click me!');
|
||||
});
|
||||
@ -193,3 +193,95 @@ setTimeout(
|
||||
1000,
|
||||
);
|
||||
```
|
||||
|
||||
# Open fixed iframes
|
||||
|
||||
You can use the scripting API to display an iframe (so any HTML element) above the game. The iframe is positionned relative to the browser window (so unlike [embedded websites](website-in-map.md), the position of the iframe does not move when someone walks on the map).
|
||||
|
||||
<div class="col">
|
||||
<img src="images/ui-website.png" class="figure-img img-fluid rounded" alt="" />
|
||||
</div>
|
||||
|
||||
This functonnality creates an iframe positionned on the viewport.
|
||||
|
||||
## Display a UI website
|
||||
|
||||
```ts
|
||||
WA.ui.website.open(website: CreateUIWebsiteEvent): Promise<UIWebsite>
|
||||
|
||||
interface CreateUIWebsiteEvent {
|
||||
url: string, // Website URL
|
||||
visible?: boolean, // The website is visible or not
|
||||
allowApi?: boolean, // Allow scripting API on the website
|
||||
allowPolicy?: string, // The list of feature policies allowed
|
||||
position: {
|
||||
vertical: "top"|"middle"|"bottom",,
|
||||
horizontal: "left","middle","right",
|
||||
},
|
||||
size: { // Size on the UI (available units: px|em|%|cm|in|pc|pt|mm|ex|vw|vh|rem and others values auto|inherit)
|
||||
height: string,
|
||||
width: string,
|
||||
},
|
||||
margin?: { // Website margin (available units: px|em|%|cm|in|pc|pt|mm|ex|vw|vh|rem and others values auto|inherit)
|
||||
top?: string,
|
||||
bottom?: string,
|
||||
left?: string,
|
||||
right?: string,
|
||||
},
|
||||
}
|
||||
|
||||
interface UIWebsite {
|
||||
readonly id: string, // Unique ID
|
||||
url: string, // Website URL
|
||||
visible: boolean, // The website is visible or not
|
||||
readonly allowApi: boolean, // Allow scripting API on the website
|
||||
readonly allowPolicy: string, // The list of feature policies allowed
|
||||
position: {
|
||||
vertical: string, // Vertical position (top, middle, bottom)
|
||||
horizontal: string, // Horizontal position (left, middle, right)
|
||||
},
|
||||
size: { // Size on the UI (available units: px|em|%|cm|in|pc|pt|mm|ex|vw|vh|rem and others values auto|inherit)
|
||||
height: string,
|
||||
width: string,
|
||||
},
|
||||
margin?: { // Website margin (available units: px|em|%|cm|in|pc|pt|mm|ex|vw|vh|rem and others values auto|inherit)
|
||||
top?: string,
|
||||
bottom?: string,
|
||||
left?: string,
|
||||
right?: string,
|
||||
},
|
||||
close(): Promise<void>, // Close the current website instance
|
||||
}
|
||||
```
|
||||
|
||||
You can open a website with the `WA.ui.website.open()` method. It returns an `Promise<UIWebsite>` instance.
|
||||
|
||||
```ts
|
||||
const myWebsite = await WA.ui.website.open({
|
||||
url: "https://wikipedia.org",
|
||||
position: {
|
||||
vertical: "middle",
|
||||
horizontal: "middle",
|
||||
},
|
||||
size: {
|
||||
height: "50vh",
|
||||
width: "50vw",
|
||||
},
|
||||
});
|
||||
|
||||
myWebsite.position.vertical = "top";
|
||||
```
|
||||
|
||||
## Close a UI website
|
||||
You can close a website with the close function on the `UIWebsite` object
|
||||
|
||||
```ts
|
||||
myWebsite.close();
|
||||
```
|
||||
|
||||
## Get all UI websites
|
||||
You can get all websites with the `WA.ui.website.getAll()` method. It returns an `Promise<UIWebsite[]>` instance.
|
||||
|
||||
```ts
|
||||
WA.ui.website.getAll();
|
||||
```
|
||||
|
BIN
docs/maps/images/ui-website.png
Normal file
BIN
docs/maps/images/ui-website.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 454 KiB |
1
front/.gitignore
vendored
1
front/.gitignore
vendored
@ -5,3 +5,4 @@ dist/
|
||||
*.sh
|
||||
!templater.sh
|
||||
/public/iframe_api.js
|
||||
/public/es.js
|
||||
|
@ -21,7 +21,7 @@
|
||||
"lint-staged": "^12.3.7",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"prettier": "^2.3.1",
|
||||
"prettier-plugin-svelte": "^2.5.0",
|
||||
"prettier-plugin-svelte": "^2.7.0",
|
||||
"sass": "^1.49.7",
|
||||
"svelte": "^3.46.3",
|
||||
"svelte-check": "^2.1.0",
|
||||
@ -96,4 +96,4 @@
|
||||
"yarn run pretty"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
@ -40,6 +40,7 @@ import { isAddActionsMenuKeyToRemotePlayerEvent } from "./AddActionsMenuKeyToRem
|
||||
import type { ActionsMenuActionClickedEvent } from "./ActionsMenuActionClickedEvent";
|
||||
import { isRemoveActionsMenuKeyFromRemotePlayerEvent } from "./RemoveActionsMenuKeyFromRemotePlayerEvent";
|
||||
import { isGetPropertyEvent } from "./GetPropertyEvent";
|
||||
import { isCreateUIWebsiteEvent, isModifyUIWebsiteEvent, isUIWebsite } from "./ui/UIWebsite";
|
||||
|
||||
export interface TypedMessageEvent<T> extends MessageEvent {
|
||||
data: T;
|
||||
@ -97,6 +98,14 @@ export const isIframeEventWrapper = z.union([
|
||||
type: z.literal("restorePlayerControls"),
|
||||
data: z.undefined(),
|
||||
}),
|
||||
z.object({
|
||||
type: z.literal("disablePlayerProximityMeeting"),
|
||||
data: z.undefined(),
|
||||
}),
|
||||
z.object({
|
||||
type: z.literal("restorePlayerProximityMeeting"),
|
||||
data: z.undefined(),
|
||||
}),
|
||||
z.object({
|
||||
type: z.literal("displayBubble"),
|
||||
data: z.undefined(),
|
||||
@ -153,6 +162,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>;
|
||||
@ -266,6 +279,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;
|
||||
|
75
front/src/Api/Events/ui/UIWebsite.ts
Normal file
75
front/src/Api/Events/ui/UIWebsite.ts
Normal file
@ -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"],
|
||||
@ -58,6 +59,15 @@ class IframeListener {
|
||||
private readonly _disablePlayerControlStream: Subject<void> = new Subject();
|
||||
public readonly disablePlayerControlStream = this._disablePlayerControlStream.asObservable();
|
||||
|
||||
private readonly _enablePlayerControlStream: Subject<void> = new Subject();
|
||||
public readonly enablePlayerControlStream = this._enablePlayerControlStream.asObservable();
|
||||
|
||||
private readonly _disablePlayerProximityMeetingStream: Subject<void> = new Subject();
|
||||
public readonly disablePlayerProximityMeetingStream = this._disablePlayerProximityMeetingStream.asObservable();
|
||||
|
||||
private readonly _enablePlayerProximityMeetingStream: Subject<void> = new Subject();
|
||||
public readonly enablePlayerProximityMeetingStream = this._enablePlayerProximityMeetingStream.asObservable();
|
||||
|
||||
private readonly _cameraSetStream: Subject<CameraSetEvent> = new Subject();
|
||||
public readonly cameraSetStream = this._cameraSetStream.asObservable();
|
||||
|
||||
@ -73,9 +83,6 @@ class IframeListener {
|
||||
public readonly removeActionsMenuKeyFromRemotePlayerEvent =
|
||||
this._removeActionsMenuKeyFromRemotePlayerEvent.asObservable();
|
||||
|
||||
private readonly _enablePlayerControlStream: Subject<void> = new Subject();
|
||||
public readonly enablePlayerControlStream = this._enablePlayerControlStream.asObservable();
|
||||
|
||||
private readonly _closePopupStream: Subject<ClosePopupEvent> = new Subject();
|
||||
public readonly closePopupStream = this._closePopupStream.asObservable();
|
||||
|
||||
@ -115,6 +122,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>();
|
||||
@ -263,6 +273,10 @@ class IframeListener {
|
||||
this._disablePlayerControlStream.next();
|
||||
} else if (iframeEvent.type === "restorePlayerControls") {
|
||||
this._enablePlayerControlStream.next();
|
||||
} else if (iframeEvent.type === "disablePlayerProximityMeeting") {
|
||||
this._disablePlayerProximityMeetingStream.next();
|
||||
} else if (iframeEvent.type === "restorePlayerProximityMeeting") {
|
||||
this._enablePlayerProximityMeetingStream.next();
|
||||
} else if (iframeEvent.type === "displayBubble") {
|
||||
this._displayBubbleStream.next();
|
||||
} else if (iframeEvent.type === "removeBubble") {
|
||||
@ -279,6 +293,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(() => {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { requestedCameraState, requestedMicrophoneState, silentStore } from "../../Stores/MediaStore";
|
||||
import { get } from "svelte/store";
|
||||
import { WorkAdventureDesktopApi } from "@wa-preload-app";
|
||||
import { WorkAdventureDesktopApi } from "../../Interfaces/DesktopAppInterfaces";
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
|
338
front/src/Api/iframe/Ui/UIWebsite.ts
Normal file
338
front/src/Api/iframe/Ui/UIWebsite.ts
Normal file
@ -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();
|
@ -10,6 +10,14 @@ export class WorkadventureControlsCommands extends IframeApiContribution<Workadv
|
||||
restorePlayerControls(): void {
|
||||
sendToWorkadventure({ type: "restorePlayerControls", data: undefined });
|
||||
}
|
||||
|
||||
disablePlayerProximityMeeting(): void {
|
||||
sendToWorkadventure({ type: "disablePlayerProximityMeeting", data: undefined });
|
||||
}
|
||||
|
||||
restorePlayerProximityMeeting(): void {
|
||||
sendToWorkadventure({ type: "restorePlayerProximityMeeting", data: undefined });
|
||||
}
|
||||
}
|
||||
|
||||
export default new WorkadventureControlsCommands();
|
||||
|
@ -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>();
|
||||
@ -290,6 +292,10 @@ export class WorkAdventureUiCommands extends IframeApiContribution<WorkAdventure
|
||||
actionMessages.set(actionMessage.uuid, actionMessage);
|
||||
return actionMessage;
|
||||
}
|
||||
|
||||
get website(): UIWebsiteCommands {
|
||||
return website;
|
||||
}
|
||||
}
|
||||
|
||||
export default new WorkAdventureUiCommands();
|
||||
|
@ -63,7 +63,7 @@
|
||||
<section class="action">
|
||||
<button type="submit" class="nes-btn is-primary loginSceneFormSubmit">{$LL.login.continue()}</button>
|
||||
</section>
|
||||
{#if logo !== logoImg}
|
||||
{#if logo !== logoImg && gameManager.currentStartedRoom.showPoweredBy !== false}
|
||||
<section class="text-center powered-by">
|
||||
<img src={poweredByWorkAdventureImg} alt="Powered by WorkAdventure" />
|
||||
</section>
|
||||
|
@ -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">
|
||||
|
@ -11,14 +11,17 @@
|
||||
import reload from "../images/reload.png";
|
||||
let errorScreen = get(errorScreenStore);
|
||||
|
||||
import error from "./images/error.png";
|
||||
let errorLogo = errorScreen?.image ?? error;
|
||||
|
||||
function click() {
|
||||
if (errorScreen.type === "unauthorized") void connectionManager.logout();
|
||||
if (errorScreen?.type === "unauthorized") void connectionManager.logout();
|
||||
else window.location.reload();
|
||||
}
|
||||
let details = errorScreen.details;
|
||||
let timeVar = errorScreen.timeToRetry ?? 0;
|
||||
let details = errorScreen?.details ?? "";
|
||||
let timeVar = errorScreen?.timeToRetry ?? 0;
|
||||
|
||||
if (errorScreen.type === "retry") {
|
||||
if (errorScreen?.type === "retry") {
|
||||
let interval = setInterval(() => {
|
||||
if (timeVar <= 1000) click();
|
||||
timeVar -= 1000;
|
||||
@ -29,24 +32,29 @@
|
||||
$: detailsStylized = (details ?? "").replace("{time}", `${timeVar / 1000}`);
|
||||
</script>
|
||||
|
||||
<main class="errorScreen" transition:fly={{ y: -200, duration: 500 }}>
|
||||
<div style="width: 90%;">
|
||||
<img src={logo} alt="WorkAdventure" class="logo" />
|
||||
<div><img src={$errorScreenStore.image} alt="" class="icon" /></div>
|
||||
{#if $errorScreenStore.type !== "retry"}<h2>{$errorScreenStore.title}</h2>{/if}
|
||||
<p>{$errorScreenStore.subtitle}</p>
|
||||
{#if $errorScreenStore.type !== "retry"}<p class="code">Code : {$errorScreenStore.code}</p>{/if}
|
||||
<p class="details">
|
||||
{detailsStylized}{#if $errorScreenStore.type === "retry"}<div class="loading" />{/if}
|
||||
</p>
|
||||
{#if ($errorScreenStore.type === "retry" && $errorScreenStore.canRetryManual) || $errorScreenStore.type === "unauthorized"}
|
||||
<button type="button" class="nes-btn is-primary button" on:click={click}>
|
||||
{#if $errorScreenStore.type === "retry"}<img src={reload} alt="" class="reload" />{/if}
|
||||
{$errorScreenStore.buttonTitle}
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
</main>
|
||||
{#if $errorScreenStore}
|
||||
<main class="errorScreen" transition:fly={{ y: -200, duration: 500 }}>
|
||||
<div style="width: 90%;">
|
||||
<img src={logo} alt="WorkAdventure" class="logo" />
|
||||
<div><img src={errorLogo} alt="Error logo" class="icon" /></div>
|
||||
{#if $errorScreenStore.type !== "retry"}<h2>{$errorScreenStore.title}</h2>{/if}
|
||||
{#if $errorScreenStore.subtitle}<p>{$errorScreenStore.subtitle}</p>{/if}
|
||||
{#if $errorScreenStore.type !== "retry"}<p class="code">Code : {$errorScreenStore.code}</p>{/if}
|
||||
<p class="details">
|
||||
{detailsStylized}
|
||||
{#if $errorScreenStore.type === "retry" || $errorScreenStore.type === "reconnecting"}
|
||||
<div class="loading" />
|
||||
{/if}
|
||||
</p>
|
||||
{#if ($errorScreenStore.type === "retry" && $errorScreenStore.canRetryManual) || $errorScreenStore.type === "unauthorized"}
|
||||
<button type="button" class="nes-btn is-primary button" on:click={click}>
|
||||
{#if $errorScreenStore.type === "retry"}<img src={reload} alt="" class="reload" />{/if}
|
||||
{$errorScreenStore.buttonTitle}
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
</main>
|
||||
{/if}
|
||||
|
||||
<style lang="scss">
|
||||
main.errorScreen {
|
||||
|
21
front/src/Components/UI/Website/UIWebsiteContainer.svelte
Normal file
21
front/src/Components/UI/Website/UIWebsiteContainer.svelte
Normal file
@ -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>
|
66
front/src/Components/UI/Website/UIWebsiteLayer.svelte
Normal file
66
front/src/Components/UI/Website/UIWebsiteLayer.svelte
Normal file
@ -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>
|
BIN
front/src/Components/UI/images/error.png
Normal file
BIN
front/src/Components/UI/images/error.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.3 KiB |
@ -5,7 +5,7 @@
|
||||
showDesktopCapturerSourcePicker,
|
||||
} from "../../Stores/ScreenSharingStore";
|
||||
import { onDestroy, onMount } from "svelte";
|
||||
import type { DesktopCapturerSource } from "@wa-preload-app";
|
||||
import type { DesktopCapturerSource } from "../../Interfaces/DesktopAppInterfaces";
|
||||
|
||||
let desktopCapturerSources: DesktopCapturerSource[] = [];
|
||||
let interval: ReturnType<typeof setInterval>;
|
||||
|
@ -296,7 +296,32 @@ class ConnectionManager {
|
||||
});
|
||||
|
||||
connection.connectionErrorStream.subscribe((event: CloseEvent) => {
|
||||
console.log("connectionErrorStream => An error occurred while connecting to socket server. Retrying");
|
||||
console.info(
|
||||
"An error occurred while connecting to socket server. Retrying => Event: ",
|
||||
event.reason,
|
||||
event.code,
|
||||
event
|
||||
);
|
||||
|
||||
//However, Chrome will rarely report any close code 1006 reasons to the Javascript side.
|
||||
//This is likely due to client security rules in the WebSocket spec to prevent abusing WebSocket.
|
||||
//(such as using it to scan for open ports on a destination server, or for generating lots of connections for a denial-of-service attack).
|
||||
// more detail here: https://www.rfc-editor.org/rfc/rfc6455#section-7.4.1
|
||||
if (event.code === 1006) {
|
||||
//check cookies
|
||||
const cookies = document.cookie.split(";");
|
||||
for (const cookie of cookies) {
|
||||
//check id cookie posthog exist
|
||||
const numberIndexPh = cookie.indexOf("_posthog=");
|
||||
if (numberIndexPh !== -1) {
|
||||
//if exist, remove posthog cookie
|
||||
document.cookie =
|
||||
cookie.slice(0, numberIndexPh + 9) +
|
||||
"; domain=.workadventu.re; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
reject(
|
||||
new Error(
|
||||
"An error occurred while connecting to socket server. Retrying. Code: " +
|
||||
|
@ -25,6 +25,7 @@ export class Room {
|
||||
private _canReport: boolean = false;
|
||||
private _loadingLogo: string | undefined;
|
||||
private _loginSceneLogo: string | undefined;
|
||||
private _showPoweredBy: boolean | undefined = true;
|
||||
|
||||
private constructor(private roomUrl: URL) {
|
||||
this.id = roomUrl.pathname;
|
||||
@ -120,6 +121,7 @@ export class Room {
|
||||
this._canReport = data.canReport ?? false;
|
||||
this._loadingLogo = data.loadingLogo ?? undefined;
|
||||
this._loginSceneLogo = data.loginSceneLogo ?? undefined;
|
||||
this._showPoweredBy = data.showPoweredBy ?? true;
|
||||
return new MapDetail(data.mapUrl);
|
||||
} else {
|
||||
console.log(data);
|
||||
@ -213,4 +215,8 @@ export class Room {
|
||||
get loginSceneLogo(): string | undefined {
|
||||
return this._loginSceneLogo;
|
||||
}
|
||||
|
||||
get showPoweredBy(): boolean | undefined {
|
||||
return this._showPoweredBy;
|
||||
}
|
||||
}
|
||||
|
21
front/src/Interfaces/DesktopAppInterfaces.ts
Normal file
21
front/src/Interfaces/DesktopAppInterfaces.ts
Normal file
@ -0,0 +1,21 @@
|
||||
// copy of Electron.SourcesOptions to avoid Electron dependency in front
|
||||
export interface SourcesOptions {
|
||||
types: string[];
|
||||
thumbnailSize?: { height: number; width: number };
|
||||
}
|
||||
|
||||
export interface DesktopCapturerSource {
|
||||
id: string;
|
||||
name: string;
|
||||
thumbnailURL: string;
|
||||
}
|
||||
|
||||
export type WorkAdventureDesktopApi = {
|
||||
desktop: boolean;
|
||||
isDevelopment: () => Promise<boolean>;
|
||||
getVersion: () => Promise<string>;
|
||||
notify: (txt: string) => void;
|
||||
onMuteToggle: (callback: () => void) => void;
|
||||
onCameraToggle: (callback: () => void) => void;
|
||||
getDesktopCapturerSources: (options: SourcesOptions) => Promise<DesktopCapturerSource[]>;
|
||||
};
|
@ -54,7 +54,7 @@ export class Loader {
|
||||
.catch((e) => console.warn("Could not load logo: ", logoResource, e));
|
||||
|
||||
let poweredByLogoPromise: CancelablePromise<Texture> | undefined;
|
||||
if (gameManager.currentStartedRoom.loadingLogo) {
|
||||
if (gameManager.currentStartedRoom.loadingLogo && gameManager.currentStartedRoom.showPoweredBy !== false) {
|
||||
poweredByLogoPromise = this.superLoad.image(
|
||||
"poweredByLogo",
|
||||
"static/images/Powered_By_WorkAdventure_Small.png"
|
||||
|
@ -4,5 +4,6 @@ export const DEPTH_TILE_INDEX = 0;
|
||||
//Note: Player characters use their y coordinate as their depth to simulate a perspective.
|
||||
//See the Character class.
|
||||
export const DEPTH_OVERLAY_INDEX = 10000;
|
||||
export const DEPTH_BUBBLE_CHAT_SPRITE = 99999;
|
||||
export const DEPTH_INGAME_TEXT_INDEX = 100000;
|
||||
export const DEPTH_UI_INDEX = 1000000;
|
||||
|
@ -76,6 +76,7 @@ import { contactPageStore } from "../../Stores/MenuStore";
|
||||
import type { WasCameraUpdatedEvent } from "../../Api/Events/WasCameraUpdatedEvent";
|
||||
import { audioManagerFileStore } from "../../Stores/AudioManagerStore";
|
||||
import { currentPlayerGroupLockStateStore } from "../../Stores/CurrentPlayerGroupStore";
|
||||
import { errorScreenStore } from "../../Stores/ErrorScreenStore";
|
||||
|
||||
import EVENT_TYPE = Phaser.Scenes.Events;
|
||||
import Texture = Phaser.Textures.Texture;
|
||||
@ -90,8 +91,8 @@ import FILE_LOAD_ERROR = Phaser.Loader.Events.FILE_LOAD_ERROR;
|
||||
import { MapStore } from "../../Stores/Utils/MapStore";
|
||||
import { followUsersColorStore } from "../../Stores/FollowStore";
|
||||
import { GameSceneUserInputHandler } from "../UserInput/GameSceneUserInputHandler";
|
||||
import { locale } from "../../i18n/i18n-svelte";
|
||||
import { i18nJson } from "../../i18n/locales";
|
||||
import LL, { locale } from "../../i18n/i18n-svelte";
|
||||
import { availabilityStatusStore, localVolumeStore } from "../../Stores/MediaStore";
|
||||
import { StringUtils } from "../../Utils/StringUtils";
|
||||
import { startLayerNamesStore } from "../../Stores/StartLayerNamesStore";
|
||||
@ -101,7 +102,9 @@ import type { CoWebsite } from "../../WebRtc/CoWebsite/CoWesbite";
|
||||
import CancelablePromise from "cancelable-promise";
|
||||
import { Deferred } from "ts-deferred";
|
||||
import { SuperLoaderPlugin } from "../Services/SuperLoaderPlugin";
|
||||
import { PlayerDetailsUpdatedMessage } from "../../Messages/ts-proto-generated/protos/messages";
|
||||
import { DEPTH_BUBBLE_CHAT_SPRITE } from "./DepthIndexes";
|
||||
import { ErrorScreenMessage, PlayerDetailsUpdatedMessage } from "../../Messages/ts-proto-generated/protos/messages";
|
||||
import { uiWebsiteManager } from "./UI/UIWebsiteManager";
|
||||
export interface GameSceneInitInterface {
|
||||
initPosition: PointInterface | null;
|
||||
reconnecting: boolean;
|
||||
@ -613,14 +616,30 @@ export class GameScene extends DirtyScene {
|
||||
if (this.isReconnecting) {
|
||||
setTimeout(() => {
|
||||
this.scene.sleep();
|
||||
this.scene.launch(ReconnectingSceneName);
|
||||
errorScreenStore.setError(
|
||||
ErrorScreenMessage.fromPartial({
|
||||
type: "reconnecting",
|
||||
code: "CONNECTION_LOST",
|
||||
title: get(LL).warning.connectionLostTitle(),
|
||||
details: get(LL).warning.connectionLostSubtitle(),
|
||||
})
|
||||
);
|
||||
//this.scene.launch(ReconnectingSceneName);
|
||||
}, 0);
|
||||
} else if (this.connection === undefined) {
|
||||
// Let's wait 1 second before printing the "connecting" screen to avoid blinking
|
||||
setTimeout(() => {
|
||||
if (this.connection === undefined) {
|
||||
this.scene.sleep();
|
||||
this.scene.launch(ReconnectingSceneName);
|
||||
errorScreenStore.setError(
|
||||
ErrorScreenMessage.fromPartial({
|
||||
type: "reconnecting",
|
||||
code: "CONNECTION_LOST",
|
||||
title: get(LL).warning.connectionLostTitle(),
|
||||
details: get(LL).warning.connectionLostSubtitle(),
|
||||
})
|
||||
);
|
||||
//this.scene.launch(ReconnectingSceneName);
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
@ -904,7 +923,9 @@ export class GameScene extends DirtyScene {
|
||||
// Analyze tags to find if we are admin. If yes, show console.
|
||||
|
||||
if (this.scene.isSleeping()) {
|
||||
this.scene.stop(ReconnectingSceneName);
|
||||
const error = get(errorScreenStore);
|
||||
if (error && error?.type === "reconnecting") errorScreenStore.delete();
|
||||
//this.scene.stop(ReconnectingSceneName);
|
||||
}
|
||||
|
||||
//init user position and play trigger to check layers properties
|
||||
@ -1111,6 +1132,24 @@ export class GameScene extends DirtyScene {
|
||||
})
|
||||
);
|
||||
|
||||
this.iframeSubscriptionList.push(
|
||||
iframeListener.enablePlayerControlStream.subscribe(() => {
|
||||
this.userInputManager.restoreControls();
|
||||
})
|
||||
);
|
||||
|
||||
this.iframeSubscriptionList.push(
|
||||
iframeListener.disablePlayerProximityMeetingStream.subscribe(() => {
|
||||
this.disableMediaBehaviors();
|
||||
})
|
||||
);
|
||||
|
||||
this.iframeSubscriptionList.push(
|
||||
iframeListener.enablePlayerProximityMeetingStream.subscribe(() => {
|
||||
this.enableMediaBehaviors();
|
||||
})
|
||||
);
|
||||
|
||||
this.iframeSubscriptionList.push(
|
||||
iframeListener.cameraSetStream.subscribe((cameraSetEvent) => {
|
||||
const duration = cameraSetEvent.smooth ? 1000 : 0;
|
||||
@ -1198,11 +1237,6 @@ export class GameScene extends DirtyScene {
|
||||
})
|
||||
);
|
||||
|
||||
this.iframeSubscriptionList.push(
|
||||
iframeListener.enablePlayerControlStream.subscribe(() => {
|
||||
this.userInputManager.restoreControls();
|
||||
})
|
||||
);
|
||||
this.iframeSubscriptionList.push(
|
||||
iframeListener.loadPageStream.subscribe((url: string) => {
|
||||
this.loadNextGameFromExitUrl(url)
|
||||
@ -1225,7 +1259,7 @@ export class GameScene extends DirtyScene {
|
||||
this.CurrentPlayer.y,
|
||||
"circleSprite-white"
|
||||
);
|
||||
scriptedBubbleSprite.setDisplayOrigin(48, 48);
|
||||
scriptedBubbleSprite.setDisplayOrigin(48, 48).setDepth(DEPTH_BUBBLE_CHAT_SPRITE);
|
||||
this.add.existing(scriptedBubbleSprite);
|
||||
})
|
||||
);
|
||||
@ -1313,6 +1347,17 @@ export class GameScene extends DirtyScene {
|
||||
data.propertyValue = this.gameMap.getLayerProperty(data.layerName, data.propertyName);
|
||||
return data;
|
||||
});
|
||||
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 {
|
||||
@ -1610,6 +1655,9 @@ export class GameScene extends DirtyScene {
|
||||
iframeListener.unregisterAnswerer("getCoWebsites");
|
||||
iframeListener.unregisterAnswerer("setPlayerOutline");
|
||||
iframeListener.unregisterAnswerer("setVariable");
|
||||
iframeListener.unregisterAnswerer("openUIWebsite");
|
||||
iframeListener.unregisterAnswerer("getUIWebsites");
|
||||
iframeListener.unregisterAnswerer("closeUIWebsite");
|
||||
this.sharedVariablesManager?.close();
|
||||
this.embeddedWebsiteManager?.close();
|
||||
|
||||
@ -2079,7 +2127,7 @@ export class GameScene extends DirtyScene {
|
||||
? "circleSprite-red"
|
||||
: "circleSprite-white"
|
||||
);
|
||||
sprite.setDisplayOrigin(48, 48);
|
||||
sprite.setDisplayOrigin(48, 48).setDepth(DEPTH_BUBBLE_CHAT_SPRITE);
|
||||
this.add.existing(sprite);
|
||||
this.groups.set(groupPositionMessage.groupId, sprite);
|
||||
if (this.currentPlayerGroupId === groupPositionMessage.groupId) {
|
||||
|
94
front/src/Phaser/Game/UI/UIWebsiteManager.ts
Normal file
94
front/src/Phaser/Game/UI/UIWebsiteManager.ts
Normal file
@ -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();
|
@ -94,6 +94,7 @@ export class CustomizeScene extends AbstractCharacterScene {
|
||||
}
|
||||
|
||||
public create(): void {
|
||||
this.selectedLayers = [0, 0, 0, 0, 0, 0];
|
||||
this.tryLoadLastUsedWokaLayers();
|
||||
waScaleManager.zoomModifier = 1;
|
||||
this.createSlotBackgroundTextures();
|
||||
@ -154,11 +155,10 @@ export class CustomizeScene extends AbstractCharacterScene {
|
||||
try {
|
||||
const savedWokaLayers = gameManager.getCharacterLayers();
|
||||
if (savedWokaLayers && savedWokaLayers.length !== 0) {
|
||||
this.selectedLayers = [];
|
||||
for (let i = 0; i < savedWokaLayers.length; i += 1) {
|
||||
this.selectedLayers.push(
|
||||
this.layers[i].findIndex((item) => item.id === gameManager.getCharacterLayers()[i])
|
||||
);
|
||||
const index = this.layers[i].findIndex((item) => item.id === gameManager.getCharacterLayers()[i]);
|
||||
// set first item as default if not found
|
||||
this.selectedLayers[i] = index !== -1 ? index : 0;
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
|
@ -5,13 +5,16 @@ import { ErrorScreenMessage } from "../Messages/ts-proto-generated/protos/messag
|
||||
* A store that contains one error of type WAError to be displayed.
|
||||
*/
|
||||
function createErrorScreenStore() {
|
||||
const { subscribe, set } = writable<ErrorScreenMessage>(undefined);
|
||||
const { subscribe, set } = writable<ErrorScreenMessage | undefined>(undefined);
|
||||
|
||||
return {
|
||||
subscribe,
|
||||
setError: (e: ErrorScreenMessage): void => {
|
||||
set(e);
|
||||
},
|
||||
delete: () => {
|
||||
set(undefined);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,7 @@ import { derived, Readable, readable, writable } from "svelte/store";
|
||||
import { peerStore } from "./PeerStore";
|
||||
import type { LocalStreamStoreValue } from "./MediaStore";
|
||||
import { myCameraVisibilityStore } from "./MyCameraStoreVisibility";
|
||||
import type { DesktopCapturerSource } from "@wa-preload-app";
|
||||
import type { DesktopCapturerSource } from "../Interfaces/DesktopAppInterfaces";
|
||||
|
||||
declare const navigator: any; // eslint-disable-line @typescript-eslint/no-explicit-any
|
||||
|
||||
|
20
front/src/Stores/UIWebsiteStore.ts
Normal file
20
front/src/Stores/UIWebsiteStore.ts
Normal file
@ -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();
|
@ -238,6 +238,7 @@ class CoWebsiteManager {
|
||||
|
||||
const iframe = coWebsite.getIframe();
|
||||
if (iframe) {
|
||||
this.activateMainLoaderAnimation();
|
||||
iframe.style.display = "none";
|
||||
}
|
||||
this.resizing = true;
|
||||
@ -258,6 +259,7 @@ class CoWebsiteManager {
|
||||
const iframe = coWebsite.getIframe();
|
||||
if (iframe) {
|
||||
iframe.style.display = "flex";
|
||||
this.desactivateMainLoaderAnimation();
|
||||
}
|
||||
this.resizing = false;
|
||||
});
|
||||
@ -273,6 +275,7 @@ class CoWebsiteManager {
|
||||
|
||||
const iframe = coWebsite.getIframe();
|
||||
if (iframe) {
|
||||
this.activateMainLoaderAnimation();
|
||||
iframe.style.display = "none";
|
||||
}
|
||||
this.resizing = true;
|
||||
@ -296,6 +299,7 @@ class CoWebsiteManager {
|
||||
const iframe = coWebsite.getIframe();
|
||||
if (iframe) {
|
||||
iframe.style.display = "flex";
|
||||
this.desactivateMainLoaderAnimation();
|
||||
}
|
||||
this.resizing = false;
|
||||
});
|
||||
@ -309,9 +313,7 @@ class CoWebsiteManager {
|
||||
|
||||
if (this.cowebsiteDom.classList.contains("closing")) {
|
||||
this.cowebsiteDom.classList.remove("closing");
|
||||
if (this.loaderAnimationInterval.interval) {
|
||||
clearInterval(this.loaderAnimationInterval.interval);
|
||||
}
|
||||
this.desactivateMainLoaderAnimation();
|
||||
this.loaderAnimationInterval.trails = undefined;
|
||||
}
|
||||
});
|
||||
@ -353,7 +355,10 @@ class CoWebsiteManager {
|
||||
this.fire();
|
||||
}
|
||||
|
||||
private loadMain(openingWidth?: number): void {
|
||||
private activateMainLoaderAnimation() {
|
||||
this.desactivateMainLoaderAnimation();
|
||||
|
||||
this.cowebsiteLoaderDom.style.display = "block";
|
||||
this.loaderAnimationInterval.interval = setInterval(() => {
|
||||
if (!this.loaderAnimationInterval.trails) {
|
||||
this.loaderAnimationInterval.trails = [0, 1, 2];
|
||||
@ -361,7 +366,6 @@ class CoWebsiteManager {
|
||||
|
||||
for (let trail = 1; trail < this.loaderAnimationInterval.trails.length + 1; trail++) {
|
||||
for (let state = 0; state < 4; state++) {
|
||||
// const newState = this.loaderAnimationInterval.frames + trail -1;
|
||||
const stateDom = this.cowebsiteLoaderDom.querySelector(
|
||||
`#trail-${trail}-state-${state}`
|
||||
) as SVGPolygonElement;
|
||||
@ -382,6 +386,17 @@ class CoWebsiteManager {
|
||||
trail === 3 ? 0 : trail + 1
|
||||
);
|
||||
}, 200);
|
||||
}
|
||||
|
||||
private desactivateMainLoaderAnimation() {
|
||||
if (this.loaderAnimationInterval.interval) {
|
||||
this.cowebsiteLoaderDom.style.display = "none";
|
||||
clearInterval(this.loaderAnimationInterval.interval);
|
||||
}
|
||||
}
|
||||
|
||||
private loadMain(openingWidth?: number): void {
|
||||
this.activateMainLoaderAnimation();
|
||||
|
||||
if (!this.verticalMode && openingWidth) {
|
||||
let newWidth = 50;
|
||||
@ -623,6 +638,8 @@ class CoWebsiteManager {
|
||||
setTimeout(() => {
|
||||
this.fire();
|
||||
}, animationTime);
|
||||
|
||||
this.desactivateMainLoaderAnimation();
|
||||
} else if (
|
||||
!highlightedEmbed &&
|
||||
this.getCoWebsites().find((searchCoWebsite) => searchCoWebsite.getId() === coWebsite.getId())
|
||||
|
@ -14,6 +14,8 @@ const warning: NonNullable<Translation["warning"]> = {
|
||||
},
|
||||
importantMessage: "Wichtige Nachricht",
|
||||
connectionLost: "Verbindungen unterbrochen. Wiederverbinden...",
|
||||
connectionLostTitle: "Verbindungen unterbrochen",
|
||||
connectionLostSubtitle: "Wiederverbinden",
|
||||
};
|
||||
|
||||
export default warning;
|
||||
|
@ -13,6 +13,8 @@ const warning: BaseTranslation = {
|
||||
},
|
||||
importantMessage: "Important message",
|
||||
connectionLost: "Connection lost. Reconnecting...",
|
||||
connectionLostTitle: "Connection lost",
|
||||
connectionLostSubtitle: "Reconnecting",
|
||||
};
|
||||
|
||||
export default warning;
|
||||
|
@ -71,6 +71,7 @@
|
||||
|
||||
&-loader {
|
||||
width: 20%;
|
||||
display: none;
|
||||
|
||||
#smoke {
|
||||
@for $i from 1 through 3 {
|
||||
|
@ -1,6 +1,5 @@
|
||||
{
|
||||
// "include": ["src/**/*"],
|
||||
|
||||
// "include": ["src/**/*"],
|
||||
"extends": "@tsconfig/svelte/tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./dist/",
|
||||
@ -14,23 +13,17 @@
|
||||
"jsx": "react",
|
||||
"allowJs": true,
|
||||
"esModuleInterop": true,
|
||||
|
||||
"importsNotUsedAsValues": "remove",
|
||||
|
||||
"strict": true, /* Enable all strict type-checking options. */
|
||||
"noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
|
||||
"strictNullChecks": true, /* Enable strict null checks. */
|
||||
"strictFunctionTypes": true, /* Enable strict checking of function types. */
|
||||
"strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
|
||||
"strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
|
||||
"noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
|
||||
"alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
|
||||
|
||||
"noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
|
||||
"noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
|
||||
"paths": {
|
||||
"@wa-preload-app": ["../desktop/electron/src/preload-app/types.ts"],
|
||||
}
|
||||
"strict": true, /* Enable all strict type-checking options. */
|
||||
"noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
|
||||
"strictNullChecks": true, /* Enable strict null checks. */
|
||||
"strictFunctionTypes": true, /* Enable strict checking of function types. */
|
||||
"strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
|
||||
"strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
|
||||
"noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
|
||||
"alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
|
||||
"noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
|
||||
"noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
@ -38,4 +31,4 @@
|
||||
"public/iframe_api.js",
|
||||
"packages/iframe-api-typings"
|
||||
]
|
||||
}
|
||||
}
|
@ -2244,10 +2244,10 @@ prelude-ls@^1.2.1:
|
||||
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396"
|
||||
integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==
|
||||
|
||||
prettier-plugin-svelte@^2.5.0:
|
||||
version "2.5.0"
|
||||
resolved "https://registry.yarnpkg.com/prettier-plugin-svelte/-/prettier-plugin-svelte-2.5.0.tgz#7922534729f7febe59b4c56c3f5360539f0d8ab1"
|
||||
integrity sha512-+iHY2uGChOngrgKielJUnqo74gIL/EO5oeWm8MftFWjEi213lq9QYTOwm1pv4lI1nA61tdgf80CF2i5zMcu1kw==
|
||||
prettier-plugin-svelte@^2.7.0:
|
||||
version "2.7.0"
|
||||
resolved "https://registry.yarnpkg.com/prettier-plugin-svelte/-/prettier-plugin-svelte-2.7.0.tgz#ecfa4fe824238a4466a3497df1a96d15cf43cabb"
|
||||
integrity sha512-fQhhZICprZot2IqEyoiUYLTRdumULGRvw0o4dzl5jt0jfzVWdGqeYW27QTWAeXhoupEZJULmNoH3ueJwUWFLIA==
|
||||
|
||||
prettier@^2.0.2:
|
||||
version "2.5.1"
|
||||
|
5
maps/tests/UIWebsite/index.html
Normal file
5
maps/tests/UIWebsite/index.html
Normal file
@ -0,0 +1,5 @@
|
||||
<html>
|
||||
<body style="background-color: white;">
|
||||
This is test page
|
||||
</body>
|
||||
</html>
|
48
maps/tests/UIWebsite/script.js
Normal file
48
maps/tests/UIWebsite/script.js
Normal file
@ -0,0 +1,48 @@
|
||||
WA.onInit().then(() => {
|
||||
initListeners();
|
||||
});
|
||||
|
||||
function initListeners() {
|
||||
let first_website = undefined;
|
||||
let second_website = undefined;
|
||||
|
||||
WA.room.onEnterLayer('first_website').subscribe(async () => {
|
||||
first_website = await WA.ui.website.open({
|
||||
url: "http://maps.workadventure.localhost/tests/UIWebsite/index.html",
|
||||
position: {
|
||||
vertical: "middle",
|
||||
horizontal: "middle",
|
||||
},
|
||||
size: {
|
||||
height: "50vh",
|
||||
width: "50vw",
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
WA.room.onLeaveLayer('first_website').subscribe(() => {
|
||||
if (first_website) {
|
||||
first_website.close();
|
||||
}
|
||||
});
|
||||
|
||||
WA.room.onEnterLayer('second_website').subscribe(async () => {
|
||||
second_website = await WA.ui.website.open({
|
||||
url: "https://www.wikipedia.org/",
|
||||
position: {
|
||||
vertical: "top",
|
||||
horizontal: "right",
|
||||
},
|
||||
size: {
|
||||
height: "20vh",
|
||||
width: "50vw",
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
WA.room.onLeaveLayer('second_website').subscribe(() => {
|
||||
if (second_website) {
|
||||
second_website.close();
|
||||
}
|
||||
});
|
||||
}
|
679
maps/tests/UIWebsite/uiwebsite.json
Normal file
679
maps/tests/UIWebsite/uiwebsite.json
Normal file
@ -0,0 +1,679 @@
|
||||
{ "compressionlevel":-1,
|
||||
"height":10,
|
||||
"infinite":false,
|
||||
"layers":[
|
||||
{
|
||||
"data":[1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
|
||||
"height":10,
|
||||
"id":1,
|
||||
"name":"floor",
|
||||
"opacity":1,
|
||||
"type":"tilelayer",
|
||||
"visible":true,
|
||||
"width":10,
|
||||
"x":0,
|
||||
"y":0
|
||||
},
|
||||
{
|
||||
"data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
12, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
"height":10,
|
||||
"id":2,
|
||||
"name":"start",
|
||||
"opacity":1,
|
||||
"type":"tilelayer",
|
||||
"visible":true,
|
||||
"width":10,
|
||||
"x":0,
|
||||
"y":0
|
||||
},
|
||||
{
|
||||
"data":[0, 0, 0, 0, 0, 23, 23, 23, 23, 23,
|
||||
0, 0, 0, 0, 0, 23, 23, 23, 23, 23,
|
||||
0, 0, 0, 0, 0, 23, 23, 23, 23, 23,
|
||||
0, 0, 0, 0, 0, 23, 23, 23, 23, 23,
|
||||
0, 0, 0, 0, 0, 23, 23, 23, 23, 23,
|
||||
0, 0, 0, 0, 0, 23, 23, 23, 23, 23,
|
||||
0, 0, 0, 0, 0, 23, 23, 23, 23, 23,
|
||||
0, 0, 0, 0, 0, 23, 23, 23, 23, 23,
|
||||
0, 0, 0, 0, 0, 23, 23, 23, 23, 23,
|
||||
0, 0, 0, 0, 0, 23, 23, 23, 23, 23],
|
||||
"height":10,
|
||||
"id":5,
|
||||
"name":"first_website",
|
||||
"opacity":1,
|
||||
"type":"tilelayer",
|
||||
"visible":true,
|
||||
"width":10,
|
||||
"x":0,
|
||||
"y":0
|
||||
},
|
||||
{
|
||||
"data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 12, 12, 12, 12, 12,
|
||||
0, 0, 0, 0, 0, 12, 12, 12, 12, 12,
|
||||
0, 0, 0, 0, 0, 12, 12, 12, 12, 12,
|
||||
0, 0, 0, 0, 0, 12, 12, 12, 12, 12,
|
||||
0, 0, 0, 0, 0, 12, 12, 12, 12, 12],
|
||||
"height":10,
|
||||
"id":7,
|
||||
"name":"second_website",
|
||||
"opacity":1,
|
||||
"type":"tilelayer",
|
||||
"visible":true,
|
||||
"width":10,
|
||||
"x":0,
|
||||
"y":0
|
||||
},
|
||||
{
|
||||
"draworder":"topdown",
|
||||
"id":3,
|
||||
"name":"floorLayer",
|
||||
"objects":[
|
||||
{
|
||||
"height":116.924156284309,
|
||||
"id":1,
|
||||
"name":"Tests",
|
||||
"rotation":0,
|
||||
"text":
|
||||
{
|
||||
"fontfamily":"Sans Serif",
|
||||
"pixelsize":8,
|
||||
"text":"Test 1:\nMove on the white carpet to display a UIWebsite.\n\nTest 2:\nMove on the blue carpet to display an other UIWebsite above the first.",
|
||||
"wrap":true
|
||||
},
|
||||
"type":"",
|
||||
"visible":true,
|
||||
"width":158.381128664136,
|
||||
"x":1.64026713939023,
|
||||
"y":201.037039933902
|
||||
}],
|
||||
"opacity":1,
|
||||
"type":"objectgroup",
|
||||
"visible":true,
|
||||
"x":0,
|
||||
"y":0
|
||||
},
|
||||
{
|
||||
"data":[0, 0, 0, 0, 0, 0, 0, 0, 82, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 8, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 19, 27, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 30, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
"height":10,
|
||||
"id":8,
|
||||
"name":"objects",
|
||||
"opacity":1,
|
||||
"type":"tilelayer",
|
||||
"visible":true,
|
||||
"width":10,
|
||||
"x":0,
|
||||
"y":0
|
||||
}],
|
||||
"nextlayerid":9,
|
||||
"nextobjectid":3,
|
||||
"orientation":"orthogonal",
|
||||
"properties":[
|
||||
{
|
||||
"name":"script",
|
||||
"type":"string",
|
||||
"value":"script.js"
|
||||
}],
|
||||
"renderorder":"right-down",
|
||||
"tiledversion":"1.8.4",
|
||||
"tileheight":32,
|
||||
"tilesets":[
|
||||
{
|
||||
"columns":11,
|
||||
"firstgid":1,
|
||||
"image":"..\/tileset1.png",
|
||||
"imageheight":352,
|
||||
"imagewidth":352,
|
||||
"margin":0,
|
||||
"name":"tileset1",
|
||||
"spacing":0,
|
||||
"tilecount":121,
|
||||
"tileheight":32,
|
||||
"tiles":[
|
||||
{
|
||||
"id":1,
|
||||
"properties":[
|
||||
{
|
||||
"name":"collides",
|
||||
"type":"bool",
|
||||
"value":true
|
||||
}]
|
||||
},
|
||||
{
|
||||
"id":2,
|
||||
"properties":[
|
||||
{
|
||||
"name":"collides",
|
||||
"type":"bool",
|
||||
"value":true
|
||||
}]
|
||||
},
|
||||
{
|
||||
"id":3,
|
||||
"properties":[
|
||||
{
|
||||
"name":"collides",
|
||||
"type":"bool",
|
||||
"value":true
|
||||
}]
|
||||
},
|
||||
{
|
||||
"id":4,
|
||||
"properties":[
|
||||
{
|
||||
"name":"collides",
|
||||
"type":"bool",
|
||||
"value":true
|
||||
}]
|
||||
},
|
||||
{
|
||||
"id":5,
|
||||
"properties":[
|
||||
{
|
||||
"name":"collides",
|
||||
"type":"bool",
|
||||
"value":true
|
||||
}]
|
||||
},
|
||||
{
|
||||
"id":6,
|
||||
"properties":[
|
||||
{
|
||||
"name":"collides",
|
||||
"type":"bool",
|
||||
"value":true
|
||||
}]
|
||||
},
|
||||
{
|
||||
"id":7,
|
||||
"properties":[
|
||||
{
|
||||
"name":"collides",
|
||||
"type":"bool",
|
||||
"value":true
|
||||
}]
|
||||
},
|
||||
{
|
||||
"id":8,
|
||||
"properties":[
|
||||
{
|
||||
"name":"collides",
|
||||
"type":"bool",
|
||||
"value":true
|
||||
}]
|
||||
},
|
||||
{
|
||||
"id":9,
|
||||
"properties":[
|
||||
{
|
||||
"name":"collides",
|
||||
"type":"bool",
|
||||
"value":true
|
||||
}]
|
||||
},
|
||||
{
|
||||
"id":10,
|
||||
"properties":[
|
||||
{
|
||||
"name":"collides",
|
||||
"type":"bool",
|
||||
"value":true
|
||||
}]
|
||||
},
|
||||
|
||||
{
|
||||
"id":12,
|
||||
"properties":[
|
||||
{
|
||||
"name":"collides",
|
||||
"type":"bool",
|
||||
"value":true
|
||||
}]
|
||||
},
|
||||
{
|
||||
"id":16,
|
||||
"properties":[
|
||||
{
|
||||
"name":"collides",
|
||||
"type":"bool",
|
||||
"value":true
|
||||
}]
|
||||
},
|
||||
{
|
||||
"id":17,
|
||||
"properties":[
|
||||
{
|
||||
"name":"collides",
|
||||
"type":"bool",
|
||||
"value":true
|
||||
}]
|
||||
},
|
||||
{
|
||||
"id":18,
|
||||
"properties":[
|
||||
{
|
||||
"name":"collides",
|
||||
"type":"bool",
|
||||
"value":true
|
||||
}]
|
||||
},
|
||||
{
|
||||
"id":19,
|
||||
"properties":[
|
||||
{
|
||||
"name":"collides",
|
||||
"type":"bool",
|
||||
"value":true
|
||||
}]
|
||||
},
|
||||
{
|
||||
"id":20,
|
||||
"properties":[
|
||||
{
|
||||
"name":"collides",
|
||||
"type":"bool",
|
||||
"value":true
|
||||
}]
|
||||
},
|
||||
{
|
||||
"id":21,
|
||||
"properties":[
|
||||
{
|
||||
"name":"collides",
|
||||
"type":"bool",
|
||||
"value":true
|
||||
}]
|
||||
},
|
||||
{
|
||||
"id":23,
|
||||
"properties":[
|
||||
{
|
||||
"name":"collides",
|
||||
"type":"bool",
|
||||
"value":true
|
||||
}]
|
||||
},
|
||||
{
|
||||
"id":24,
|
||||
"properties":[
|
||||
{
|
||||
"name":"collides",
|
||||
"type":"bool",
|
||||
"value":true
|
||||
}]
|
||||
},
|
||||
{
|
||||
"id":25,
|
||||
"properties":[
|
||||
{
|
||||
"name":"collides",
|
||||
"type":"bool",
|
||||
"value":true
|
||||
}]
|
||||
},
|
||||
|
||||
{
|
||||
"id":26,
|
||||
"properties":[
|
||||
{
|
||||
"name":"collides",
|
||||
"type":"bool",
|
||||
"value":true
|
||||
}]
|
||||
},
|
||||
{
|
||||
"id":27,
|
||||
"properties":[
|
||||
{
|
||||
"name":"collides",
|
||||
"type":"bool",
|
||||
"value":true
|
||||
}]
|
||||
},
|
||||
{
|
||||
"id":28,
|
||||
"properties":[
|
||||
{
|
||||
"name":"collides",
|
||||
"type":"bool",
|
||||
"value":true
|
||||
}]
|
||||
},
|
||||
{
|
||||
"id":29,
|
||||
"properties":[
|
||||
{
|
||||
"name":"collides",
|
||||
"type":"bool",
|
||||
"value":true
|
||||
}]
|
||||
},
|
||||
{
|
||||
"id":30,
|
||||
"properties":[
|
||||
{
|
||||
"name":"collides",
|
||||
"type":"bool",
|
||||
"value":true
|
||||
}]
|
||||
},
|
||||
{
|
||||
"id":31,
|
||||
"properties":[
|
||||
{
|
||||
"name":"collides",
|
||||
"type":"bool",
|
||||
"value":true
|
||||
}]
|
||||
},
|
||||
{
|
||||
"id":32,
|
||||
"properties":[
|
||||
{
|
||||
"name":"collides",
|
||||
"type":"bool",
|
||||
"value":true
|
||||
}]
|
||||
},
|
||||
{
|
||||
"id":34,
|
||||
"properties":[
|
||||
{
|
||||
"name":"collides",
|
||||
"type":"bool",
|
||||
"value":true
|
||||
}]
|
||||
},
|
||||
{
|
||||
"id":35,
|
||||
"properties":[
|
||||
{
|
||||
"name":"collides",
|
||||
"type":"bool",
|
||||
"value":true
|
||||
}]
|
||||
},
|
||||
{
|
||||
"id":42,
|
||||
"properties":[
|
||||
{
|
||||
"name":"collides",
|
||||
"type":"bool",
|
||||
"value":true
|
||||
}]
|
||||
},
|
||||
|
||||
{
|
||||
"id":43,
|
||||
"properties":[
|
||||
{
|
||||
"name":"collides",
|
||||
"type":"bool",
|
||||
"value":true
|
||||
}]
|
||||
},
|
||||
{
|
||||
"id":45,
|
||||
"properties":[
|
||||
{
|
||||
"name":"collides",
|
||||
"type":"bool",
|
||||
"value":true
|
||||
}]
|
||||
},
|
||||
{
|
||||
"id":46,
|
||||
"properties":[
|
||||
{
|
||||
"name":"collides",
|
||||
"type":"bool",
|
||||
"value":true
|
||||
}]
|
||||
},
|
||||
{
|
||||
"id":59,
|
||||
"properties":[
|
||||
{
|
||||
"name":"collides",
|
||||
"type":"bool",
|
||||
"value":true
|
||||
}]
|
||||
},
|
||||
{
|
||||
"id":60,
|
||||
"properties":[
|
||||
{
|
||||
"name":"collides",
|
||||
"type":"bool",
|
||||
"value":true
|
||||
}]
|
||||
},
|
||||
{
|
||||
"id":70,
|
||||
"properties":[
|
||||
{
|
||||
"name":"collides",
|
||||
"type":"bool",
|
||||
"value":true
|
||||
}]
|
||||
},
|
||||
{
|
||||
"id":71,
|
||||
"properties":[
|
||||
{
|
||||
"name":"collides",
|
||||
"type":"bool",
|
||||
"value":true
|
||||
}]
|
||||
},
|
||||
{
|
||||
"id":80,
|
||||
"properties":[
|
||||
{
|
||||
"name":"collides",
|
||||
"type":"bool",
|
||||
"value":true
|
||||
}]
|
||||
},
|
||||
{
|
||||
"id":81,
|
||||
"properties":[
|
||||
{
|
||||
"name":"collides",
|
||||
"type":"bool",
|
||||
"value":true
|
||||
}]
|
||||
},
|
||||
{
|
||||
"id":89,
|
||||
"properties":[
|
||||
{
|
||||
"name":"collides",
|
||||
"type":"bool",
|
||||
"value":true
|
||||
}]
|
||||
},
|
||||
|
||||
{
|
||||
"id":91,
|
||||
"properties":[
|
||||
{
|
||||
"name":"collides",
|
||||
"type":"bool",
|
||||
"value":true
|
||||
}]
|
||||
},
|
||||
{
|
||||
"id":93,
|
||||
"properties":[
|
||||
{
|
||||
"name":"collides",
|
||||
"type":"bool",
|
||||
"value":true
|
||||
}]
|
||||
},
|
||||
{
|
||||
"id":94,
|
||||
"properties":[
|
||||
{
|
||||
"name":"collides",
|
||||
"type":"bool",
|
||||
"value":true
|
||||
}]
|
||||
},
|
||||
{
|
||||
"id":95,
|
||||
"properties":[
|
||||
{
|
||||
"name":"collides",
|
||||
"type":"bool",
|
||||
"value":true
|
||||
}]
|
||||
},
|
||||
{
|
||||
"id":96,
|
||||
"properties":[
|
||||
{
|
||||
"name":"collides",
|
||||
"type":"bool",
|
||||
"value":true
|
||||
}]
|
||||
},
|
||||
{
|
||||
"id":97,
|
||||
"properties":[
|
||||
{
|
||||
"name":"collides",
|
||||
"type":"bool",
|
||||
"value":true
|
||||
}]
|
||||
},
|
||||
{
|
||||
"id":100,
|
||||
"properties":[
|
||||
{
|
||||
"name":"collides",
|
||||
"type":"bool",
|
||||
"value":true
|
||||
}]
|
||||
},
|
||||
{
|
||||
"id":102,
|
||||
"properties":[
|
||||
{
|
||||
"name":"collides",
|
||||
"type":"bool",
|
||||
"value":true
|
||||
}]
|
||||
},
|
||||
{
|
||||
"id":103,
|
||||
"properties":[
|
||||
{
|
||||
"name":"collides",
|
||||
"type":"bool",
|
||||
"value":true
|
||||
}]
|
||||
},
|
||||
{
|
||||
"id":104,
|
||||
"properties":[
|
||||
{
|
||||
"name":"collides",
|
||||
"type":"bool",
|
||||
"value":true
|
||||
}]
|
||||
},
|
||||
|
||||
{
|
||||
"id":105,
|
||||
"properties":[
|
||||
{
|
||||
"name":"collides",
|
||||
"type":"bool",
|
||||
"value":true
|
||||
}]
|
||||
},
|
||||
{
|
||||
"id":106,
|
||||
"properties":[
|
||||
{
|
||||
"name":"collides",
|
||||
"type":"bool",
|
||||
"value":true
|
||||
}]
|
||||
},
|
||||
{
|
||||
"id":107,
|
||||
"properties":[
|
||||
{
|
||||
"name":"collides",
|
||||
"type":"bool",
|
||||
"value":true
|
||||
}]
|
||||
},
|
||||
{
|
||||
"id":108,
|
||||
"properties":[
|
||||
{
|
||||
"name":"collides",
|
||||
"type":"bool",
|
||||
"value":true
|
||||
}]
|
||||
},
|
||||
{
|
||||
"id":114,
|
||||
"properties":[
|
||||
{
|
||||
"name":"collides",
|
||||
"type":"bool",
|
||||
"value":true
|
||||
}]
|
||||
},
|
||||
{
|
||||
"id":115,
|
||||
"properties":[
|
||||
{
|
||||
"name":"collides",
|
||||
"type":"bool",
|
||||
"value":true
|
||||
}]
|
||||
}],
|
||||
"tilewidth":32
|
||||
}],
|
||||
"tilewidth":32,
|
||||
"type":"map",
|
||||
"version":"1.8",
|
||||
"width":10
|
||||
}
|
@ -363,6 +363,14 @@
|
||||
<a href="#" class="testLink" data-testmap="Modules/without_modules.json" target="_blank">Testing scripts with modules mode disabled</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<input type="radio" name="test-uiwebsite"> Success <input type="radio" name="test-uiwebsite"> Failure <input type="radio" name="test-uiwebsite" checked> Pending
|
||||
</td>
|
||||
<td>
|
||||
<a href="#" class="testLink" data-testmap="UIWebsite/uiwebsite.json" target="_blank">Testing UIWebsites</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<h2>CoWebsite</h2>
|
||||
<table class="table">
|
||||
|
@ -49,6 +49,10 @@ export const isMapDetailsData = z.object({
|
||||
description: "The URL of the image to be used on the LoginScene",
|
||||
example: "https://example.com/logo_login.png",
|
||||
}),
|
||||
showPoweredBy: extendApi(z.boolean(), {
|
||||
description: "The URL of the image to be used on the name scene",
|
||||
example: "https://example.com/logo_login.png",
|
||||
}),
|
||||
});
|
||||
|
||||
export type MapDetailsData = z.infer<typeof isMapDetailsData>;
|
||||
|
@ -49,6 +49,7 @@ class LocalAdmin implements AdminInterface {
|
||||
iframeAuthentication: null,
|
||||
loadingLogo: null,
|
||||
loginSceneLogo: null,
|
||||
showPoweredBy: true,
|
||||
});
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user