Merge remote-tracking branch 'origin/develop' into swaggerPusher
This commit is contained in:
commit
231facbae2
16
.github/workflows/build-and-release-desktop.yml
vendored
16
.github/workflows/build-and-release-desktop.yml
vendored
@ -60,6 +60,10 @@ jobs:
|
|||||||
run: yarn build
|
run: yarn build
|
||||||
working-directory: "desktop/local-app"
|
working-directory: "desktop/local-app"
|
||||||
|
|
||||||
|
- name: "Set desktop app version"
|
||||||
|
run: node helpers/set-version.js
|
||||||
|
working-directory: "desktop/electron"
|
||||||
|
|
||||||
- name: "Install dependencies"
|
- name: "Install dependencies"
|
||||||
run: yarn install --froze-lockfile
|
run: yarn install --froze-lockfile
|
||||||
working-directory: "desktop/electron"
|
working-directory: "desktop/electron"
|
||||||
@ -68,15 +72,19 @@ jobs:
|
|||||||
run: yarn build
|
run: yarn build
|
||||||
working-directory: "desktop/electron"
|
working-directory: "desktop/electron"
|
||||||
|
|
||||||
- name: "Build app"
|
- name: "Install electron tools"
|
||||||
run: yarn bundle --publish never
|
run: yarn electron-builder install-app-deps
|
||||||
|
working-directory: "desktop/electron"
|
||||||
|
|
||||||
|
- name: "Build app for testing"
|
||||||
|
run: yarn electron-builder --publish never
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
working-directory: "desktop/electron"
|
working-directory: "desktop/electron"
|
||||||
if: ${{ github.event_name != 'release' }}
|
if: ${{ github.event_name != 'release' }}
|
||||||
|
|
||||||
- name: "Build & publish App"
|
- name: "Build & release app"
|
||||||
run: yarn release
|
run: yarn electron-builder --publish always
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
working-directory: "desktop/electron"
|
working-directory: "desktop/electron"
|
||||||
|
@ -27,7 +27,7 @@
|
|||||||
"no-unused-vars": "off",
|
"no-unused-vars": "off",
|
||||||
"@typescript-eslint/no-explicit-any": "error",
|
"@typescript-eslint/no-explicit-any": "error",
|
||||||
"@typescript-eslint/no-unused-vars": [
|
"@typescript-eslint/no-unused-vars": [
|
||||||
"error"
|
"error", { "args": "none", "caughtErrors": "all", "varsIgnorePattern": "_exhaustiveCheck" }
|
||||||
],
|
],
|
||||||
"no-throw-literal": "error"
|
"no-throw-literal": "error"
|
||||||
}
|
}
|
||||||
|
@ -144,9 +144,8 @@ export class GameRoom {
|
|||||||
joinRoomMessage.getUseruuid(),
|
joinRoomMessage.getUseruuid(),
|
||||||
joinRoomMessage.getIpaddress(),
|
joinRoomMessage.getIpaddress(),
|
||||||
position,
|
position,
|
||||||
false,
|
|
||||||
this.positionNotifier,
|
this.positionNotifier,
|
||||||
joinRoomMessage.getAway(),
|
joinRoomMessage.getStatus(),
|
||||||
socket,
|
socket,
|
||||||
joinRoomMessage.getTagList(),
|
joinRoomMessage.getTagList(),
|
||||||
joinRoomMessage.getVisitcardurl(),
|
joinRoomMessage.getVisitcardurl(),
|
||||||
@ -208,6 +207,9 @@ export class GameRoom {
|
|||||||
|
|
||||||
updatePlayerDetails(user: User, playerDetailsMessage: SetPlayerDetailsMessage) {
|
updatePlayerDetails(user: User, playerDetailsMessage: SetPlayerDetailsMessage) {
|
||||||
user.updateDetails(playerDetailsMessage);
|
user.updateDetails(playerDetailsMessage);
|
||||||
|
if (user.group !== undefined && user.silent) {
|
||||||
|
this.leaveGroup(user);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private updateUserGroup(user: User): void {
|
private updateUserGroup(user: User): void {
|
||||||
@ -345,21 +347,6 @@ export class GameRoom {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
setSilent(user: User, silent: boolean) {
|
|
||||||
if (user.silent === silent) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
user.silent = silent;
|
|
||||||
if (silent && user.group !== undefined) {
|
|
||||||
this.leaveGroup(user);
|
|
||||||
}
|
|
||||||
if (!silent) {
|
|
||||||
// If we are back to life, let's trigger a position update to see if we can join some group.
|
|
||||||
this.updatePosition(user, user.getPosition());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Makes a user leave a group and closes and destroy the group if the group contains only one remaining person.
|
* Makes a user leave a group and closes and destroy the group if the group contains only one remaining person.
|
||||||
*
|
*
|
||||||
@ -572,11 +559,7 @@ export class GameRoom {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
mapUrl,
|
mapUrl,
|
||||||
policy_type: 1,
|
|
||||||
tags: [],
|
|
||||||
authenticationMandatory: null,
|
authenticationMandatory: null,
|
||||||
roomSlug: null,
|
|
||||||
contactPage: null,
|
|
||||||
group: null,
|
group: null,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ import { Movable } from "../Model/Movable";
|
|||||||
import { PositionNotifier } from "../Model/PositionNotifier";
|
import { PositionNotifier } from "../Model/PositionNotifier";
|
||||||
import { ServerDuplexStream } from "grpc";
|
import { ServerDuplexStream } from "grpc";
|
||||||
import {
|
import {
|
||||||
|
AvailabilityStatus,
|
||||||
BatchMessage,
|
BatchMessage,
|
||||||
CompanionMessage,
|
CompanionMessage,
|
||||||
FollowAbortMessage,
|
FollowAbortMessage,
|
||||||
@ -30,9 +31,8 @@ export class User implements Movable {
|
|||||||
public readonly uuid: string,
|
public readonly uuid: string,
|
||||||
public readonly IPAddress: string,
|
public readonly IPAddress: string,
|
||||||
private position: PointInterface,
|
private position: PointInterface,
|
||||||
public silent: boolean,
|
|
||||||
private positionNotifier: PositionNotifier,
|
private positionNotifier: PositionNotifier,
|
||||||
private away: boolean,
|
private status: AvailabilityStatus,
|
||||||
public readonly socket: UserSocket,
|
public readonly socket: UserSocket,
|
||||||
public readonly tags: string[],
|
public readonly tags: string[],
|
||||||
public readonly visitCardUrl: string | null,
|
public readonly visitCardUrl: string | null,
|
||||||
@ -90,8 +90,12 @@ export class User implements Movable {
|
|||||||
return this.outlineColor;
|
return this.outlineColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
public isAway(): boolean {
|
public getStatus(): AvailabilityStatus {
|
||||||
return this.away;
|
return this.status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get silent(): boolean {
|
||||||
|
return this.status === AvailabilityStatus.SILENT || this.status === AvailabilityStatus.JITSI;
|
||||||
}
|
}
|
||||||
|
|
||||||
get following(): User | undefined {
|
get following(): User | undefined {
|
||||||
@ -134,9 +138,11 @@ export class User implements Movable {
|
|||||||
}
|
}
|
||||||
this.voiceIndicatorShown = details.getShowvoiceindicator()?.getValue();
|
this.voiceIndicatorShown = details.getShowvoiceindicator()?.getValue();
|
||||||
|
|
||||||
const away = details.getAway();
|
const status = details.getStatus();
|
||||||
if (away) {
|
let sendStatusUpdate = false;
|
||||||
this.away = away.getValue();
|
if (status && status !== this.status) {
|
||||||
|
this.status = status;
|
||||||
|
sendStatusUpdate = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const playerDetails = new SetPlayerDetailsMessage();
|
const playerDetails = new SetPlayerDetailsMessage();
|
||||||
@ -144,11 +150,14 @@ export class User implements Movable {
|
|||||||
if (this.outlineColor !== undefined) {
|
if (this.outlineColor !== undefined) {
|
||||||
playerDetails.setOutlinecolor(new UInt32Value().setValue(this.outlineColor));
|
playerDetails.setOutlinecolor(new UInt32Value().setValue(this.outlineColor));
|
||||||
}
|
}
|
||||||
|
if (details.getRemoveoutlinecolor()) {
|
||||||
|
playerDetails.setRemoveoutlinecolor(new BoolValue().setValue(true));
|
||||||
|
}
|
||||||
if (this.voiceIndicatorShown !== undefined) {
|
if (this.voiceIndicatorShown !== undefined) {
|
||||||
playerDetails.setShowvoiceindicator(new BoolValue().setValue(this.voiceIndicatorShown));
|
playerDetails.setShowvoiceindicator(new BoolValue().setValue(this.voiceIndicatorShown));
|
||||||
}
|
}
|
||||||
if (details.getAway() !== undefined) {
|
if (sendStatusUpdate) {
|
||||||
playerDetails.setAway(new BoolValue().setValue(this.away));
|
playerDetails.setStatus(details.getStatus());
|
||||||
}
|
}
|
||||||
|
|
||||||
this.positionNotifier.updatePlayerDetails(this, playerDetails);
|
this.positionNotifier.updatePlayerDetails(this, playerDetails);
|
||||||
|
@ -71,7 +71,6 @@ export class Zone {
|
|||||||
/**
|
/**
|
||||||
* Notify listeners of this zone that this user entered
|
* Notify listeners of this zone that this user entered
|
||||||
*/
|
*/
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
private notifyEnter(thing: Movable, oldZone: Zone | null, position: PositionInterface) {
|
private notifyEnter(thing: Movable, oldZone: Zone | null, position: PositionInterface) {
|
||||||
for (const listener of this.listeners) {
|
for (const listener of this.listeners) {
|
||||||
this.onEnters(thing, oldZone, listener);
|
this.onEnters(thing, oldZone, listener);
|
||||||
|
@ -22,7 +22,6 @@ import {
|
|||||||
SendUserMessage,
|
SendUserMessage,
|
||||||
ServerToAdminClientMessage,
|
ServerToAdminClientMessage,
|
||||||
SetPlayerDetailsMessage,
|
SetPlayerDetailsMessage,
|
||||||
SilentMessage,
|
|
||||||
UserMovesMessage,
|
UserMovesMessage,
|
||||||
VariableMessage,
|
VariableMessage,
|
||||||
WebRtcSignalToServerMessage,
|
WebRtcSignalToServerMessage,
|
||||||
@ -80,8 +79,6 @@ const roomManager: IRoomManagerServer = {
|
|||||||
user,
|
user,
|
||||||
message.getUsermovesmessage() as UserMovesMessage
|
message.getUsermovesmessage() as UserMovesMessage
|
||||||
);
|
);
|
||||||
} else if (message.hasSilentmessage()) {
|
|
||||||
socketManager.handleSilentMessage(room, user, message.getSilentmessage() as SilentMessage);
|
|
||||||
} else if (message.hasItemeventmessage()) {
|
} else if (message.hasItemeventmessage()) {
|
||||||
socketManager.handleItemEvent(
|
socketManager.handleItemEvent(
|
||||||
room,
|
room,
|
||||||
|
@ -4,12 +4,10 @@ import { VariablesRepositoryInterface } from "./VariablesRepositoryInterface";
|
|||||||
* Mock class in charge of NOT saving/loading variables from the data store
|
* Mock class in charge of NOT saving/loading variables from the data store
|
||||||
*/
|
*/
|
||||||
export class VoidVariablesRepository implements VariablesRepositoryInterface {
|
export class VoidVariablesRepository implements VariablesRepositoryInterface {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
loadVariables(roomUrl: string): Promise<{ [key: string]: string }> {
|
loadVariables(roomUrl: string): Promise<{ [key: string]: string }> {
|
||||||
return Promise.resolve({});
|
return Promise.resolve({});
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
saveVariable(roomUrl: string, key: string, value: string): Promise<number> {
|
saveVariable(roomUrl: string, key: string, value: string): Promise<number> {
|
||||||
return Promise.resolve(0);
|
return Promise.resolve(0);
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,6 @@ import {
|
|||||||
PointMessage,
|
PointMessage,
|
||||||
RoomJoinedMessage,
|
RoomJoinedMessage,
|
||||||
ServerToClientMessage,
|
ServerToClientMessage,
|
||||||
SilentMessage,
|
|
||||||
SubMessage,
|
SubMessage,
|
||||||
UserMovedMessage,
|
UserMovedMessage,
|
||||||
UserMovesMessage,
|
UserMovesMessage,
|
||||||
@ -160,10 +159,6 @@ export class SocketManager {
|
|||||||
room.updatePlayerDetails(user, playerDetailsMessage);
|
room.updatePlayerDetails(user, playerDetailsMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSilentMessage(room: GameRoom, user: User, silentMessage: SilentMessage) {
|
|
||||||
room.setSilent(user, silentMessage.getSilent());
|
|
||||||
}
|
|
||||||
|
|
||||||
handleItemEvent(room: GameRoom, user: User, itemEventMessage: ItemEventMessage) {
|
handleItemEvent(room: GameRoom, user: User, itemEventMessage: ItemEventMessage) {
|
||||||
const itemEvent = ProtobufUtils.toItemEvent(itemEventMessage);
|
const itemEvent = ProtobufUtils.toItemEvent(itemEventMessage);
|
||||||
|
|
||||||
@ -328,7 +323,7 @@ export class SocketManager {
|
|||||||
userJoinedZoneMessage.setUserid(thing.id);
|
userJoinedZoneMessage.setUserid(thing.id);
|
||||||
userJoinedZoneMessage.setUseruuid(thing.uuid);
|
userJoinedZoneMessage.setUseruuid(thing.uuid);
|
||||||
userJoinedZoneMessage.setName(thing.name);
|
userJoinedZoneMessage.setName(thing.name);
|
||||||
userJoinedZoneMessage.setAway(thing.isAway());
|
userJoinedZoneMessage.setStatus(thing.getStatus());
|
||||||
userJoinedZoneMessage.setCharacterlayersList(ProtobufUtils.toCharacterLayerMessages(thing.characterLayers));
|
userJoinedZoneMessage.setCharacterlayersList(ProtobufUtils.toCharacterLayerMessages(thing.characterLayers));
|
||||||
userJoinedZoneMessage.setPosition(ProtobufUtils.toPositionMessage(thing.getPosition()));
|
userJoinedZoneMessage.setPosition(ProtobufUtils.toPositionMessage(thing.getPosition()));
|
||||||
userJoinedZoneMessage.setFromzone(this.toProtoZone(fromZone));
|
userJoinedZoneMessage.setFromzone(this.toProtoZone(fromZone));
|
||||||
@ -656,7 +651,7 @@ export class SocketManager {
|
|||||||
userJoinedMessage.setUserid(thing.id);
|
userJoinedMessage.setUserid(thing.id);
|
||||||
userJoinedMessage.setUseruuid(thing.uuid);
|
userJoinedMessage.setUseruuid(thing.uuid);
|
||||||
userJoinedMessage.setName(thing.name);
|
userJoinedMessage.setName(thing.name);
|
||||||
userJoinedMessage.setAway(thing.isAway());
|
userJoinedMessage.setStatus(thing.getStatus());
|
||||||
userJoinedMessage.setCharacterlayersList(ProtobufUtils.toCharacterLayerMessages(thing.characterLayers));
|
userJoinedMessage.setCharacterlayersList(ProtobufUtils.toCharacterLayerMessages(thing.characterLayers));
|
||||||
userJoinedMessage.setPosition(ProtobufUtils.toPositionMessage(thing.getPosition()));
|
userJoinedMessage.setPosition(ProtobufUtils.toPositionMessage(thing.getPosition()));
|
||||||
if (thing.visitCardUrl) {
|
if (thing.visitCardUrl) {
|
||||||
|
@ -6,6 +6,7 @@ import { Zone } from "../src/Model/Zone";
|
|||||||
import { Movable } from "../src/Model/Movable";
|
import { Movable } from "../src/Model/Movable";
|
||||||
import { PositionInterface } from "../src/Model/PositionInterface";
|
import { PositionInterface } from "../src/Model/PositionInterface";
|
||||||
import { ZoneSocket } from "../src/RoomManager";
|
import { ZoneSocket } from "../src/RoomManager";
|
||||||
|
import { AvailabilityStatus } from "../src/Messages/generated/messages_pb";
|
||||||
|
|
||||||
describe("PositionNotifier", () => {
|
describe("PositionNotifier", () => {
|
||||||
it("should receive notifications when player moves", () => {
|
it("should receive notifications when player moves", () => {
|
||||||
@ -40,9 +41,8 @@ describe("PositionNotifier", () => {
|
|||||||
moving: false,
|
moving: false,
|
||||||
direction: "down",
|
direction: "down",
|
||||||
},
|
},
|
||||||
false,
|
|
||||||
positionNotifier,
|
positionNotifier,
|
||||||
false,
|
AvailabilityStatus.ONLINE,
|
||||||
{} as UserSocket,
|
{} as UserSocket,
|
||||||
[],
|
[],
|
||||||
null,
|
null,
|
||||||
@ -60,9 +60,8 @@ describe("PositionNotifier", () => {
|
|||||||
moving: false,
|
moving: false,
|
||||||
direction: "down",
|
direction: "down",
|
||||||
},
|
},
|
||||||
false,
|
|
||||||
positionNotifier,
|
positionNotifier,
|
||||||
false,
|
AvailabilityStatus.ONLINE,
|
||||||
{} as UserSocket,
|
{} as UserSocket,
|
||||||
[],
|
[],
|
||||||
null,
|
null,
|
||||||
@ -150,9 +149,8 @@ describe("PositionNotifier", () => {
|
|||||||
moving: false,
|
moving: false,
|
||||||
direction: "down",
|
direction: "down",
|
||||||
},
|
},
|
||||||
false,
|
|
||||||
positionNotifier,
|
positionNotifier,
|
||||||
false,
|
AvailabilityStatus.ONLINE,
|
||||||
{} as UserSocket,
|
{} as UserSocket,
|
||||||
[],
|
[],
|
||||||
null,
|
null,
|
||||||
@ -170,9 +168,8 @@ describe("PositionNotifier", () => {
|
|||||||
moving: false,
|
moving: false,
|
||||||
direction: "down",
|
direction: "down",
|
||||||
},
|
},
|
||||||
false,
|
|
||||||
positionNotifier,
|
positionNotifier,
|
||||||
false,
|
AvailabilityStatus.ONLINE,
|
||||||
{} as UserSocket,
|
{} as UserSocket,
|
||||||
[],
|
[],
|
||||||
null,
|
null,
|
||||||
|
@ -30,7 +30,7 @@ ADMIN_API_URL=
|
|||||||
DATA_DIR=./wa
|
DATA_DIR=./wa
|
||||||
|
|
||||||
# The URL used by default, in the form: "/_/global/map/url.json"
|
# The URL used by default, in the form: "/_/global/map/url.json"
|
||||||
START_ROOM_URL=/_/global/maps.workadventu.re/Floor0/floor0.json
|
START_ROOM_URL=/_/global/thecodingmachine.github.io/workadventure-map-starter-kit/map.json
|
||||||
|
|
||||||
# If you want to have a contact page in your menu,
|
# If you want to have a contact page in your menu,
|
||||||
# you MUST set CONTACT_URL to the URL of the page that you want
|
# you MUST set CONTACT_URL to the URL of the page that you want
|
||||||
|
34
contrib/docker/README.md
Normal file
34
contrib/docker/README.md
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
# Deploying WorkAdventure in production
|
||||||
|
|
||||||
|
This directory contains a sample production deployment of WorkAdventure using docker-compose.
|
||||||
|
|
||||||
|
Every production environment is different and this docker-compose file will not
|
||||||
|
fit all use cases. But it is intended to be a good starting point for you
|
||||||
|
to build your own deployment.
|
||||||
|
|
||||||
|
In this docker-compose file, you will find:
|
||||||
|
|
||||||
|
- A reverse-proxy (Traefik) that dispatches requests to the WorkAdventure containers and handles HTTPS certificates using LetsEncrypt
|
||||||
|
- A front container (nginx) that servers static files (HTML/JS/CSS)
|
||||||
|
- A pusher container (NodeJS) that is the point of entry for users (you can start many if you want to increase performance)
|
||||||
|
- A back container (NodeJS) that shares your rooms information
|
||||||
|
- An icon container to fetch the favicon of sites imported in iframes
|
||||||
|
- A Redis server to store values from variables originating from the Scripting API
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
graph LR
|
||||||
|
A[Browser] --> B(Traefik)
|
||||||
|
subgraph docker-compose
|
||||||
|
B --> C(Front)
|
||||||
|
B --> D(Pusher)
|
||||||
|
B --> E(Icon)
|
||||||
|
D --> F(Back)
|
||||||
|
F --> G(Redis)
|
||||||
|
end
|
||||||
|
A .-> H(Map)
|
||||||
|
F .-> H
|
||||||
|
```
|
||||||
|
|
||||||
|
**Important**: the default docker-compose file does **not** contain a container dedicated to hosting maps. The documentation and
|
||||||
|
tutorials are relying on GitHub Pages to host the maps. If you want to self-host your maps, you will need to add a simple
|
||||||
|
HTTP server (nginx / Apache, ...) and properly configure the [CORS settings as explained in the documentation](../../docs/maps/hosting.md).
|
@ -95,6 +95,7 @@ services:
|
|||||||
- JITSI_ISS
|
- JITSI_ISS
|
||||||
- MAX_PER_GROUP
|
- MAX_PER_GROUP
|
||||||
- STORE_VARIABLES_FOR_LOCAL_MAPS
|
- STORE_VARIABLES_FOR_LOCAL_MAPS
|
||||||
|
- REDIS_HOST=redis
|
||||||
labels:
|
labels:
|
||||||
- "traefik.http.routers.back.rule=Host(`${BACK_HOST}`)"
|
- "traefik.http.routers.back.rule=Host(`${BACK_HOST}`)"
|
||||||
- "traefik.http.routers.back.entryPoints=web"
|
- "traefik.http.routers.back.entryPoints=web"
|
||||||
@ -117,3 +118,11 @@ services:
|
|||||||
- "traefik.http.routers.icon-ssl.service=icon"
|
- "traefik.http.routers.icon-ssl.service=icon"
|
||||||
- "traefik.http.routers.icon-ssl.tls=true"
|
- "traefik.http.routers.icon-ssl.tls=true"
|
||||||
- "traefik.http.routers.icon-ssl.tls.certresolver=myresolver"
|
- "traefik.http.routers.icon-ssl.tls.certresolver=myresolver"
|
||||||
|
|
||||||
|
redis:
|
||||||
|
image: redis:6
|
||||||
|
volumes:
|
||||||
|
- redisdata:/data
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
redisdata:
|
||||||
|
@ -28,5 +28,4 @@ publish:
|
|||||||
provider: github
|
provider: github
|
||||||
owner: thecodingmachine
|
owner: thecodingmachine
|
||||||
repo: workadventure
|
repo: workadventure
|
||||||
vPrefixedTagName: false
|
releaseType: release
|
||||||
releaseType: draft
|
|
||||||
|
18
desktop/electron/helpers/set-version.js
Normal file
18
desktop/electron/helpers/set-version.js
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
const path = require('path');
|
||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
|
let version = '0.0.0';
|
||||||
|
|
||||||
|
if (process.env.GITHUB_REF.startsWith('refs/tags/v')) {
|
||||||
|
version = process.env.GITHUB_REF.replace('refs/tags/v', '');
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Version:', version);
|
||||||
|
|
||||||
|
const packageJsonPath = path.resolve(__dirname, '..', 'package.json');
|
||||||
|
|
||||||
|
let data = fs.readFileSync(packageJsonPath, 'utf8');
|
||||||
|
|
||||||
|
data = data.replace('managedbyci', version);
|
||||||
|
|
||||||
|
fs.writeFileSync(packageJsonPath, data);
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "workadventure-desktop",
|
"name": "workadventure-desktop",
|
||||||
"version": "1.0.0",
|
"version": "managedbyci",
|
||||||
"description": "Desktop application for WorkAdventure",
|
"description": "Desktop application for WorkAdventure",
|
||||||
"author": "thecodingmachine",
|
"author": "thecodingmachine",
|
||||||
"main": "dist/main.js",
|
"main": "dist/main.js",
|
||||||
@ -11,7 +11,6 @@
|
|||||||
"dev": "yarn build --watch --onSuccess 'yarn electron dist/main.js'",
|
"dev": "yarn build --watch --onSuccess 'yarn electron dist/main.js'",
|
||||||
"dev:local-app": "cd ../local-app && yarn && yarn dev",
|
"dev:local-app": "cd ../local-app && yarn && yarn dev",
|
||||||
"bundle": "yarn build:local-app && yarn build && electron-builder install-app-deps && electron-builder",
|
"bundle": "yarn build:local-app && yarn build && electron-builder install-app-deps && electron-builder",
|
||||||
"release": "yarn bundle",
|
|
||||||
"typecheck": "tsc --noEmit",
|
"typecheck": "tsc --noEmit",
|
||||||
"test": "exit 0",
|
"test": "exit 0",
|
||||||
"lint": "yarn eslint src/ . --ext .ts",
|
"lint": "yarn eslint src/ . --ext .ts",
|
||||||
@ -32,13 +31,13 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/auto-launch": "^5.0.2",
|
"@types/auto-launch": "^5.0.2",
|
||||||
"@typescript-eslint/eslint-plugin": "^2.26.0",
|
"@typescript-eslint/eslint-plugin": "^5.18.0",
|
||||||
"@typescript-eslint/parser": "^2.26.0",
|
"@typescript-eslint/parser": "^5.18.0",
|
||||||
"electron": "^17.0.1",
|
"electron": "^18.0.3",
|
||||||
"electron-builder": "^22.14.13",
|
"electron-builder": "^22.14.13",
|
||||||
"eslint": "^6.8.0",
|
"eslint": "^8.12.0",
|
||||||
"prettier": "^2.5.1",
|
"prettier": "^2.6.2",
|
||||||
"tsup": "^5.11.13",
|
"tsup": "^5.12.4",
|
||||||
"typescript": "^3.8.3"
|
"typescript": "^4.6.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ import { setLogLevel } from "./log";
|
|||||||
import "./serve"; // prepare custom url scheme
|
import "./serve"; // prepare custom url scheme
|
||||||
import { loadShortcuts } from "./shortcuts";
|
import { loadShortcuts } from "./shortcuts";
|
||||||
|
|
||||||
function init() {
|
async function init() {
|
||||||
const appLock = app.requestSingleInstanceLock();
|
const appLock = app.requestSingleInstanceLock();
|
||||||
|
|
||||||
if (!appLock) {
|
if (!appLock) {
|
||||||
@ -21,7 +21,7 @@ function init() {
|
|||||||
|
|
||||||
app.on("second-instance", () => {
|
app.on("second-instance", () => {
|
||||||
// re-create window if closed
|
// re-create window if closed
|
||||||
createWindow();
|
void createWindow();
|
||||||
|
|
||||||
const mainWindow = getWindow();
|
const mainWindow = getWindow();
|
||||||
|
|
||||||
@ -36,15 +36,15 @@ function init() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// This method will be called when Electron has finished loading
|
// This method will be called when Electron has finished loading
|
||||||
app.whenReady().then(async () => {
|
await app.whenReady().then(async () => {
|
||||||
await settings.init();
|
await settings.init();
|
||||||
|
|
||||||
setLogLevel(settings.get("log_level") || "info");
|
setLogLevel(settings.get("log_level") || "info");
|
||||||
|
|
||||||
autoUpdater.init();
|
await autoUpdater.init();
|
||||||
|
|
||||||
// enable auto launch
|
// enable auto launch
|
||||||
updateAutoLaunch();
|
await updateAutoLaunch();
|
||||||
|
|
||||||
// load ipc handler
|
// load ipc handler
|
||||||
ipc();
|
ipc();
|
||||||
@ -72,7 +72,7 @@ function init() {
|
|||||||
// On macOS it's common to re-create a window in the app when the
|
// On macOS it's common to re-create a window in the app when the
|
||||||
// dock icon is clicked and there are no other windows open.
|
// dock icon is clicked and there are no other windows open.
|
||||||
if (BrowserWindow.getAllWindows().length === 0) {
|
if (BrowserWindow.getAllWindows().length === 0) {
|
||||||
createWindow();
|
void createWindow();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -38,11 +38,13 @@ export async function manualRequestUpdateCheck() {
|
|||||||
isManualRequestedUpdate = false;
|
isManualRequestedUpdate = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function init() {
|
async function init() {
|
||||||
autoUpdater.logger = log;
|
autoUpdater.logger = log;
|
||||||
|
|
||||||
autoUpdater.on("update-downloaded", ({ releaseNotes, releaseName }) => {
|
autoUpdater.on(
|
||||||
(async () => {
|
"update-downloaded",
|
||||||
|
({ releaseNotes, releaseName }: { releaseNotes: string; releaseName: string }) => {
|
||||||
|
void (async () => {
|
||||||
const dialogOpts = {
|
const dialogOpts = {
|
||||||
type: "question",
|
type: "question",
|
||||||
buttons: ["Install and Restart", "Install Later"],
|
buttons: ["Install and Restart", "Install Later"],
|
||||||
@ -63,7 +65,8 @@ function init() {
|
|||||||
app.quit();
|
app.quit();
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
if (process.platform === "linux" && !process.env.APPIMAGE) {
|
if (process.platform === "linux" && !process.env.APPIMAGE) {
|
||||||
autoUpdater.autoDownload = false;
|
autoUpdater.autoDownload = false;
|
||||||
@ -85,7 +88,7 @@ function init() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
checkForUpdates();
|
await checkForUpdates();
|
||||||
|
|
||||||
// run update check every hour again
|
// run update check every hour again
|
||||||
setInterval(() => checkForUpdates, 1000 * 60 * 1);
|
setInterval(() => checkForUpdates, 1000 * 60 * 1);
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { ipcMain, app } from "electron";
|
import { ipcMain, app, desktopCapturer } from "electron";
|
||||||
import electronIsDev from "electron-is-dev";
|
import electronIsDev from "electron-is-dev";
|
||||||
import { createAndShowNotification } from "./notification";
|
import { createAndShowNotification } from "./notification";
|
||||||
import { Server } from "./preload-local-app/types";
|
import { Server } from "./preload-local-app/types";
|
||||||
@ -30,10 +30,18 @@ export default () => {
|
|||||||
ipcMain.handle("get-version", () => (electronIsDev ? "dev" : app.getVersion()));
|
ipcMain.handle("get-version", () => (electronIsDev ? "dev" : app.getVersion()));
|
||||||
|
|
||||||
// app ipc
|
// app ipc
|
||||||
ipcMain.on("app:notify", (event, txt) => {
|
ipcMain.on("app:notify", (event, txt: string) => {
|
||||||
createAndShowNotification({ body: txt });
|
createAndShowNotification({ body: txt });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ipcMain.handle("app:getDesktopCapturerSources", async (event, options: Electron.SourcesOptions) => {
|
||||||
|
return (await desktopCapturer.getSources(options)).map((source) => ({
|
||||||
|
id: source.id,
|
||||||
|
name: source.name,
|
||||||
|
thumbnailURL: source.thumbnail.toDataURL(),
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
// local-app ipc
|
// local-app ipc
|
||||||
ipcMain.handle("local-app:showLocalApp", () => {
|
ipcMain.handle("local-app:showLocalApp", () => {
|
||||||
hideAppView();
|
hideAppView();
|
||||||
@ -43,7 +51,7 @@ export default () => {
|
|||||||
return settings.get("servers");
|
return settings.get("servers");
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle("local-app:selectServer", (event, serverId: string) => {
|
ipcMain.handle("local-app:selectServer", async (event, serverId: string) => {
|
||||||
const servers = settings.get("servers") || [];
|
const servers = settings.get("servers") || [];
|
||||||
const selectedServer = servers.find((s) => s._id === serverId);
|
const selectedServer = servers.find((s) => s._id === serverId);
|
||||||
|
|
||||||
@ -51,7 +59,7 @@ export default () => {
|
|||||||
return new Error("Server not found");
|
return new Error("Server not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
showAppView(selectedServer.url);
|
await showAppView(selectedServer.url);
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@ function onError(e: Error) {
|
|||||||
function onRejection(reason: Error) {
|
function onRejection(reason: Error) {
|
||||||
if (reason instanceof Error) {
|
if (reason instanceof Error) {
|
||||||
let _reason = reason;
|
let _reason = reason;
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||||
const errPrototype = Object.getPrototypeOf(reason);
|
const errPrototype = Object.getPrototypeOf(reason);
|
||||||
const nameProperty = Object.getOwnPropertyDescriptor(errPrototype, "name");
|
const nameProperty = Object.getOwnPropertyDescriptor(errPrototype, "name");
|
||||||
|
|
||||||
|
@ -2,4 +2,4 @@ import app from "./app";
|
|||||||
import log from "./log";
|
import log from "./log";
|
||||||
|
|
||||||
log.init();
|
log.init();
|
||||||
app.init();
|
void app.init();
|
||||||
|
@ -8,6 +8,7 @@ const api: WorkAdventureDesktopApi = {
|
|||||||
notify: (txt) => ipcRenderer.send("app:notify", txt),
|
notify: (txt) => ipcRenderer.send("app:notify", txt),
|
||||||
onMuteToggle: (callback) => ipcRenderer.on("app:on-mute-toggle", callback),
|
onMuteToggle: (callback) => ipcRenderer.on("app:on-mute-toggle", callback),
|
||||||
onCameraToggle: (callback) => ipcRenderer.on("app:on-camera-toggle", callback),
|
onCameraToggle: (callback) => ipcRenderer.on("app:on-camera-toggle", callback),
|
||||||
|
getDesktopCapturerSources: (options) => ipcRenderer.invoke("app:getDesktopCapturerSources", options),
|
||||||
};
|
};
|
||||||
|
|
||||||
contextBridge.exposeInMainWorld("WAD", api);
|
contextBridge.exposeInMainWorld("WAD", api);
|
||||||
|
@ -1,3 +1,15 @@
|
|||||||
|
// 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 = {
|
export type WorkAdventureDesktopApi = {
|
||||||
desktop: boolean;
|
desktop: boolean;
|
||||||
isDevelopment: () => Promise<boolean>;
|
isDevelopment: () => Promise<boolean>;
|
||||||
@ -5,4 +17,5 @@ export type WorkAdventureDesktopApi = {
|
|||||||
notify: (txt: string) => void;
|
notify: (txt: string) => void;
|
||||||
onMuteToggle: (callback: () => void) => void;
|
onMuteToggle: (callback: () => void) => void;
|
||||||
onCameraToggle: (callback: () => void) => void;
|
onCameraToggle: (callback: () => void) => void;
|
||||||
|
getDesktopCapturerSources: (options: SourcesOptions) => Promise<DesktopCapturerSource[]>;
|
||||||
};
|
};
|
||||||
|
@ -36,14 +36,14 @@ export function createTray() {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Check for updates",
|
label: "Check for updates",
|
||||||
async click() {
|
click() {
|
||||||
await autoUpdater.manualRequestUpdateCheck();
|
void autoUpdater.manualRequestUpdateCheck();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Open Logs",
|
label: "Open Logs",
|
||||||
click() {
|
click() {
|
||||||
log.openLog();
|
void log.openLog();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -115,7 +115,7 @@ export async function createWindow() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function showAppView(url?: string) {
|
export async function showAppView(url?: string) {
|
||||||
if (!appView) {
|
if (!appView) {
|
||||||
throw new Error("App view not found");
|
throw new Error("App view not found");
|
||||||
}
|
}
|
||||||
@ -130,7 +130,7 @@ export function showAppView(url?: string) {
|
|||||||
mainWindow.addBrowserView(appView);
|
mainWindow.addBrowserView(appView);
|
||||||
|
|
||||||
if (url && url !== appViewUrl) {
|
if (url && url !== appViewUrl) {
|
||||||
appView.webContents.loadURL(url);
|
await appView.webContents.loadURL(url);
|
||||||
appViewUrl = url;
|
appViewUrl = url;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -166,7 +166,8 @@ return [
|
|||||||
],
|
],
|
||||||
[
|
[
|
||||||
'title' => 'Troubleshooting',
|
'title' => 'Troubleshooting',
|
||||||
'url' => '/map-building/troubleshooting',
|
'url' => '/map-building/troubleshooting.md',
|
||||||
'view' => 'content.map.troubleshooting'
|
'markdown' => 'maps.troubleshooting',
|
||||||
|
'editUrl' => 'https://github.com/thecodingmachine/workadventure/edit/develop/docs/maps/troubleshooting.md',
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
@ -18,11 +18,18 @@ In order to create a zone that opens websites:
|
|||||||
|
|
||||||
{.alert.alert-warning}
|
{.alert.alert-warning}
|
||||||
A website can explicitly forbid another website from loading it in an iFrame using
|
A website can explicitly forbid another website from loading it in an iFrame using
|
||||||
the [X-Frame-Options HTTP header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options).
|
the [X-Frame-Options HTTP header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options). You can
|
||||||
|
read more about this common issue and possible workaround the [troubleshooting guide](troubleshooting.md#embedding-an-iframe-is-forbidden).
|
||||||
|
|
||||||
{.alert.alert-info}
|
{.alert.alert-info}
|
||||||
As an alternative, you may also put the `openWebsite` properties on a layer (rather than putting them on an "area" object)
|
As an alternative, you may also put the `openWebsite` properties on a layer (rather than putting them on an "area" object)
|
||||||
but we advise to stick with "area" objects for better performance!
|
but we advise sticking with "area" objects for better performance!
|
||||||
|
|
||||||
|
{.alert.alert-warning}
|
||||||
|
If the website you are embedding is using cookies, those cookies must be configured with the `SameSite=none` attribute. Otherwise,
|
||||||
|
they will be ignored by the browser. If you manage to see the website you embed but cannot log into it, the `SameSite` attribute is most
|
||||||
|
likely the culprit. You can read more about this common issue and possible workaround the [troubleshooting guide](troubleshooting.md#i-cannot-log-into-my-embedded-website).
|
||||||
|
|
||||||
|
|
||||||
## Integrating a Youtube video
|
## Integrating a Youtube video
|
||||||
|
|
||||||
|
94
docs/maps/troubleshooting.md
Normal file
94
docs/maps/troubleshooting.md
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
{.section-title.accent.text-primary}
|
||||||
|
# Troubleshooting
|
||||||
|
|
||||||
|
## Look at the browser console
|
||||||
|
|
||||||
|
If your map is not displayed correctly (most notably if you are getting a black screen), open your browser console.
|
||||||
|
This is usually done by pressing the F12 key and selecting the "console" tab.
|
||||||
|
|
||||||
|
Scan the output. Towards the end, you might see a message explaining why your map cannot be loaded.
|
||||||
|
|
||||||
|
## Check webserver CORS settings
|
||||||
|
|
||||||
|
If you are hosting the map you built on your own webserver and if the map does not load, please check that
|
||||||
|
[your webserver CORS settings are correctly configured](hosting.md).
|
||||||
|
|
||||||
|
## Issues embedding a website
|
||||||
|
|
||||||
|
When you are embedding a website in WorkAdventure (whether it is using the [`openWebsite` property](opening-a-website.md) or
|
||||||
|
the [integrated website in a map](website-in-map.md) feature or the [Scripting API](scripting.md)), WorkAdventure
|
||||||
|
will open your website using an iFrame.
|
||||||
|
|
||||||
|
Browsers have various security measures in place, and website owners can use those measures to prevent websites from
|
||||||
|
being used inside iFrames (either partially or completely).
|
||||||
|
|
||||||
|
In the chapters below, we will list what can possibly prevent you from embedding a website, and see what are your options.
|
||||||
|
|
||||||
|
### Embedding an iFrame is forbidden
|
||||||
|
|
||||||
|
The worst that can happen is that the website you are trying to embed completely denies you the authorisation.
|
||||||
|
A website owner can do that using the [`X-Frame-Options` HTTP header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options),
|
||||||
|
or the newer [`Content-Security-Policy` HTTP header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy).
|
||||||
|
|
||||||
|
Take a look at the headers of the page you are trying to load.
|
||||||
|
|
||||||
|
{.alert.alert-info}
|
||||||
|
You can view the headers of the web page you try to load in the developer tools of your browser (usually accessible using the F12 key
|
||||||
|
of your keyboard), in the network tab. Click on the top-most request and check the "Response Headers".
|
||||||
|
|
||||||
|
Below is what you can see when opening a Youtube video page:
|
||||||
|
|
||||||
|
![](images/x-frame-options.png)
|
||||||
|
|
||||||
|
`X-Frame-Options: DENY` or `X-Frame-Options: SAMEORIGIN` will prevent WorkAdventure from loading the page.
|
||||||
|
`Content-Security-Policy` header have also the potential to prevent WorkAdventure from loading the page.
|
||||||
|
|
||||||
|
If the website you are trying to embed has one of these headers set, here are your options:
|
||||||
|
|
||||||
|
- if you have control over the website or know the owner, you can contact the owner/administrator of the website and ask for an exception
|
||||||
|
- otherwise, you can look for an "embed" option. Some websites have special pages that can be embedded. For instance,
|
||||||
|
YouTube has special "embed" links that can be used to embed a video in your website. A lot of websites have the same feature (you
|
||||||
|
can usually find those links in the "share" section)
|
||||||
|
|
||||||
|
If none of these options are available to you, as a last resort, you can use the [`openTab` property](opening-a-website.md) instead of the `openWebsite` property.
|
||||||
|
It will open your webpage in another tab instead of opening it in an iFrame.
|
||||||
|
|
||||||
|
### I cannot log into my embedded website
|
||||||
|
|
||||||
|
When you log into a website, the website is issuing a "cookie". The cookie is a unique identifier that allows the website
|
||||||
|
to recognize you and to identify you. To improve the privacy of their users, browsers can sometimes treat cookies
|
||||||
|
inside iFrames as "third-party cookies" and discard them.
|
||||||
|
|
||||||
|
Cookies can come with a `SameSite` attribute.
|
||||||
|
|
||||||
|
The `SameSite` attribute can take these values: "Lax", "Strict" or "None". The only value that allows using the
|
||||||
|
cookie inside an iFrame is "None".
|
||||||
|
|
||||||
|
{.alert.alert-info}
|
||||||
|
The `SameSite` attribute of your cookie MUST be set to "None" if you want to be able to use this cookie from an iFrame inside WorkAdventure.
|
||||||
|
|
||||||
|
**Default values**:
|
||||||
|
|
||||||
|
If the "SameSite" attribute is not explicitly set, [the behaviour depends on the browser](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite#browser_compatibility).
|
||||||
|
Chrome, Edge and Opera will default to "Lax".
|
||||||
|
Firefox and Safari will default to "None" (as of 2022/04/25).
|
||||||
|
|
||||||
|
As a result, a website that does not set the `SameSite` attribute on cookies will work correctly in Firefox and Safari but
|
||||||
|
login will fail on Chrome, Edge and Opera.
|
||||||
|
|
||||||
|
If the website you are trying to embed has the `SameSite` attribute set to a value other than "None", here are your options:
|
||||||
|
|
||||||
|
- if you have control over the website or know the owner, you can contact the owner/administrator of the website and ask
|
||||||
|
the owner/administrator to change the `SameSite` settings.
|
||||||
|
- otherwise, you will have to use the [`openTab` property](opening-a-website.md) instead of the `openWebsite` property.
|
||||||
|
It will open your webpage in another tab instead of in an iFrame.
|
||||||
|
|
||||||
|
## Need some help?
|
||||||
|
|
||||||
|
<div class="card bg-red text-white"><div class="card-body">
|
||||||
|
<p>WorkAdventure is a constantly evolving project and there is plenty of room for improvement regarding map editing.</p>
|
||||||
|
<p>If you are facing any troubles, do not hesitate to seek help in
|
||||||
|
<a href="https://discord.gg/G6Xh9ZM9aR">our Discord server</a> or open an "issue" in the
|
||||||
|
<a href="https://github.com/thecodingmachine/workadventure/issues" target="_blank">GitHub WorkAdventure account</a>.
|
||||||
|
</p>
|
||||||
|
</div></div>
|
@ -36,7 +36,7 @@ module.exports = {
|
|||||||
"eol-last": ["error", "always"],
|
"eol-last": ["error", "always"],
|
||||||
"@typescript-eslint/no-explicit-any": "error",
|
"@typescript-eslint/no-explicit-any": "error",
|
||||||
"no-throw-literal": "error",
|
"no-throw-literal": "error",
|
||||||
"@typescript-eslint/no-unused-vars": ["error"],
|
"@typescript-eslint/no-unused-vars": ["error", { "args": "none", "caughtErrors": "all", "varsIgnorePattern": "_exhaustiveCheck" }],
|
||||||
// TODO: remove those ignored rules and write a stronger code!
|
// TODO: remove those ignored rules and write a stronger code!
|
||||||
"@typescript-eslint/no-unsafe-call": "off",
|
"@typescript-eslint/no-unsafe-call": "off",
|
||||||
"@typescript-eslint/restrict-plus-operands": "off",
|
"@typescript-eslint/restrict-plus-operands": "off",
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
src/Messages/generated
|
src/Messages/generated
|
||||||
src/Messages/JsonMessages
|
src/Messages/JsonMessages
|
||||||
src/i18n/i18n-svelte.ts
|
src/i18n/i18n-*.ts
|
||||||
src/i18n/i18n-types.ts
|
|
||||||
src/i18n/i18n-util.ts
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://unpkg.com/typesafe-i18n@2.59.0/schema/typesafe-i18n.json",
|
"$schema": "https://unpkg.com/typesafe-i18n@5.4.0/schema/typesafe-i18n.json",
|
||||||
"baseLocale": "en-US",
|
"baseLocale": "en-US",
|
||||||
"adapter": "svelte"
|
"adapter": "svelte"
|
||||||
}
|
}
|
@ -5,7 +5,7 @@
|
|||||||
"license": "SEE LICENSE IN LICENSE.txt",
|
"license": "SEE LICENSE IN LICENSE.txt",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@geprog/vite-plugin-env-config": "^4.0.3",
|
"@geprog/vite-plugin-env-config": "^4.0.3",
|
||||||
"@home-based-studio/phaser3-utils": "^0.4.2",
|
"@home-based-studio/phaser3-utils": "^0.4.7",
|
||||||
"@sveltejs/vite-plugin-svelte": "^1.0.0-next.36",
|
"@sveltejs/vite-plugin-svelte": "^1.0.0-next.36",
|
||||||
"@tsconfig/svelte": "^1.0.10",
|
"@tsconfig/svelte": "^1.0.10",
|
||||||
"@types/google-protobuf": "^3.7.3",
|
"@types/google-protobuf": "^3.7.3",
|
||||||
@ -42,7 +42,7 @@
|
|||||||
"buffer": "^6.0.3",
|
"buffer": "^6.0.3",
|
||||||
"cancelable-promise": "^4.2.1",
|
"cancelable-promise": "^4.2.1",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"deep-copy-ts": "^0.5.0",
|
"deep-copy-ts": "^0.5.4",
|
||||||
"easystarjs": "^0.4.4",
|
"easystarjs": "^0.4.4",
|
||||||
"fast-deep-equal": "^3.1.3",
|
"fast-deep-equal": "^3.1.3",
|
||||||
"google-protobuf": "^3.13.0",
|
"google-protobuf": "^3.13.0",
|
||||||
@ -60,7 +60,7 @@
|
|||||||
"standardized-audio-context": "^25.2.4",
|
"standardized-audio-context": "^25.2.4",
|
||||||
"ts-deferred": "^1.0.4",
|
"ts-deferred": "^1.0.4",
|
||||||
"ts-proto": "^1.96.0",
|
"ts-proto": "^1.96.0",
|
||||||
"typesafe-i18n": "^2.59.0",
|
"typesafe-i18n": "^5.4.0",
|
||||||
"uuidv4": "^6.2.10",
|
"uuidv4": "^6.2.10",
|
||||||
"zod": "^3.14.3"
|
"zod": "^3.14.3"
|
||||||
},
|
},
|
||||||
|
BIN
front/public/resources/icons/icon_status_indicator_inside.png
Normal file
BIN
front/public/resources/icons/icon_status_indicator_inside.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 123 B |
BIN
front/public/resources/icons/icon_status_indicator_outline.png
Normal file
BIN
front/public/resources/icons/icon_status_indicator_outline.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.3 KiB |
@ -239,7 +239,7 @@ class IframeListener {
|
|||||||
} else if (iframeEvent.type === "cameraFollowPlayer") {
|
} else if (iframeEvent.type === "cameraFollowPlayer") {
|
||||||
this._cameraFollowPlayerStream.next(iframeEvent.data);
|
this._cameraFollowPlayerStream.next(iframeEvent.data);
|
||||||
} else if (iframeEvent.type === "chat") {
|
} else if (iframeEvent.type === "chat") {
|
||||||
scriptUtils.sendAnonymousChat(iframeEvent.data);
|
scriptUtils.sendAnonymousChat(iframeEvent.data, iframe.contentWindow ?? undefined);
|
||||||
} else if (iframeEvent.type === "openPopup") {
|
} else if (iframeEvent.type === "openPopup") {
|
||||||
this._openPopupStream.next(iframeEvent.data);
|
this._openPopupStream.next(iframeEvent.data);
|
||||||
} else if (iframeEvent.type === "closePopup") {
|
} else if (iframeEvent.type === "closePopup") {
|
||||||
@ -294,7 +294,6 @@ class IframeListener {
|
|||||||
handleMenuUnregisterEvent(iframeEvent.data.name);
|
handleMenuUnregisterEvent(iframeEvent.data.name);
|
||||||
} else {
|
} else {
|
||||||
// Keep the line below. It will throw an error if we forget to handle one of the possible values.
|
// Keep the line below. It will throw an error if we forget to handle one of the possible values.
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
const _exhaustiveCheck: never = iframeEvent;
|
const _exhaustiveCheck: never = iframeEvent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -400,13 +399,20 @@ class IframeListener {
|
|||||||
this.scripts.delete(scriptUrl);
|
this.scripts.delete(scriptUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
sendUserInputChat(message: string) {
|
/**
|
||||||
this.postMessage({
|
* @param message The message to dispatch
|
||||||
|
* @param exceptOrigin Don't dispatch the message to exceptOrigin (to avoid infinite loops)
|
||||||
|
*/
|
||||||
|
sendUserInputChat(message: string, exceptOrigin?: Window) {
|
||||||
|
this.postMessage(
|
||||||
|
{
|
||||||
type: "userInputChat",
|
type: "userInputChat",
|
||||||
data: {
|
data: {
|
||||||
message: message,
|
message: message,
|
||||||
} as UserInputChatEvent,
|
} as UserInputChatEvent,
|
||||||
});
|
},
|
||||||
|
exceptOrigin
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
sendEnterEvent(name: string) {
|
sendEnterEvent(name: string) {
|
||||||
@ -522,8 +528,11 @@ class IframeListener {
|
|||||||
/**
|
/**
|
||||||
* Sends the message... to all allowed iframes.
|
* Sends the message... to all allowed iframes.
|
||||||
*/
|
*/
|
||||||
public postMessage(message: IframeResponseEvent<keyof IframeResponseEventMap>) {
|
public postMessage(message: IframeResponseEvent<keyof IframeResponseEventMap>, exceptOrigin?: Window) {
|
||||||
for (const iframe of this.iframes) {
|
for (const iframe of this.iframes) {
|
||||||
|
if (exceptOrigin === iframe.contentWindow) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
iframe.contentWindow?.postMessage(message, "*");
|
iframe.contentWindow?.postMessage(message, "*");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,9 +11,9 @@ class ScriptUtils {
|
|||||||
window.location.href = url;
|
window.location.href = url;
|
||||||
}
|
}
|
||||||
|
|
||||||
public sendAnonymousChat(chatEvent: ChatEvent) {
|
public sendAnonymousChat(chatEvent: ChatEvent, origin?: Window) {
|
||||||
const userId = playersStore.addFacticePlayer(chatEvent.author);
|
const userId = playersStore.addFacticePlayer(chatEvent.author);
|
||||||
chatMessagesStore.addExternalMessage(userId, chatEvent.message);
|
chatMessagesStore.addExternalMessage(userId, chatEvent.message, origin);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import { isSilentStore, requestedCameraState, requestedMicrophoneState } from "../../Stores/MediaStore";
|
import { requestedCameraState, requestedMicrophoneState, silentStore } from "../../Stores/MediaStore";
|
||||||
import { get } from "svelte/store";
|
import { get } from "svelte/store";
|
||||||
import { WorkAdventureDesktopApi } from "@wa-preload-app";
|
import { WorkAdventureDesktopApi } from "@wa-preload-app";
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface Window {
|
interface Window {
|
||||||
WAD: WorkAdventureDesktopApi;
|
WAD?: WorkAdventureDesktopApi;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,8 +36,8 @@ class DesktopApi {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
isSilentStore.subscribe((value) => {
|
silentStore.subscribe((silent) => {
|
||||||
this.isSilent = value;
|
this.isSilent = silent;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -88,7 +88,6 @@ export function createState(target: "global" | "player"): WorkadventureStateComm
|
|||||||
}
|
}
|
||||||
return target.loadVariable(p.toString());
|
return target.loadVariable(p.toString());
|
||||||
},
|
},
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
set(target: WorkadventureStateCommands, p: PropertyKey, value: unknown, receiver: unknown): boolean {
|
set(target: WorkadventureStateCommands, p: PropertyKey, value: unknown, receiver: unknown): boolean {
|
||||||
// Note: when using "set", there is no way to wait, so we ignore the return of the promise.
|
// Note: when using "set", there is no way to wait, so we ignore the return of the promise.
|
||||||
// User must use WA.state.saveVariable to have error message.
|
// User must use WA.state.saveVariable to have error message.
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { requestedScreenSharingState, screenSharingAvailableStore } from "../Stores/ScreenSharingStore";
|
import { requestedScreenSharingState, screenSharingAvailableStore } from "../Stores/ScreenSharingStore";
|
||||||
import { isSilentStore, requestedCameraState, requestedMicrophoneState } from "../Stores/MediaStore";
|
import { requestedCameraState, requestedMicrophoneState, silentStore } from "../Stores/MediaStore";
|
||||||
import monitorImg from "./images/monitor.svg";
|
import monitorImg from "./images/monitor.svg";
|
||||||
import monitorCloseImg from "./images/monitor-close.svg";
|
import monitorCloseImg from "./images/monitor-close.svg";
|
||||||
import cinemaImg from "./images/cinema.svg";
|
import cinemaImg from "./images/cinema.svg";
|
||||||
@ -13,7 +13,6 @@
|
|||||||
import lockImg from "./images/lock.svg";
|
import lockImg from "./images/lock.svg";
|
||||||
import { LayoutMode } from "../WebRtc/LayoutManager";
|
import { LayoutMode } from "../WebRtc/LayoutManager";
|
||||||
import { peerStore } from "../Stores/PeerStore";
|
import { peerStore } from "../Stores/PeerStore";
|
||||||
import { onDestroy } from "svelte";
|
|
||||||
import { embedScreenLayout } from "../Stores/EmbedScreensStore";
|
import { embedScreenLayout } from "../Stores/EmbedScreensStore";
|
||||||
import { followRoleStore, followStateStore, followUsersStore } from "../Stores/FollowStore";
|
import { followRoleStore, followStateStore, followUsersStore } from "../Stores/FollowStore";
|
||||||
import { gameManager } from "../Phaser/Game/GameManager";
|
import { gameManager } from "../Phaser/Game/GameManager";
|
||||||
@ -22,7 +21,7 @@
|
|||||||
const gameScene = gameManager.getCurrentGameScene();
|
const gameScene = gameManager.getCurrentGameScene();
|
||||||
|
|
||||||
function screenSharingClick(): void {
|
function screenSharingClick(): void {
|
||||||
if (isSilent) return;
|
if ($silentStore) return;
|
||||||
if ($requestedScreenSharingState === true) {
|
if ($requestedScreenSharingState === true) {
|
||||||
requestedScreenSharingState.disableScreenSharing();
|
requestedScreenSharingState.disableScreenSharing();
|
||||||
} else {
|
} else {
|
||||||
@ -31,7 +30,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function cameraClick(): void {
|
function cameraClick(): void {
|
||||||
if (isSilent) return;
|
if ($silentStore) return;
|
||||||
if ($requestedCameraState === true) {
|
if ($requestedCameraState === true) {
|
||||||
requestedCameraState.disableWebcam();
|
requestedCameraState.disableWebcam();
|
||||||
} else {
|
} else {
|
||||||
@ -40,7 +39,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function microphoneClick(): void {
|
function microphoneClick(): void {
|
||||||
if (isSilent) return;
|
if ($silentStore) return;
|
||||||
if ($requestedMicrophoneState === true) {
|
if ($requestedMicrophoneState === true) {
|
||||||
requestedMicrophoneState.disableMicrophone();
|
requestedMicrophoneState.disableMicrophone();
|
||||||
} else {
|
} else {
|
||||||
@ -75,12 +74,6 @@
|
|||||||
function lockClick() {
|
function lockClick() {
|
||||||
gameScene.connection?.emitLockGroup(!$currentPlayerGroupLockStateStore);
|
gameScene.connection?.emitLockGroup(!$currentPlayerGroupLockStateStore);
|
||||||
}
|
}
|
||||||
|
|
||||||
let isSilent: boolean;
|
|
||||||
const unsubscribeIsSilent = isSilentStore.subscribe((value) => {
|
|
||||||
isSilent = value;
|
|
||||||
});
|
|
||||||
onDestroy(unsubscribeIsSilent);
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="btn-cam-action">
|
<div class="btn-cam-action">
|
||||||
@ -94,7 +87,7 @@
|
|||||||
|
|
||||||
<div
|
<div
|
||||||
class="btn-follow"
|
class="btn-follow"
|
||||||
class:hide={($peerStore.size === 0 && $followStateStore === "off") || isSilent}
|
class:hide={($peerStore.size === 0 && $followStateStore === "off") || $silentStore}
|
||||||
class:disabled={$followStateStore !== "off"}
|
class:disabled={$followStateStore !== "off"}
|
||||||
on:click={followClick}
|
on:click={followClick}
|
||||||
>
|
>
|
||||||
@ -103,7 +96,7 @@
|
|||||||
|
|
||||||
<div
|
<div
|
||||||
class="btn-lock"
|
class="btn-lock"
|
||||||
class:hide={$peerStore.size === 0 || isSilent}
|
class:hide={$peerStore.size === 0 || $silentStore}
|
||||||
class:disabled={$currentPlayerGroupLockStateStore}
|
class:disabled={$currentPlayerGroupLockStateStore}
|
||||||
on:click={lockClick}
|
on:click={lockClick}
|
||||||
>
|
>
|
||||||
@ -113,26 +106,26 @@
|
|||||||
<div
|
<div
|
||||||
class="btn-monitor"
|
class="btn-monitor"
|
||||||
on:click={screenSharingClick}
|
on:click={screenSharingClick}
|
||||||
class:hide={!$screenSharingAvailableStore || isSilent}
|
class:hide={!$screenSharingAvailableStore || $silentStore}
|
||||||
class:enabled={$requestedScreenSharingState}
|
class:enabled={$requestedScreenSharingState}
|
||||||
>
|
>
|
||||||
{#if $requestedScreenSharingState && !isSilent}
|
{#if $requestedScreenSharingState && !$silentStore}
|
||||||
<img class="noselect" src={monitorImg} alt="Start screen sharing" />
|
<img class="noselect" src={monitorImg} alt="Start screen sharing" />
|
||||||
{:else}
|
{:else}
|
||||||
<img class="noselect" src={monitorCloseImg} alt="Stop screen sharing" />
|
<img class="noselect" src={monitorCloseImg} alt="Stop screen sharing" />
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="btn-video" on:click={cameraClick} class:disabled={!$requestedCameraState || isSilent}>
|
<div class="btn-video" on:click={cameraClick} class:disabled={!$requestedCameraState || $silentStore}>
|
||||||
{#if $requestedCameraState && !isSilent}
|
{#if $requestedCameraState && !$silentStore}
|
||||||
<img class="noselect" src={cinemaImg} alt="Turn on webcam" />
|
<img class="noselect" src={cinemaImg} alt="Turn on webcam" />
|
||||||
{:else}
|
{:else}
|
||||||
<img class="noselect" src={cinemaCloseImg} alt="Turn off webcam" />
|
<img class="noselect" src={cinemaCloseImg} alt="Turn off webcam" />
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="btn-micro" on:click={microphoneClick} class:disabled={!$requestedMicrophoneState || isSilent}>
|
<div class="btn-micro" on:click={microphoneClick} class:disabled={!$requestedMicrophoneState || $silentStore}>
|
||||||
{#if $requestedMicrophoneState && !isSilent}
|
{#if $requestedMicrophoneState && !$silentStore}
|
||||||
<img class="noselect" src={microphoneImg} alt="Turn on microphone" />
|
<img class="noselect" src={microphoneImg} alt="Turn on microphone" />
|
||||||
{:else}
|
{:else}
|
||||||
<img class="noselect" src={microphoneCloseImg} alt="Turn off microphone" />
|
<img class="noselect" src={microphoneCloseImg} alt="Turn off microphone" />
|
||||||
|
@ -19,6 +19,5 @@
|
|||||||
padding-top: 2%;
|
padding-top: 2%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 200;
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -67,10 +67,11 @@
|
|||||||
{/key}
|
{/key}
|
||||||
{:else if $highlightedEmbedScreen.type === "cowebsite"}
|
{:else if $highlightedEmbedScreen.type === "cowebsite"}
|
||||||
{#key $highlightedEmbedScreen.embed.getId()}
|
{#key $highlightedEmbedScreen.embed.getId()}
|
||||||
|
<div class="highlighted-cowebsite-container nes-container is-rounded">
|
||||||
<div
|
<div
|
||||||
id={"cowebsite-slot-" + $highlightedEmbedScreen.embed.getId()}
|
id={"cowebsite-slot-" + $highlightedEmbedScreen.embed.getId()}
|
||||||
class="highlighted-cowebsite nes-container is-rounded"
|
class="highlighted-cowebsite"
|
||||||
>
|
/>
|
||||||
<div class="actions">
|
<div class="actions">
|
||||||
<button type="button" class="nes-btn is-error close" on:click={closeCoWebsite}
|
<button type="button" class="nes-btn is-error close" on:click={closeCoWebsite}
|
||||||
>×</button
|
>×</button
|
||||||
@ -119,14 +120,22 @@
|
|||||||
margin-bottom: 3%;
|
margin-bottom: 3%;
|
||||||
|
|
||||||
.highlighted-cowebsite {
|
.highlighted-cowebsite {
|
||||||
|
height: 100% !important;
|
||||||
|
width: 100% !important;
|
||||||
|
position: relative;
|
||||||
|
z-index: 200;
|
||||||
|
|
||||||
|
&-container {
|
||||||
height: 100% !important;
|
height: 100% !important;
|
||||||
width: 96%;
|
width: 96%;
|
||||||
background-color: rgba(#000000, 0.6);
|
background-color: rgba(#000000, 0.6);
|
||||||
margin: 0 !important;
|
margin: 0 !important;
|
||||||
|
padding: 0 !important;
|
||||||
.actions {
|
.actions {
|
||||||
z-index: 200;
|
z-index: 202;
|
||||||
position: relative;
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
top: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
justify-content: end;
|
justify-content: end;
|
||||||
@ -138,4 +147,5 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -53,11 +53,14 @@
|
|||||||
background: #eceeee;
|
background: #eceeee;
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
margin-right: auto;
|
margin-right: auto;
|
||||||
|
position: absolute;
|
||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
margin-top: 4%;
|
margin-top: 4%;
|
||||||
max-height: 80vh;
|
max-height: 80vh;
|
||||||
max-width: 80vw;
|
max-width: 80vw;
|
||||||
|
margin-left: 10%;
|
||||||
|
margin-right: 10%;
|
||||||
z-index: 600;
|
z-index: 600;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
@ -38,6 +38,7 @@
|
|||||||
import { actionsMenuStore } from "../Stores/ActionsMenuStore";
|
import { actionsMenuStore } from "../Stores/ActionsMenuStore";
|
||||||
import ActionsMenu from "./ActionsMenu/ActionsMenu.svelte";
|
import ActionsMenu from "./ActionsMenu/ActionsMenu.svelte";
|
||||||
import Lazy from "./Lazy.svelte";
|
import Lazy from "./Lazy.svelte";
|
||||||
|
import { showDesktopCapturerSourcePicker } from "../Stores/ScreenSharingStore";
|
||||||
|
|
||||||
let mainLayout: HTMLDivElement;
|
let mainLayout: HTMLDivElement;
|
||||||
|
|
||||||
@ -67,6 +68,11 @@
|
|||||||
</aside>
|
</aside>
|
||||||
|
|
||||||
<section id="main-layout-main">
|
<section id="main-layout-main">
|
||||||
|
<Lazy
|
||||||
|
when={$showDesktopCapturerSourcePicker}
|
||||||
|
component={() => import("./Video/DesktopCapturerSourcePicker.svelte")}
|
||||||
|
/>
|
||||||
|
|
||||||
{#if $menuVisiblilityStore}
|
{#if $menuVisiblilityStore}
|
||||||
<Menu />
|
<Menu />
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -88,16 +88,11 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function translateMenuName(menu: MenuItem) {
|
$: subMenuTranslations = $subMenusStore.map((subMenu) =>
|
||||||
if (menu.type === "scripting") {
|
subMenu.type === "scripting" ? subMenu.label : $LL.menu.sub[subMenu.key]()
|
||||||
return menu.label;
|
);
|
||||||
}
|
$: activeSubMenuTranslation =
|
||||||
|
activeSubMenu.type === "scripting" ? activeSubMenu.label : $LL.menu.sub[activeSubMenu.key]();
|
||||||
// Bypass the proxy of typesafe for getting the menu name : https://github.com/ivanhofer/typesafe-i18n/issues/156
|
|
||||||
const getMenuName = $LL.menu.sub[menu.key];
|
|
||||||
|
|
||||||
return getMenuName();
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:window on:keydown={onKeyDown} />
|
<svelte:window on:keydown={onKeyDown} />
|
||||||
@ -106,20 +101,20 @@
|
|||||||
<div class="menu-nav-sidebar nes-container is-rounded" transition:fly={{ x: -1000, duration: 500 }}>
|
<div class="menu-nav-sidebar nes-container is-rounded" transition:fly={{ x: -1000, duration: 500 }}>
|
||||||
<h2>{$LL.menu.title()}</h2>
|
<h2>{$LL.menu.title()}</h2>
|
||||||
<nav>
|
<nav>
|
||||||
{#each $subMenusStore as submenu}
|
{#each $subMenusStore as submenu, i}
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="nes-btn {activeSubMenu === submenu ? 'is-disabled' : ''}"
|
class="nes-btn {activeSubMenu === submenu ? 'is-disabled' : ''}"
|
||||||
on:click|preventDefault={() => void switchMenu(submenu)}
|
on:click|preventDefault={() => void switchMenu(submenu)}
|
||||||
>
|
>
|
||||||
{translateMenuName(submenu)}
|
{subMenuTranslations[i]}
|
||||||
</button>
|
</button>
|
||||||
{/each}
|
{/each}
|
||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
<div class="menu-submenu-container nes-container is-rounded" transition:fly={{ y: -1000, duration: 500 }}>
|
<div class="menu-submenu-container nes-container is-rounded" transition:fly={{ y: -1000, duration: 500 }}>
|
||||||
<button type="button" class="nes-btn is-error close" on:click={closeMenu}>×</button>
|
<button type="button" class="nes-btn is-error close" on:click={closeMenu}>×</button>
|
||||||
<h2>{translateMenuName(activeSubMenu)}</h2>
|
<h2>{activeSubMenuTranslation}</h2>
|
||||||
<svelte:component this={activeComponent} {...props} />
|
<svelte:component this={activeComponent} {...props} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -28,12 +28,12 @@
|
|||||||
let previewCameraPrivacySettings = valueCameraPrivacySettings;
|
let previewCameraPrivacySettings = valueCameraPrivacySettings;
|
||||||
let previewMicrophonePrivacySettings = valueMicrophonePrivacySettings;
|
let previewMicrophonePrivacySettings = valueMicrophonePrivacySettings;
|
||||||
|
|
||||||
function saveSetting() {
|
async function saveSetting() {
|
||||||
let change = false;
|
let change = false;
|
||||||
|
|
||||||
if (valueLocale !== previewValueLocale) {
|
if (valueLocale !== previewValueLocale) {
|
||||||
previewValueLocale = valueLocale;
|
previewValueLocale = valueLocale;
|
||||||
setCurrentLocale(valueLocale as Locales);
|
await setCurrentLocale(valueLocale as Locales);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (valueVideo !== previewValueVideo) {
|
if (valueVideo !== previewValueVideo) {
|
||||||
@ -174,7 +174,7 @@
|
|||||||
<div class="nes-select is-dark">
|
<div class="nes-select is-dark">
|
||||||
<select class="languages-switcher" bind:value={valueLocale}>
|
<select class="languages-switcher" bind:value={valueLocale}>
|
||||||
{#each displayableLocales as locale (locale.id)}
|
{#each displayableLocales as locale (locale.id)}
|
||||||
<option value={locale.id}>{`${locale.language} (${locale.country})`}</option>
|
<option value={locale.id}>{`${locale.language} (${locale.region})`}</option>
|
||||||
{/each}
|
{/each}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { localVolumeStore, obtainedMediaConstraintStore } from "../Stores/MediaStore";
|
import { localVolumeStore, obtainedMediaConstraintStore, silentStore } from "../Stores/MediaStore";
|
||||||
import { localStreamStore, isSilentStore } from "../Stores/MediaStore";
|
import { localStreamStore } from "../Stores/MediaStore";
|
||||||
import SoundMeterWidget from "./SoundMeterWidget.svelte";
|
import SoundMeterWidget from "./SoundMeterWidget.svelte";
|
||||||
import { onDestroy, onMount } from "svelte";
|
import { onDestroy, onMount } from "svelte";
|
||||||
import { srcObject } from "./Video/utils";
|
import { srcObject } from "./Video/utils";
|
||||||
@ -20,11 +20,6 @@
|
|||||||
unsubscribeLocalStreamStore();
|
unsubscribeLocalStreamStore();
|
||||||
});
|
});
|
||||||
|
|
||||||
let isSilent: boolean;
|
|
||||||
const unsubscribeIsSilent = isSilentStore.subscribe((value) => {
|
|
||||||
isSilent = value;
|
|
||||||
});
|
|
||||||
|
|
||||||
let cameraContainer: HTMLDivElement;
|
let cameraContainer: HTMLDivElement;
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
@ -40,16 +35,14 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
onDestroy(unsubscribeIsSilent);
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="nes-container is-rounded my-cam-video-container"
|
class="nes-container is-rounded my-cam-video-container"
|
||||||
class:hide={($localStreamStore.type !== "success" || !$obtainedMediaConstraintStore.video) && !isSilent}
|
class:hide={($localStreamStore.type !== "success" || !$obtainedMediaConstraintStore.video) && !$silentStore}
|
||||||
bind:this={cameraContainer}
|
bind:this={cameraContainer}
|
||||||
>
|
>
|
||||||
{#if isSilent}
|
{#if $silentStore}
|
||||||
<div class="is-silent">{$LL.camera.my.silentZone()}</div>
|
<div class="is-silent">{$LL.camera.my.silentZone()}</div>
|
||||||
{:else if $localStreamStore.type === "success" && $localStreamStore.stream}
|
{:else if $localStreamStore.type === "success" && $localStreamStore.stream}
|
||||||
<video class="my-cam-video" use:srcObject={stream} autoplay muted playsinline />
|
<video class="my-cam-video" use:srcObject={stream} autoplay muted playsinline />
|
||||||
|
@ -2,47 +2,46 @@
|
|||||||
import { fly } from "svelte/transition";
|
import { fly } from "svelte/transition";
|
||||||
import { errorScreenStore } from "../../Stores/ErrorScreenStore";
|
import { errorScreenStore } from "../../Stores/ErrorScreenStore";
|
||||||
import { gameManager } from "../../Phaser/Game/GameManager";
|
import { gameManager } from "../../Phaser/Game/GameManager";
|
||||||
|
import { connectionManager } from "../../Connexion/ConnectionManager";
|
||||||
|
import { get } from "svelte/store";
|
||||||
|
import { onDestroy } from "svelte";
|
||||||
|
|
||||||
import logoImg from "../images/logo-min-white.png";
|
import logoImg from "../images/logo-min-white.png";
|
||||||
let logo = gameManager?.currentStartedRoom?.loginSceneLogo ?? logoImg;
|
let logo = gameManager?.currentStartedRoom?.loginSceneLogo ?? logoImg;
|
||||||
import error from "../images/error.png";
|
|
||||||
import cup from "../images/cup.png";
|
|
||||||
import reload from "../images/reload.png";
|
import reload from "../images/reload.png";
|
||||||
import external from "../images/external-link.png";
|
|
||||||
import { get } from "svelte/store";
|
|
||||||
|
|
||||||
let errorScreen = get(errorScreenStore);
|
let errorScreen = get(errorScreenStore);
|
||||||
|
|
||||||
function click() {
|
function click() {
|
||||||
if (errorScreen.urlToRedirect) window.location.replace(errorScreen.urlToRedirect);
|
if (errorScreen.type === "unauthorized") void connectionManager.logout();
|
||||||
else if (errorScreen.type === "redirect" && window.history.length > 2) history.back();
|
|
||||||
else window.location.reload();
|
else window.location.reload();
|
||||||
}
|
}
|
||||||
let details = errorScreen.details;
|
let details = errorScreen.details;
|
||||||
let timeVar = errorScreen.timeToRetry ?? 0;
|
let timeVar = errorScreen.timeToRetry ?? 0;
|
||||||
|
|
||||||
if (errorScreen.type === "retry") {
|
if (errorScreen.type === "retry") {
|
||||||
setInterval(() => {
|
let interval = setInterval(() => {
|
||||||
if (timeVar <= 1000) click();
|
if (timeVar <= 1000) click();
|
||||||
timeVar -= 1000;
|
timeVar -= 1000;
|
||||||
}, 1000);
|
}, 1000);
|
||||||
|
onDestroy(() => clearInterval(interval));
|
||||||
}
|
}
|
||||||
|
|
||||||
$: detailsStylized = details.replace("{time}", `${timeVar / 1000}`);
|
$: detailsStylized = (details ?? "").replace("{time}", `${timeVar / 1000}`);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<main class="errorScreen" transition:fly={{ y: -200, duration: 500 }}>
|
<main class="errorScreen" transition:fly={{ y: -200, duration: 500 }}>
|
||||||
<div style="width: 90%;">
|
<div style="width: 90%;">
|
||||||
<img src={logo} alt="WorkAdventure" class="logo" />
|
<img src={logo} alt="WorkAdventure" class="logo" />
|
||||||
<div><img src={$errorScreenStore.type === "retry" ? cup : error} alt="" class="icon" /></div>
|
<div><img src={$errorScreenStore.image} alt="" class="icon" /></div>
|
||||||
{#if $errorScreenStore.type !== "retry"}<h2>{$errorScreenStore.title}</h2>{/if}
|
{#if $errorScreenStore.type !== "retry"}<h2>{$errorScreenStore.title}</h2>{/if}
|
||||||
<p>{$errorScreenStore.subtitle}</p>
|
<p>{$errorScreenStore.subtitle}</p>
|
||||||
{#if $errorScreenStore.type !== "retry"}<p class="code">Code : {$errorScreenStore.code}</p>{/if}
|
{#if $errorScreenStore.type !== "retry"}<p class="code">Code : {$errorScreenStore.code}</p>{/if}
|
||||||
<p class="details">
|
<p class="details">
|
||||||
{detailsStylized}{#if $errorScreenStore.type === "retry"}<div class="loading" />{/if}
|
{detailsStylized}{#if $errorScreenStore.type === "retry"}<div class="loading" />{/if}
|
||||||
</p>
|
</p>
|
||||||
{#if ($errorScreenStore.type === "retry" && $errorScreenStore.canRetryManual) || ($errorScreenStore.type === "redirect" && (window.history.length > 2 || $errorScreenStore.urlToRedirect))}
|
{#if ($errorScreenStore.type === "retry" && $errorScreenStore.canRetryManual) || $errorScreenStore.type === "unauthorized"}
|
||||||
<button type="button" class="nes-btn is-primary button" on:click={click}>
|
<button type="button" class="nes-btn is-primary button" on:click={click}>
|
||||||
<img src={$errorScreenStore.type === "retry" ? reload : external} alt="" class="reload" />
|
{#if $errorScreenStore.type === "retry"}<img src={reload} alt="" class="reload" />{/if}
|
||||||
{$errorScreenStore.buttonTitle}
|
{$errorScreenStore.buttonTitle}
|
||||||
</button>
|
</button>
|
||||||
{/if}
|
{/if}
|
||||||
@ -74,10 +73,14 @@
|
|||||||
.logo {
|
.logo {
|
||||||
width: 50%;
|
width: 50%;
|
||||||
margin-bottom: 50px;
|
margin-bottom: 50px;
|
||||||
|
max-height: 25vh;
|
||||||
|
max-width: 50vw;
|
||||||
}
|
}
|
||||||
.icon {
|
.icon {
|
||||||
height: 125px;
|
height: 125px;
|
||||||
margin-bottom: 25px;
|
margin-bottom: 25px;
|
||||||
|
max-height: 25vh;
|
||||||
|
max-width: 50vw;
|
||||||
}
|
}
|
||||||
h2 {
|
h2 {
|
||||||
font-family: "Press Start 2P";
|
font-family: "Press Start 2P";
|
||||||
|
171
front/src/Components/Video/DesktopCapturerSourcePicker.svelte
Normal file
171
front/src/Components/Video/DesktopCapturerSourcePicker.svelte
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { fly } from "svelte/transition";
|
||||||
|
import {
|
||||||
|
desktopCapturerSourcePromiseResolve,
|
||||||
|
showDesktopCapturerSourcePicker,
|
||||||
|
} from "../../Stores/ScreenSharingStore";
|
||||||
|
import { onDestroy, onMount } from "svelte";
|
||||||
|
import type { DesktopCapturerSource } from "@wa-preload-app";
|
||||||
|
|
||||||
|
let desktopCapturerSources: DesktopCapturerSource[] = [];
|
||||||
|
let interval: ReturnType<typeof setInterval>;
|
||||||
|
|
||||||
|
async function getDesktopCapturerSources() {
|
||||||
|
if (!window.WAD) {
|
||||||
|
throw new Error("This component can only be used in the desktop app");
|
||||||
|
}
|
||||||
|
desktopCapturerSources = await window.WAD.getDesktopCapturerSources({
|
||||||
|
thumbnailSize: {
|
||||||
|
height: 144,
|
||||||
|
width: 256,
|
||||||
|
},
|
||||||
|
types: ["screen", "window"],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onMount(async () => {
|
||||||
|
await getDesktopCapturerSources();
|
||||||
|
interval = setInterval(() => {
|
||||||
|
void getDesktopCapturerSources();
|
||||||
|
}, 1000);
|
||||||
|
});
|
||||||
|
|
||||||
|
onDestroy(() => {
|
||||||
|
clearInterval(interval);
|
||||||
|
});
|
||||||
|
|
||||||
|
function selectDesktopCapturerSource(source: DesktopCapturerSource) {
|
||||||
|
if (!desktopCapturerSourcePromiseResolve) {
|
||||||
|
throw new Error("desktopCapturerSourcePromiseResolve is not defined");
|
||||||
|
}
|
||||||
|
desktopCapturerSourcePromiseResolve(source);
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
|
||||||
|
function cancel() {
|
||||||
|
if (!desktopCapturerSourcePromiseResolve) {
|
||||||
|
throw new Error("desktopCapturerSourcePromiseResolve is not defined");
|
||||||
|
}
|
||||||
|
desktopCapturerSourcePromiseResolve(null);
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
|
||||||
|
function close() {
|
||||||
|
$showDesktopCapturerSourcePicker = false;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="source-picker nes-container is-rounded" transition:fly={{ y: -50, duration: 500 }}>
|
||||||
|
<button type="button" class="nes-btn is-error close" on:click={cancel}>×</button>
|
||||||
|
<h2>Select a Screen or Window to share!</h2>
|
||||||
|
<section class="streams">
|
||||||
|
{#each desktopCapturerSources as source}
|
||||||
|
<div
|
||||||
|
class="media-box nes-container is-rounded clickable"
|
||||||
|
on:click|preventDefault={() => selectDesktopCapturerSource(source)}
|
||||||
|
>
|
||||||
|
<img src={source.thumbnailURL} alt={source.name} />
|
||||||
|
<div class="container">
|
||||||
|
{source.name}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.source-picker {
|
||||||
|
position: absolute;
|
||||||
|
pointer-events: auto;
|
||||||
|
background: #eceeee;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
margin-top: 4%;
|
||||||
|
height: 80vh;
|
||||||
|
width: 80vw;
|
||||||
|
max-width: 1024px;
|
||||||
|
z-index: 900;
|
||||||
|
text-align: center;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
background-color: #333333;
|
||||||
|
color: whitesmoke;
|
||||||
|
|
||||||
|
.nes-btn.is-error.close {
|
||||||
|
position: absolute;
|
||||||
|
top: -20px;
|
||||||
|
right: -20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-family: "Press Start 2P";
|
||||||
|
}
|
||||||
|
|
||||||
|
section.streams {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 10px;
|
||||||
|
overflow-y: auto;
|
||||||
|
justify-content: center;
|
||||||
|
align-content: flex-start;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.media-box {
|
||||||
|
position: relative;
|
||||||
|
padding: 0;
|
||||||
|
width: calc(100% / 3 - 20px);
|
||||||
|
max-width: 256px;
|
||||||
|
padding-bottom: calc(min((100% / 3 - 20px), 256px) * (144px / 256px));
|
||||||
|
max-height: 144px;
|
||||||
|
justify-content: center;
|
||||||
|
background-color: #000;
|
||||||
|
background-clip: padding-box;
|
||||||
|
|
||||||
|
&.clickable * {
|
||||||
|
cursor: url("../../../style/images/cursor_pointer.png"), pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
transform: scale(1.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
max-width: 100%;
|
||||||
|
max-height: 100%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.nes-container.is-rounded {
|
||||||
|
border-image-outset: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.container {
|
||||||
|
position: absolute;
|
||||||
|
width: 90%;
|
||||||
|
height: auto;
|
||||||
|
left: 5%;
|
||||||
|
top: calc(100% - 28px);
|
||||||
|
text-align: center;
|
||||||
|
padding: 2px 36px;
|
||||||
|
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow-x: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
font-size: 14px;
|
||||||
|
margin: 2px;
|
||||||
|
background-color: white;
|
||||||
|
color: #333333;
|
||||||
|
border: solid 3px black;
|
||||||
|
border-radius: 8px;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
Binary file not shown.
Before Width: | Height: | Size: 3.5 KiB |
Binary file not shown.
Before Width: | Height: | Size: 1.7 KiB |
Binary file not shown.
Before Width: | Height: | Size: 4.7 KiB |
Binary file not shown.
Before Width: | Height: | Size: 10 KiB |
@ -2,18 +2,19 @@
|
|||||||
import type { Game } from "../../Phaser/Game/Game";
|
import type { Game } from "../../Phaser/Game/Game";
|
||||||
import { SelectCharacterScene, SelectCharacterSceneName } from "../../Phaser/Login/SelectCharacterScene";
|
import { SelectCharacterScene, SelectCharacterSceneName } from "../../Phaser/Login/SelectCharacterScene";
|
||||||
import LL from "../../i18n/i18n-svelte";
|
import LL from "../../i18n/i18n-svelte";
|
||||||
import { customizeAvailableStore } from "../../Stores/SelectCharacterSceneStore";
|
import { customizeAvailableStore, selectedCollection } from "../../Stores/SelectCharacterSceneStore";
|
||||||
|
|
||||||
export let game: Game;
|
export let game: Game;
|
||||||
|
|
||||||
const selectCharacterScene = game.scene.getScene(SelectCharacterSceneName) as SelectCharacterScene;
|
const selectCharacterScene = game.scene.getScene(SelectCharacterSceneName) as SelectCharacterScene;
|
||||||
|
const showArrows = selectCharacterScene.getCollectionKeysSize() > 1;
|
||||||
|
|
||||||
function selectLeft() {
|
function selectLeft() {
|
||||||
selectCharacterScene.moveToLeft();
|
selectCharacterScene.selectPreviousCollection();
|
||||||
}
|
}
|
||||||
|
|
||||||
function selectRight() {
|
function selectRight() {
|
||||||
selectCharacterScene.moveToRight();
|
selectCharacterScene.selectNextCollection();
|
||||||
}
|
}
|
||||||
|
|
||||||
function cameraScene() {
|
function cameraScene() {
|
||||||
@ -25,15 +26,15 @@
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<form class="selectCharacterScene">
|
|
||||||
<section class="text-center">
|
<section class="text-center">
|
||||||
<h2>{$LL.woka.selectWoka.title()}</h2>
|
<h2>{$LL.woka.selectWoka.title()}</h2>
|
||||||
<button class="selectCharacterButton selectCharacterButtonLeft nes-btn" on:click|preventDefault={selectLeft}>
|
</section>
|
||||||
<
|
<section class="category">
|
||||||
</button>
|
{#if showArrows}
|
||||||
<button class="selectCharacterButton selectCharacterButtonRight nes-btn" on:click|preventDefault={selectRight}>
|
<button class="selectCharacterButton nes-btn" on:click|preventDefault={selectLeft}> < </button>
|
||||||
>
|
<strong class="category-text">{$selectedCollection}</strong>
|
||||||
</button>
|
<button class="selectCharacterButton nes-btn" on:click|preventDefault={selectRight}> > </button>
|
||||||
|
{/if}
|
||||||
</section>
|
</section>
|
||||||
<section class="action">
|
<section class="action">
|
||||||
<button
|
<button
|
||||||
@ -49,22 +50,30 @@
|
|||||||
>
|
>
|
||||||
{/if}
|
{/if}
|
||||||
</section>
|
</section>
|
||||||
</form>
|
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@import "../../../style/breakpoints.scss";
|
@import "../../../style/breakpoints.scss";
|
||||||
|
|
||||||
form.selectCharacterScene {
|
|
||||||
font-family: "Press Start 2P";
|
|
||||||
pointer-events: auto;
|
|
||||||
color: #ebeeee;
|
|
||||||
|
|
||||||
section {
|
section {
|
||||||
margin: 10px;
|
font-family: "Press Start 2P";
|
||||||
|
color: #ebeeee;
|
||||||
|
margin: 5px;
|
||||||
|
|
||||||
|
&.category {
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 8vh;
|
||||||
|
.category-text {
|
||||||
|
font-family: "Press Start 2P";
|
||||||
|
display: inline-block;
|
||||||
|
width: 65%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&.action {
|
&.action {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 2vh;
|
||||||
|
width: 100%;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin-top: 55vh;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
h2 {
|
h2 {
|
||||||
@ -77,31 +86,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
button.selectCharacterButton {
|
button.selectCharacterButton {
|
||||||
position: absolute;
|
|
||||||
top: 33vh;
|
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
button {
|
button {
|
||||||
font-family: "Press Start 2P";
|
font-family: "Press Start 2P";
|
||||||
|
pointer-events: auto;
|
||||||
&.selectCharacterButtonLeft {
|
|
||||||
left: 33vw;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.selectCharacterButtonRight {
|
|
||||||
right: 33vw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@include media-breakpoint-up(md) {
|
|
||||||
form.selectCharacterScene button.selectCharacterButtonLeft {
|
|
||||||
left: 5vw;
|
|
||||||
}
|
|
||||||
form.selectCharacterScene button.selectCharacterButtonRight {
|
|
||||||
right: 5vw;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -363,15 +363,13 @@ class ConnectionManager {
|
|||||||
|
|
||||||
if (locale) {
|
if (locale) {
|
||||||
try {
|
try {
|
||||||
if (locales.indexOf(locale) == -1) {
|
if (locales.indexOf(locale) !== -1) {
|
||||||
locales.forEach((l) => {
|
await setCurrentLocale(locale as Locales);
|
||||||
if (l.startsWith(locale.split("-")[0])) {
|
|
||||||
setCurrentLocale(l);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
setCurrentLocale(locale as Locales);
|
const nonRegionSpecificLocale = locales.find((l) => l.startsWith(locale.split("-")[0]));
|
||||||
|
if (nonRegionSpecificLocale) {
|
||||||
|
await setCurrentLocale(nonRegionSpecificLocale);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.warn("Could not set locale", err);
|
console.warn("Could not set locale", err);
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import type { SignalData } from "simple-peer";
|
import type { SignalData } from "simple-peer";
|
||||||
import type { RoomConnection } from "./RoomConnection";
|
import type { RoomConnection } from "./RoomConnection";
|
||||||
import type { BodyResourceDescriptionInterface } from "../Phaser/Entity/PlayerTextures";
|
import type { BodyResourceDescriptionInterface } from "../Phaser/Entity/PlayerTextures";
|
||||||
|
import { AvailabilityStatus } from "../Messages/ts-proto-generated/protos/messages";
|
||||||
|
|
||||||
export interface PointInterface {
|
export interface PointInterface {
|
||||||
x: number;
|
x: number;
|
||||||
@ -14,7 +15,7 @@ export interface MessageUserPositionInterface {
|
|||||||
name: string;
|
name: string;
|
||||||
characterLayers: BodyResourceDescriptionInterface[];
|
characterLayers: BodyResourceDescriptionInterface[];
|
||||||
position: PointInterface;
|
position: PointInterface;
|
||||||
away: boolean;
|
status: AvailabilityStatus;
|
||||||
visitCardUrl: string | null;
|
visitCardUrl: string | null;
|
||||||
companion: string | null;
|
companion: string | null;
|
||||||
userUuid: string;
|
userUuid: string;
|
||||||
@ -30,7 +31,7 @@ export interface MessageUserJoined {
|
|||||||
name: string;
|
name: string;
|
||||||
characterLayers: BodyResourceDescriptionInterface[];
|
characterLayers: BodyResourceDescriptionInterface[];
|
||||||
position: PointInterface;
|
position: PointInterface;
|
||||||
away: boolean;
|
status: AvailabilityStatus;
|
||||||
visitCardUrl: string | null;
|
visitCardUrl: string | null;
|
||||||
companion: string | null;
|
companion: string | null;
|
||||||
userUuid: string;
|
userUuid: string;
|
||||||
|
@ -42,6 +42,7 @@ import {
|
|||||||
SetPlayerDetailsMessage as SetPlayerDetailsMessageTsProto,
|
SetPlayerDetailsMessage as SetPlayerDetailsMessageTsProto,
|
||||||
PingMessage as PingMessageTsProto,
|
PingMessage as PingMessageTsProto,
|
||||||
CharacterLayerMessage,
|
CharacterLayerMessage,
|
||||||
|
AvailabilityStatus,
|
||||||
} from "../Messages/ts-proto-generated/protos/messages";
|
} from "../Messages/ts-proto-generated/protos/messages";
|
||||||
import { Subject } from "rxjs";
|
import { Subject } from "rxjs";
|
||||||
import { selectCharacterSceneVisibleStore } from "../Stores/SelectCharacterStore";
|
import { selectCharacterSceneVisibleStore } from "../Stores/SelectCharacterStore";
|
||||||
@ -302,8 +303,7 @@ export class RoomConnection implements RoomConnection {
|
|||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
// Security check: if we forget a "case", the line below will catch the error at compile-time.
|
// Security check: if we forget a "case", the line below will catch the error at compile-time.
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
const _exhaustiveCheck: never = subMessage;
|
||||||
const tmp: never = subMessage;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -483,15 +483,20 @@ export class RoomConnection implements RoomConnection {
|
|||||||
}
|
}
|
||||||
case "errorScreenMessage": {
|
case "errorScreenMessage": {
|
||||||
this._errorScreenMessageStream.next(message.errorScreenMessage);
|
this._errorScreenMessageStream.next(message.errorScreenMessage);
|
||||||
if (message.errorScreenMessage.code !== "retry") this.closed = true;
|
console.error("An error occurred server side: " + JSON.stringify(message.errorScreenMessage));
|
||||||
console.error("An error occurred server side: " + message.errorScreenMessage.code);
|
if (message.errorScreenMessage.code !== "retry") {
|
||||||
|
this.closed = true;
|
||||||
|
}
|
||||||
|
if (message.errorScreenMessage.type === "redirect" && message.errorScreenMessage.urlToRedirect) {
|
||||||
|
window.location.assign(message.errorScreenMessage.urlToRedirect);
|
||||||
|
} else {
|
||||||
errorScreenStore.setError(message.errorScreenMessage);
|
errorScreenStore.setError(message.errorScreenMessage);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
// Security check: if we forget a "case", the line below will catch the error at compile-time.
|
// Security check: if we forget a "case", the line below will catch the error at compile-time.
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
const _exhaustiveCheck: never = message;
|
||||||
const tmp: never = message;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -532,9 +537,9 @@ export class RoomConnection implements RoomConnection {
|
|||||||
this.socket.send(bytes);
|
this.socket.send(bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
public emitPlayerAway(away: boolean): void {
|
public emitPlayerStatusChange(status: AvailabilityStatus): void {
|
||||||
const message = SetPlayerDetailsMessageTsProto.fromPartial({
|
const message = SetPlayerDetailsMessageTsProto.fromPartial({
|
||||||
away,
|
status,
|
||||||
});
|
});
|
||||||
const bytes = ClientToServerMessageTsProto.encode({
|
const bytes = ClientToServerMessageTsProto.encode({
|
||||||
message: {
|
message: {
|
||||||
@ -626,19 +631,6 @@ export class RoomConnection implements RoomConnection {
|
|||||||
this.socket.send(bytes);
|
this.socket.send(bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
public setSilent(silent: boolean): void {
|
|
||||||
const bytes = ClientToServerMessageTsProto.encode({
|
|
||||||
message: {
|
|
||||||
$case: "silentMessage",
|
|
||||||
silentMessage: {
|
|
||||||
silent,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}).finish();
|
|
||||||
|
|
||||||
this.socket.send(bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
public setViewport(viewport: ViewportInterface): void {
|
public setViewport(viewport: ViewportInterface): void {
|
||||||
const bytes = ClientToServerMessageTsProto.encode({
|
const bytes = ClientToServerMessageTsProto.encode({
|
||||||
message: {
|
message: {
|
||||||
@ -682,7 +674,7 @@ export class RoomConnection implements RoomConnection {
|
|||||||
characterLayers,
|
characterLayers,
|
||||||
visitCardUrl: message.visitCardUrl,
|
visitCardUrl: message.visitCardUrl,
|
||||||
position: ProtobufClientUtils.toPointInterface(position),
|
position: ProtobufClientUtils.toPointInterface(position),
|
||||||
away: message.away,
|
status: message.status,
|
||||||
companion: companion ? companion.name : null,
|
companion: companion ? companion.name : null,
|
||||||
userUuid: message.userUuid,
|
userUuid: message.userUuid,
|
||||||
outlineColor: message.hasOutline ? message.outlineColor : undefined,
|
outlineColor: message.hasOutline ? message.outlineColor : undefined,
|
||||||
|
@ -61,9 +61,8 @@ export class CustomWokaPreviewer extends Phaser.GameObjects.Container {
|
|||||||
this.frame = this.scene.add.graphics();
|
this.frame = this.scene.add.graphics();
|
||||||
this.turnIcon = this.scene.add
|
this.turnIcon = this.scene.add
|
||||||
.image(this.background.displayWidth * 0.35, this.background.displayHeight * 0.35, "iconTurn")
|
.image(this.background.displayWidth * 0.35, this.background.displayHeight * 0.35, "iconTurn")
|
||||||
.setScale(0.25)
|
.setScale(0.2)
|
||||||
.setTintFill(0xffffff)
|
.setAlpha(0.75);
|
||||||
.setAlpha(0.5);
|
|
||||||
|
|
||||||
this.drawFrame();
|
this.drawFrame();
|
||||||
this.setSize(this.SIZE, this.SIZE);
|
this.setSize(this.SIZE, this.SIZE);
|
||||||
@ -130,11 +129,11 @@ export class CustomWokaPreviewer extends Phaser.GameObjects.Container {
|
|||||||
this.changeAnimation(direction, moving);
|
this.changeAnimation(direction, moving);
|
||||||
|
|
||||||
this.turnIconTween?.stop();
|
this.turnIconTween?.stop();
|
||||||
this.turnIcon.setScale(0.25);
|
this.turnIcon.setScale(0.2);
|
||||||
this.turnIconTween = this.scene.tweens.add({
|
this.turnIconTween = this.scene.tweens.add({
|
||||||
targets: [this.turnIcon],
|
targets: [this.turnIcon],
|
||||||
duration: 100,
|
duration: 100,
|
||||||
scale: 0.2,
|
scale: 0.15,
|
||||||
yoyo: true,
|
yoyo: true,
|
||||||
ease: Easing.SineEaseIn,
|
ease: Easing.SineEaseIn,
|
||||||
});
|
});
|
||||||
|
@ -1,36 +1,41 @@
|
|||||||
|
import { AvailabilityStatus } from "../../Messages/ts-proto-generated/protos/messages";
|
||||||
import { Easing } from "../../types";
|
import { Easing } from "../../types";
|
||||||
|
|
||||||
export class PlayerStatusDot extends Phaser.GameObjects.Container {
|
export class PlayerStatusDot extends Phaser.GameObjects.Container {
|
||||||
private graphics: Phaser.GameObjects.Graphics;
|
private statusImage: Phaser.GameObjects.Image;
|
||||||
|
private statusImageOutline: Phaser.GameObjects.Image;
|
||||||
|
|
||||||
private away: boolean;
|
private status: AvailabilityStatus;
|
||||||
|
|
||||||
private readonly COLORS = {
|
private readonly COLORS: Record<AvailabilityStatus, { filling: number; outline: number }> = {
|
||||||
// online: 0x00ff00,
|
[AvailabilityStatus.AWAY]: { filling: 0xf5931e, outline: 0x875d13 },
|
||||||
// away: 0xffff00,
|
[AvailabilityStatus.ONLINE]: { filling: 0x8cc43f, outline: 0x427a25 },
|
||||||
online: 0x8cc43f,
|
[AvailabilityStatus.SILENT]: { filling: 0xe74c3c, outline: 0xc0392b },
|
||||||
onlineOutline: 0x427a25,
|
[AvailabilityStatus.JITSI]: { filling: 0x8cc43f, outline: 0x427a25 },
|
||||||
away: 0xf5931e,
|
[AvailabilityStatus.UNRECOGNIZED]: { filling: 0xffffff, outline: 0xffffff },
|
||||||
awayOutline: 0x875d13,
|
[AvailabilityStatus.UNCHANGED]: { filling: 0xffffff, outline: 0xffffff },
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(scene: Phaser.Scene, x: number, y: number) {
|
constructor(scene: Phaser.Scene, x: number, y: number) {
|
||||||
super(scene, x, y);
|
super(scene, x, y);
|
||||||
|
|
||||||
this.away = false;
|
this.status = AvailabilityStatus.ONLINE;
|
||||||
|
|
||||||
|
this.statusImage = this.scene.add.image(0, 0, "iconStatusIndicatorInside");
|
||||||
|
this.statusImageOutline = this.scene.add.image(0, 0, "iconStatusIndicatorOutline");
|
||||||
|
|
||||||
|
this.add([this.statusImage, this.statusImageOutline]);
|
||||||
|
|
||||||
this.graphics = this.scene.add.graphics();
|
|
||||||
this.add(this.graphics);
|
|
||||||
this.redraw();
|
this.redraw();
|
||||||
|
|
||||||
this.scene.add.existing(this);
|
this.scene.add.existing(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public setAway(away: boolean = true, instant: boolean = false): void {
|
public setStatus(status: AvailabilityStatus, instant: boolean = false): void {
|
||||||
if (this.away === away) {
|
if (this.status === status || status === AvailabilityStatus.UNCHANGED) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.away = away;
|
this.status = status;
|
||||||
if (instant) {
|
if (instant) {
|
||||||
this.redraw();
|
this.redraw();
|
||||||
} else {
|
} else {
|
||||||
@ -56,10 +61,8 @@ export class PlayerStatusDot extends Phaser.GameObjects.Container {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private redraw(): void {
|
private redraw(): void {
|
||||||
this.graphics.clear();
|
const colors = this.COLORS[this.status];
|
||||||
this.graphics.fillStyle(this.away ? this.COLORS.away : this.COLORS.online);
|
this.statusImage.setTintFill(colors.filling);
|
||||||
this.graphics.lineStyle(1, this.away ? this.COLORS.awayOutline : this.COLORS.onlineOutline);
|
this.statusImageOutline.setTintFill(colors.outline);
|
||||||
this.graphics.fillCircle(0, 0, 3);
|
|
||||||
this.graphics.strokeCircle(0, 0, 3);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
35
front/src/Phaser/Components/SelectWoka/WokaSlot.ts
Normal file
35
front/src/Phaser/Components/SelectWoka/WokaSlot.ts
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import { GridItem } from "@home-based-studio/phaser3-utils";
|
||||||
|
|
||||||
|
export class WokaSlot extends GridItem {
|
||||||
|
private sprite: Phaser.GameObjects.Sprite;
|
||||||
|
private selection: Phaser.GameObjects.Rectangle;
|
||||||
|
|
||||||
|
private readonly SIZE: number = 50;
|
||||||
|
|
||||||
|
constructor(scene: Phaser.Scene, spriteKey: string, id?: string) {
|
||||||
|
super(scene, id);
|
||||||
|
|
||||||
|
this.sprite = this.scene.add.sprite(0, 0, spriteKey);
|
||||||
|
this.selection = this.scene.add
|
||||||
|
.rectangle(0, 0, this.SIZE, this.SIZE)
|
||||||
|
.setStrokeStyle(1, 0xffffff)
|
||||||
|
.setVisible(false);
|
||||||
|
|
||||||
|
this.add([this.selection, this.sprite]);
|
||||||
|
this.setSize(this.SIZE, this.SIZE);
|
||||||
|
this.setInteractive({ cursor: "pointer" });
|
||||||
|
this.scene.input.setDraggable(this);
|
||||||
|
|
||||||
|
this.bindEventHandlers();
|
||||||
|
|
||||||
|
this.scene.add.existing(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getSprite(): Phaser.GameObjects.Sprite {
|
||||||
|
return this.sprite;
|
||||||
|
}
|
||||||
|
|
||||||
|
public select(select: boolean = true): void {
|
||||||
|
this.selection.setVisible(select);
|
||||||
|
}
|
||||||
|
}
|
@ -6,6 +6,7 @@ export interface IconButtonConfig {
|
|||||||
hover: IconButtonAppearanceConfig;
|
hover: IconButtonAppearanceConfig;
|
||||||
pressed: IconButtonAppearanceConfig;
|
pressed: IconButtonAppearanceConfig;
|
||||||
selected: IconButtonAppearanceConfig;
|
selected: IconButtonAppearanceConfig;
|
||||||
|
iconScale?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IconButtonAppearanceConfig {
|
export interface IconButtonAppearanceConfig {
|
||||||
@ -34,7 +35,7 @@ export class IconButton extends Phaser.GameObjects.Container {
|
|||||||
this.config = config;
|
this.config = config;
|
||||||
|
|
||||||
this.background = this.scene.add.graphics();
|
this.background = this.scene.add.graphics();
|
||||||
this.icon = this.scene.add.image(0, 0, this.config.iconTextureKey);
|
this.icon = this.scene.add.image(0, 0, this.config.iconTextureKey).setScale(config.iconScale ?? 1);
|
||||||
this.drawBackground(this.config.idle);
|
this.drawBackground(this.config.idle);
|
||||||
|
|
||||||
this.add([this.background, this.icon]);
|
this.add([this.background, this.icon]);
|
||||||
|
@ -9,7 +9,6 @@ import { Companion } from "../Companion/Companion";
|
|||||||
import type { GameScene } from "../Game/GameScene";
|
import type { GameScene } from "../Game/GameScene";
|
||||||
import { DEPTH_INGAME_TEXT_INDEX } from "../Game/DepthIndexes";
|
import { DEPTH_INGAME_TEXT_INDEX } from "../Game/DepthIndexes";
|
||||||
import type OutlinePipelinePlugin from "phaser3-rex-plugins/plugins/outlinepipeline-plugin.js";
|
import type OutlinePipelinePlugin from "phaser3-rex-plugins/plugins/outlinepipeline-plugin.js";
|
||||||
import { isSilentStore } from "../../Stores/MediaStore";
|
|
||||||
import { lazyLoadPlayerCharacterTextures } from "./PlayerTexturesLoadingManager";
|
import { lazyLoadPlayerCharacterTextures } from "./PlayerTexturesLoadingManager";
|
||||||
import { TexturesHelper } from "../Helpers/TexturesHelper";
|
import { TexturesHelper } from "../Helpers/TexturesHelper";
|
||||||
import type { PictureStore } from "../../Stores/PictureStore";
|
import type { PictureStore } from "../../Stores/PictureStore";
|
||||||
@ -20,6 +19,7 @@ import type CancelablePromise from "cancelable-promise";
|
|||||||
import { TalkIcon } from "../Components/TalkIcon";
|
import { TalkIcon } from "../Components/TalkIcon";
|
||||||
import { Deferred } from "ts-deferred";
|
import { Deferred } from "ts-deferred";
|
||||||
import { PlayerStatusDot } from "../Components/PlayerStatusDot";
|
import { PlayerStatusDot } from "../Components/PlayerStatusDot";
|
||||||
|
import { AvailabilityStatus } from "../../Messages/ts-proto-generated/protos/messages";
|
||||||
|
|
||||||
const playerNameY = -25;
|
const playerNameY = -25;
|
||||||
const interactiveRadius = 35;
|
const interactiveRadius = 35;
|
||||||
@ -236,8 +236,8 @@ export abstract class Character extends Container implements OutlineableInterfac
|
|||||||
this.talkIcon.show(show, forceClose);
|
this.talkIcon.show(show, forceClose);
|
||||||
}
|
}
|
||||||
|
|
||||||
public setAwayStatus(away: boolean = true, instant: boolean = false): void {
|
public setStatus(status: AvailabilityStatus, instant: boolean = false): void {
|
||||||
this.statusDot.setAway(away, instant);
|
this.statusDot.setStatus(status, instant);
|
||||||
}
|
}
|
||||||
|
|
||||||
public addCompanion(name: string, texturePromise?: CancelablePromise<string>): void {
|
public addCompanion(name: string, texturePromise?: CancelablePromise<string>): void {
|
||||||
@ -355,13 +355,6 @@ export abstract class Character extends Container implements OutlineableInterfac
|
|||||||
super.destroy();
|
super.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
isSilent() {
|
|
||||||
isSilentStore.set(true);
|
|
||||||
}
|
|
||||||
noSilent() {
|
|
||||||
isSilentStore.set(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
playEmote(emote: string) {
|
playEmote(emote: string) {
|
||||||
this.cancelPreviousEmote();
|
this.cancelPreviousEmote();
|
||||||
const emoteY = -45;
|
const emoteY = -45;
|
||||||
|
@ -36,14 +36,16 @@ export enum PlayerTexturesKey {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class PlayerTextures {
|
export class PlayerTextures {
|
||||||
private PLAYER_RESOURCES: BodyResourceDescriptionListInterface = {};
|
private wokaResources: BodyResourceDescriptionListInterface = {};
|
||||||
private COLOR_RESOURCES: BodyResourceDescriptionListInterface = {};
|
private colorResources: BodyResourceDescriptionListInterface = {};
|
||||||
private EYES_RESOURCES: BodyResourceDescriptionListInterface = {};
|
private eyesResources: BodyResourceDescriptionListInterface = {};
|
||||||
private HAIR_RESOURCES: BodyResourceDescriptionListInterface = {};
|
private hairResources: BodyResourceDescriptionListInterface = {};
|
||||||
private CLOTHES_RESOURCES: BodyResourceDescriptionListInterface = {};
|
private clothesResources: BodyResourceDescriptionListInterface = {};
|
||||||
private HATS_RESOURCES: BodyResourceDescriptionListInterface = {};
|
private hatsResources: BodyResourceDescriptionListInterface = {};
|
||||||
private ACCESSORIES_RESOURCES: BodyResourceDescriptionListInterface = {};
|
private accessoriesResources: BodyResourceDescriptionListInterface = {};
|
||||||
private LAYERS: BodyResourceDescriptionListInterface[] = [];
|
private layers: BodyResourceDescriptionListInterface[] = [];
|
||||||
|
|
||||||
|
private wokaCollections = new Map<string, BodyResourceDescriptionInterface[]>();
|
||||||
|
|
||||||
public loadPlayerTexturesMetadata(metadata: WokaList): void {
|
public loadPlayerTexturesMetadata(metadata: WokaList): void {
|
||||||
this.mapTexturesMetadataIntoResources(metadata);
|
this.mapTexturesMetadataIntoResources(metadata);
|
||||||
@ -52,43 +54,53 @@ export class PlayerTextures {
|
|||||||
public getTexturesResources(key: PlayerTexturesKey): BodyResourceDescriptionListInterface {
|
public getTexturesResources(key: PlayerTexturesKey): BodyResourceDescriptionListInterface {
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case PlayerTexturesKey.Accessory:
|
case PlayerTexturesKey.Accessory:
|
||||||
return this.ACCESSORIES_RESOURCES;
|
return this.accessoriesResources;
|
||||||
case PlayerTexturesKey.Body:
|
case PlayerTexturesKey.Body:
|
||||||
return this.COLOR_RESOURCES;
|
return this.colorResources;
|
||||||
case PlayerTexturesKey.Clothes:
|
case PlayerTexturesKey.Clothes:
|
||||||
return this.CLOTHES_RESOURCES;
|
return this.clothesResources;
|
||||||
case PlayerTexturesKey.Eyes:
|
case PlayerTexturesKey.Eyes:
|
||||||
return this.EYES_RESOURCES;
|
return this.eyesResources;
|
||||||
case PlayerTexturesKey.Hair:
|
case PlayerTexturesKey.Hair:
|
||||||
return this.HAIR_RESOURCES;
|
return this.hairResources;
|
||||||
case PlayerTexturesKey.Hat:
|
case PlayerTexturesKey.Hat:
|
||||||
return this.HATS_RESOURCES;
|
return this.hatsResources;
|
||||||
case PlayerTexturesKey.Woka:
|
case PlayerTexturesKey.Woka:
|
||||||
return this.PLAYER_RESOURCES;
|
return this.wokaResources;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public getLayers(): BodyResourceDescriptionListInterface[] {
|
public getLayers(): BodyResourceDescriptionListInterface[] {
|
||||||
return this.LAYERS;
|
return this.layers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public getCollectionsKeys(): string[] {
|
||||||
|
return Array.from(this.wokaCollections.keys());
|
||||||
|
}
|
||||||
|
|
||||||
|
public getWokaCollectionTextures(key: string): BodyResourceDescriptionInterface[] {
|
||||||
|
return this.wokaCollections.get(key) ?? [];
|
||||||
}
|
}
|
||||||
|
|
||||||
private mapTexturesMetadataIntoResources(metadata: WokaList): void {
|
private mapTexturesMetadataIntoResources(metadata: WokaList): void {
|
||||||
this.PLAYER_RESOURCES = this.getMappedResources(metadata.woka);
|
this.wokaResources = this.getMappedResources(metadata.woka);
|
||||||
this.COLOR_RESOURCES = this.getMappedResources(metadata.body);
|
this.colorResources = this.getMappedResources(metadata.body);
|
||||||
this.EYES_RESOURCES = this.getMappedResources(metadata.eyes);
|
this.eyesResources = this.getMappedResources(metadata.eyes);
|
||||||
this.HAIR_RESOURCES = this.getMappedResources(metadata.hair);
|
this.hairResources = this.getMappedResources(metadata.hair);
|
||||||
this.CLOTHES_RESOURCES = this.getMappedResources(metadata.clothes);
|
this.clothesResources = this.getMappedResources(metadata.clothes);
|
||||||
this.HATS_RESOURCES = this.getMappedResources(metadata.hat);
|
this.hatsResources = this.getMappedResources(metadata.hat);
|
||||||
this.ACCESSORIES_RESOURCES = this.getMappedResources(metadata.accessory);
|
this.accessoriesResources = this.getMappedResources(metadata.accessory);
|
||||||
|
|
||||||
this.LAYERS = [
|
this.layers = [
|
||||||
this.COLOR_RESOURCES,
|
this.colorResources,
|
||||||
this.EYES_RESOURCES,
|
this.eyesResources,
|
||||||
this.HAIR_RESOURCES,
|
this.hairResources,
|
||||||
this.CLOTHES_RESOURCES,
|
this.clothesResources,
|
||||||
this.HATS_RESOURCES,
|
this.hatsResources,
|
||||||
this.ACCESSORIES_RESOURCES,
|
this.accessoriesResources,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
this.mapWokaCollections(metadata.woka);
|
||||||
}
|
}
|
||||||
|
|
||||||
private getMappedResources(category: WokaPartType): BodyResourceDescriptionListInterface {
|
private getMappedResources(category: WokaPartType): BodyResourceDescriptionListInterface {
|
||||||
@ -103,6 +115,19 @@ export class PlayerTextures {
|
|||||||
}
|
}
|
||||||
return resources;
|
return resources;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private mapWokaCollections(category: WokaPartType): void {
|
||||||
|
if (!category) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (const collection of category.collections) {
|
||||||
|
const textures: BodyResourceDescriptionInterface[] = [];
|
||||||
|
for (const texture of collection.textures) {
|
||||||
|
textures.push({ id: texture.id, img: texture.url });
|
||||||
|
}
|
||||||
|
this.wokaCollections.set(collection.name, textures);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const OBJECTS: BodyResourceDescriptionInterface[] = [
|
export const OBJECTS: BodyResourceDescriptionInterface[] = [
|
||||||
|
@ -4,7 +4,7 @@ import { Character } from "../Entity/Character";
|
|||||||
import type { GameScene } from "../Game/GameScene";
|
import type { GameScene } from "../Game/GameScene";
|
||||||
import type { PointInterface } from "../../Connexion/ConnexionModels";
|
import type { PointInterface } from "../../Connexion/ConnexionModels";
|
||||||
import type { PlayerAnimationDirections } from "../Player/Animation";
|
import type { PlayerAnimationDirections } from "../Player/Animation";
|
||||||
import type { Unsubscriber } from "svelte/store";
|
import { get, Unsubscriber } from "svelte/store";
|
||||||
import type { ActivatableInterface } from "../Game/ActivatableInterface";
|
import type { ActivatableInterface } from "../Game/ActivatableInterface";
|
||||||
import type CancelablePromise from "cancelable-promise";
|
import type CancelablePromise from "cancelable-promise";
|
||||||
import LL from "../../i18n/i18n-svelte";
|
import LL from "../../i18n/i18n-svelte";
|
||||||
@ -113,7 +113,7 @@ export class RemotePlayer extends Character implements ActivatableInterface {
|
|||||||
const actions: ActionsMenuAction[] = [];
|
const actions: ActionsMenuAction[] = [];
|
||||||
if (this.visitCardUrl) {
|
if (this.visitCardUrl) {
|
||||||
actions.push({
|
actions.push({
|
||||||
actionName: LL.woka.menu.businessCard(),
|
actionName: get(LL).woka.menu.businessCard(),
|
||||||
protected: true,
|
protected: true,
|
||||||
priority: 1,
|
priority: 1,
|
||||||
callback: () => {
|
callback: () => {
|
||||||
@ -125,8 +125,8 @@ export class RemotePlayer extends Character implements ActivatableInterface {
|
|||||||
|
|
||||||
actions.push({
|
actions.push({
|
||||||
actionName: blackListManager.isBlackListed(this.userUuid)
|
actionName: blackListManager.isBlackListed(this.userUuid)
|
||||||
? LL.report.block.unblock()
|
? get(LL).report.block.unblock()
|
||||||
: LL.report.block.block(),
|
: get(LL).report.block.block(),
|
||||||
protected: true,
|
protected: true,
|
||||||
priority: -1,
|
priority: -1,
|
||||||
style: "is-error",
|
style: "is-error",
|
||||||
|
@ -76,6 +76,7 @@ export class ActivatablesManager {
|
|||||||
const currentPlayerPos = this.currentPlayer.getDirectionalActivationPosition(
|
const currentPlayerPos = this.currentPlayer.getDirectionalActivationPosition(
|
||||||
this.directionalActivationPositionShift
|
this.directionalActivationPositionShift
|
||||||
);
|
);
|
||||||
|
this.activatableObjectsDistances.clear();
|
||||||
for (const object of objects) {
|
for (const object of objects) {
|
||||||
const distance = MathUtils.distanceBetween(currentPlayerPos, object.getPosition());
|
const distance = MathUtils.distanceBetween(currentPlayerPos, object.getPosition());
|
||||||
this.activatableObjectsDistances.set(object, distance);
|
this.activatableObjectsDistances.set(object, distance);
|
||||||
|
@ -97,7 +97,6 @@ export class CameraManager extends Phaser.Events.EventEmitter {
|
|||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
this.camera.pan(setTo.x, setTo.y, duration, Easing.SineEaseOut, true, (camera, progress, x, y) => {
|
this.camera.pan(setTo.x, setTo.y, duration, Easing.SineEaseOut, true, (camera, progress, x, y) => {
|
||||||
if (this.cameraMode === CameraMode.Positioned) {
|
if (this.cameraMode === CameraMode.Positioned) {
|
||||||
this.waScaleManager.zoomModifier = currentZoomModifier + progress * zoomModifierChange;
|
this.waScaleManager.zoomModifier = currentZoomModifier + progress * zoomModifierChange;
|
||||||
@ -139,7 +138,6 @@ export class CameraManager extends Phaser.Events.EventEmitter {
|
|||||||
this.emit(CameraManagerEvent.CameraUpdate, this.getCameraUpdateEventData());
|
this.emit(CameraManagerEvent.CameraUpdate, this.getCameraUpdateEventData());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
this.camera.pan(focusOn.x, focusOn.y, duration, Easing.SineEaseOut, true, (camera, progress, x, y) => {
|
this.camera.pan(focusOn.x, focusOn.y, duration, Easing.SineEaseOut, true, (camera, progress, x, y) => {
|
||||||
this.waScaleManager.zoomModifier = currentZoomModifier + progress * zoomModifierChange;
|
this.waScaleManager.zoomModifier = currentZoomModifier + progress * zoomModifierChange;
|
||||||
if (progress === 1) {
|
if (progress === 1) {
|
||||||
|
@ -17,6 +17,7 @@ import { audioManagerFileStore, audioManagerVisibilityStore } from "../../Stores
|
|||||||
import { iframeListener } from "../../Api/IframeListener";
|
import { iframeListener } from "../../Api/IframeListener";
|
||||||
import { Room } from "../../Connexion/Room";
|
import { Room } from "../../Connexion/Room";
|
||||||
import LL from "../../i18n/i18n-svelte";
|
import LL from "../../i18n/i18n-svelte";
|
||||||
|
import { inJitsiStore, silentStore } from "../../Stores/MediaStore";
|
||||||
|
|
||||||
interface OpenCoWebsite {
|
interface OpenCoWebsite {
|
||||||
actionId: string;
|
actionId: string;
|
||||||
@ -77,6 +78,7 @@ export class GameMapPropertiesListener {
|
|||||||
coWebsiteManager.closeCoWebsite(coWebsite);
|
coWebsiteManager.closeCoWebsite(coWebsite);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
inJitsiStore.set(false);
|
||||||
} else {
|
} else {
|
||||||
const openJitsiRoomFunction = () => {
|
const openJitsiRoomFunction = () => {
|
||||||
let addPrefix = true;
|
let addPrefix = true;
|
||||||
@ -119,11 +121,15 @@ export class GameMapPropertiesListener {
|
|||||||
uuid: "jitsi",
|
uuid: "jitsi",
|
||||||
type: "message",
|
type: "message",
|
||||||
message: message,
|
message: message,
|
||||||
callback: () => openJitsiRoomFunction(),
|
callback: () => {
|
||||||
|
openJitsiRoomFunction();
|
||||||
|
inJitsiStore.set(true);
|
||||||
|
},
|
||||||
userInputManager: this.scene.userInputManager,
|
userInputManager: this.scene.userInputManager,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
openJitsiRoomFunction();
|
openJitsiRoomFunction();
|
||||||
|
inJitsiStore.set(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -160,11 +166,9 @@ export class GameMapPropertiesListener {
|
|||||||
|
|
||||||
this.gameMap.onPropertyChange(GameMapProperties.SILENT, (newValue) => {
|
this.gameMap.onPropertyChange(GameMapProperties.SILENT, (newValue) => {
|
||||||
if (newValue === undefined || newValue === false || newValue === "") {
|
if (newValue === undefined || newValue === false || newValue === "") {
|
||||||
this.scene.connection?.setSilent(false);
|
silentStore.set(false);
|
||||||
this.scene.CurrentPlayer.noSilent();
|
|
||||||
} else {
|
} else {
|
||||||
this.scene.connection?.setSilent(true);
|
silentStore.set(true);
|
||||||
this.scene.CurrentPlayer.isSilent();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -91,7 +91,7 @@ import { MapStore } from "../../Stores/Utils/MapStore";
|
|||||||
import { followUsersColorStore } from "../../Stores/FollowStore";
|
import { followUsersColorStore } from "../../Stores/FollowStore";
|
||||||
import { GameSceneUserInputHandler } from "../UserInput/GameSceneUserInputHandler";
|
import { GameSceneUserInputHandler } from "../UserInput/GameSceneUserInputHandler";
|
||||||
import { locale } from "../../i18n/i18n-svelte";
|
import { locale } from "../../i18n/i18n-svelte";
|
||||||
import { localVolumeStore } from "../../Stores/MediaStore";
|
import { availabilityStatusStore, localVolumeStore } from "../../Stores/MediaStore";
|
||||||
import { StringUtils } from "../../Utils/StringUtils";
|
import { StringUtils } from "../../Utils/StringUtils";
|
||||||
import { startLayerNamesStore } from "../../Stores/StartLayerNamesStore";
|
import { startLayerNamesStore } from "../../Stores/StartLayerNamesStore";
|
||||||
import { JitsiCoWebsite } from "../../WebRtc/CoWebsite/JitsiCoWebsite";
|
import { JitsiCoWebsite } from "../../WebRtc/CoWebsite/JitsiCoWebsite";
|
||||||
@ -101,7 +101,6 @@ import CancelablePromise from "cancelable-promise";
|
|||||||
import { Deferred } from "ts-deferred";
|
import { Deferred } from "ts-deferred";
|
||||||
import { SuperLoaderPlugin } from "../Services/SuperLoaderPlugin";
|
import { SuperLoaderPlugin } from "../Services/SuperLoaderPlugin";
|
||||||
import { PlayerDetailsUpdatedMessage } from "../../Messages/ts-proto-generated/protos/messages";
|
import { PlayerDetailsUpdatedMessage } from "../../Messages/ts-proto-generated/protos/messages";
|
||||||
import { privacyShutdownStore } from "../../Stores/PrivacyShutdownStore";
|
|
||||||
export interface GameSceneInitInterface {
|
export interface GameSceneInitInterface {
|
||||||
initPosition: PointInterface | null;
|
initPosition: PointInterface | null;
|
||||||
reconnecting: boolean;
|
reconnecting: boolean;
|
||||||
@ -177,8 +176,6 @@ export class GameScene extends DirtyScene {
|
|||||||
|
|
||||||
private localVolumeStoreUnsubscriber: Unsubscriber | undefined;
|
private localVolumeStoreUnsubscriber: Unsubscriber | undefined;
|
||||||
private followUsersColorStoreUnsubscribe!: Unsubscriber;
|
private followUsersColorStoreUnsubscribe!: Unsubscriber;
|
||||||
private currentPlayerGroupIdStoreUnsubscribe!: Unsubscriber;
|
|
||||||
private privacyShutdownStoreUnsubscribe!: Unsubscriber;
|
|
||||||
private userIsJitsiDominantSpeakerStoreUnsubscriber!: Unsubscriber;
|
private userIsJitsiDominantSpeakerStoreUnsubscriber!: Unsubscriber;
|
||||||
private jitsiParticipantsCountStoreUnsubscriber!: Unsubscriber;
|
private jitsiParticipantsCountStoreUnsubscriber!: Unsubscriber;
|
||||||
|
|
||||||
@ -249,6 +246,8 @@ export class GameScene extends DirtyScene {
|
|||||||
this.listenToIframeEvents();
|
this.listenToIframeEvents();
|
||||||
|
|
||||||
this.load.image("iconTalk", "/resources/icons/icon_talking.png");
|
this.load.image("iconTalk", "/resources/icons/icon_talking.png");
|
||||||
|
this.load.image("iconStatusIndicatorInside", "/resources/icons/icon_status_indicator_inside.png");
|
||||||
|
this.load.image("iconStatusIndicatorOutline", "/resources/icons/icon_status_indicator_outline.png");
|
||||||
|
|
||||||
if (touchScreenManager.supportTouchScreen) {
|
if (touchScreenManager.supportTouchScreen) {
|
||||||
this.load.image(joystickBaseKey, joystickBaseImg);
|
this.load.image(joystickBaseKey, joystickBaseImg);
|
||||||
@ -679,6 +678,11 @@ export class GameScene extends DirtyScene {
|
|||||||
this.tryChangeShowVoiceIndicatorState(this.jitsiDominantSpeaker && this.jitsiParticipantsCount > 1);
|
this.tryChangeShowVoiceIndicatorState(this.jitsiDominantSpeaker && this.jitsiParticipantsCount > 1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
availabilityStatusStore.subscribe((status) => {
|
||||||
|
this.connection?.emitPlayerStatusChange(status);
|
||||||
|
this.CurrentPlayer.setStatus(status);
|
||||||
|
});
|
||||||
|
|
||||||
this.emoteUnsubscribe = emoteStore.subscribe((emote) => {
|
this.emoteUnsubscribe = emoteStore.subscribe((emote) => {
|
||||||
if (emote) {
|
if (emote) {
|
||||||
this.CurrentPlayer?.playEmote(emote.url);
|
this.CurrentPlayer?.playEmote(emote.url);
|
||||||
@ -705,10 +709,6 @@ export class GameScene extends DirtyScene {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.privacyShutdownStoreUnsubscribe = privacyShutdownStore.subscribe((away) => {
|
|
||||||
this.connection?.emitPlayerAway(away);
|
|
||||||
});
|
|
||||||
|
|
||||||
Promise.all([
|
Promise.all([
|
||||||
this.connectionAnswerPromiseDeferred.promise as Promise<unknown>,
|
this.connectionAnswerPromiseDeferred.promise as Promise<unknown>,
|
||||||
...scriptPromises,
|
...scriptPromises,
|
||||||
@ -767,7 +767,7 @@ export class GameScene extends DirtyScene {
|
|||||||
characterLayers: message.characterLayers,
|
characterLayers: message.characterLayers,
|
||||||
name: message.name,
|
name: message.name,
|
||||||
position: message.position,
|
position: message.position,
|
||||||
away: message.away,
|
status: message.status,
|
||||||
visitCardUrl: message.visitCardUrl,
|
visitCardUrl: message.visitCardUrl,
|
||||||
companion: message.companion,
|
companion: message.companion,
|
||||||
userUuid: message.userUuid,
|
userUuid: message.userUuid,
|
||||||
@ -1106,6 +1106,13 @@ ${escapedMessage}
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
this.iframeSubscriptionList.push(
|
||||||
|
iframeListener.stopSoundStream.subscribe((stopSoundEvent) => {
|
||||||
|
const url = new URL(stopSoundEvent.url, this.MapUrlFile);
|
||||||
|
soundManager.stopSound(this.sound, url.toString());
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
this.iframeSubscriptionList.push(
|
this.iframeSubscriptionList.push(
|
||||||
iframeListener.addActionsMenuKeyToRemotePlayerStream.subscribe((data) => {
|
iframeListener.addActionsMenuKeyToRemotePlayerStream.subscribe((data) => {
|
||||||
this.MapPlayersByKey.get(data.id)?.registerActionsMenuAction({
|
this.MapPlayersByKey.get(data.id)?.registerActionsMenuAction({
|
||||||
@ -1556,7 +1563,6 @@ ${escapedMessage}
|
|||||||
this.emoteUnsubscribe();
|
this.emoteUnsubscribe();
|
||||||
this.emoteMenuUnsubscribe();
|
this.emoteMenuUnsubscribe();
|
||||||
this.followUsersColorStoreUnsubscribe();
|
this.followUsersColorStoreUnsubscribe();
|
||||||
this.privacyShutdownStoreUnsubscribe();
|
|
||||||
this.biggestAvailableAreaStoreUnsubscribe();
|
this.biggestAvailableAreaStoreUnsubscribe();
|
||||||
this.userIsJitsiDominantSpeakerStoreUnsubscriber();
|
this.userIsJitsiDominantSpeakerStoreUnsubscriber();
|
||||||
this.jitsiParticipantsCountStoreUnsubscriber();
|
this.jitsiParticipantsCountStoreUnsubscriber();
|
||||||
@ -1696,7 +1702,6 @@ ${escapedMessage}
|
|||||||
private createCollisionWithPlayer() {
|
private createCollisionWithPlayer() {
|
||||||
//add collision layer
|
//add collision layer
|
||||||
for (const phaserLayer of this.gameMap.phaserLayers) {
|
for (const phaserLayer of this.gameMap.phaserLayers) {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
this.physics.add.collider(this.CurrentPlayer, phaserLayer, (object1: GameObject, object2: GameObject) => {
|
this.physics.add.collider(this.CurrentPlayer, phaserLayer, (object1: GameObject, object2: GameObject) => {
|
||||||
//this.CurrentPlayer.say("Collision with layer : "+ (object2 as Tile).layer.name)
|
//this.CurrentPlayer.say("Collision with layer : "+ (object2 as Tile).layer.name)
|
||||||
});
|
});
|
||||||
@ -1747,11 +1752,9 @@ ${escapedMessage}
|
|||||||
emoteMenuStore.openEmoteMenu();
|
emoteMenuStore.openEmoteMenu();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
this.CurrentPlayer.on(Phaser.Input.Events.POINTER_OVER, (pointer: Phaser.Input.Pointer) => {
|
this.CurrentPlayer.on(Phaser.Input.Events.POINTER_OVER, (pointer: Phaser.Input.Pointer) => {
|
||||||
this.CurrentPlayer.pointerOverOutline(0x365dff);
|
this.CurrentPlayer.pointerOverOutline(0x365dff);
|
||||||
});
|
});
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
this.CurrentPlayer.on(Phaser.Input.Events.POINTER_OUT, (pointer: Phaser.Input.Pointer) => {
|
this.CurrentPlayer.on(Phaser.Input.Events.POINTER_OUT, (pointer: Phaser.Input.Pointer) => {
|
||||||
this.CurrentPlayer.pointerOutOutline();
|
this.CurrentPlayer.pointerOutOutline();
|
||||||
});
|
});
|
||||||
@ -1853,8 +1856,7 @@ ${escapedMessage}
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
const _exhaustiveCheck: never = event;
|
||||||
const tmp: never = event;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1933,8 +1935,8 @@ ${escapedMessage}
|
|||||||
if (addPlayerData.outlineColor !== undefined) {
|
if (addPlayerData.outlineColor !== undefined) {
|
||||||
player.setApiOutlineColor(addPlayerData.outlineColor);
|
player.setApiOutlineColor(addPlayerData.outlineColor);
|
||||||
}
|
}
|
||||||
if (addPlayerData.away !== undefined) {
|
if (addPlayerData.status !== undefined) {
|
||||||
player.setAwayStatus(addPlayerData.away, true);
|
player.setStatus(addPlayerData.status, true);
|
||||||
}
|
}
|
||||||
this.MapPlayers.add(player);
|
this.MapPlayers.add(player);
|
||||||
this.MapPlayersByKey.set(player.userId, player);
|
this.MapPlayersByKey.set(player.userId, player);
|
||||||
@ -2085,8 +2087,8 @@ ${escapedMessage}
|
|||||||
if (message.details?.showVoiceIndicator !== undefined) {
|
if (message.details?.showVoiceIndicator !== undefined) {
|
||||||
character.showTalkIcon(message.details?.showVoiceIndicator);
|
character.showTalkIcon(message.details?.showVoiceIndicator);
|
||||||
}
|
}
|
||||||
if (message.details?.away !== undefined) {
|
if (message.details?.status !== undefined) {
|
||||||
character.setAwayStatus(message.details?.away);
|
character.setStatus(message.details?.status);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2130,13 +2132,10 @@ ${escapedMessage}
|
|||||||
}
|
}
|
||||||
|
|
||||||
public enableMediaBehaviors() {
|
public enableMediaBehaviors() {
|
||||||
const silent = this.gameMap.getCurrentProperties().get(GameMapProperties.SILENT);
|
|
||||||
this.connection?.setSilent(!!silent);
|
|
||||||
mediaManager.showMyCamera();
|
mediaManager.showMyCamera();
|
||||||
}
|
}
|
||||||
|
|
||||||
public disableMediaBehaviors() {
|
public disableMediaBehaviors() {
|
||||||
this.connection?.setSilent(true);
|
|
||||||
mediaManager.hideMyCamera();
|
mediaManager.hideMyCamera();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { AvailabilityStatus } from "../../Messages/ts-proto-generated/protos/messages";
|
||||||
import type { BodyResourceDescriptionInterface } from "../Entity/PlayerTextures";
|
import type { BodyResourceDescriptionInterface } from "../Entity/PlayerTextures";
|
||||||
|
|
||||||
export interface PlayerInterface {
|
export interface PlayerInterface {
|
||||||
@ -7,7 +8,7 @@ export interface PlayerInterface {
|
|||||||
visitCardUrl: string | null;
|
visitCardUrl: string | null;
|
||||||
companion: string | null;
|
companion: string | null;
|
||||||
userUuid: string;
|
userUuid: string;
|
||||||
away: boolean;
|
status: AvailabilityStatus;
|
||||||
color?: string;
|
color?: string;
|
||||||
outlineColor?: number;
|
outlineColor?: number;
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,6 @@ export class PlayerMovement {
|
|||||||
const y =
|
const y =
|
||||||
(this.endPosition.y - this.startPosition.y) * ((tick - this.startTick) / (this.endTick - this.startTick)) +
|
(this.endPosition.y - this.startPosition.y) * ((tick - this.startTick) / (this.endTick - this.startTick)) +
|
||||||
this.startPosition.y;
|
this.startPosition.y;
|
||||||
//console.log('Computed position ', x, y)
|
|
||||||
return {
|
return {
|
||||||
x,
|
x,
|
||||||
y,
|
y,
|
||||||
|
@ -28,7 +28,7 @@ export class TexturesHelper {
|
|||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
rt.destroy();
|
rt.destroy();
|
||||||
throw new Error("Could not get the snapshot");
|
throw new Error("Could not get the snapshot: " + error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,4 +67,20 @@ export class TexturesHelper {
|
|||||||
rectangleTexture.generateTexture(textureKey, width, height);
|
rectangleTexture.generateTexture(textureKey, width, height);
|
||||||
rectangleTexture.destroy();
|
rectangleTexture.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static createCircleTexture(
|
||||||
|
scene: Phaser.Scene,
|
||||||
|
textureKey: string,
|
||||||
|
radius: number,
|
||||||
|
color: number,
|
||||||
|
outlineColor?: number,
|
||||||
|
outlineThickness?: number
|
||||||
|
): void {
|
||||||
|
const circleTexture = scene.add.graphics().fillStyle(color, 1).fillCircle(radius, radius, radius);
|
||||||
|
if (outlineColor) {
|
||||||
|
circleTexture.lineStyle(outlineThickness ?? 1, outlineColor).strokeCircle(radius, radius, radius);
|
||||||
|
}
|
||||||
|
circleTexture.generateTexture(textureKey, radius * 2, radius * 2);
|
||||||
|
circleTexture.destroy();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,4 +11,8 @@ export abstract class AbstractCharacterScene extends ResizableScene {
|
|||||||
this.playerTextures = new PlayerTextures();
|
this.playerTextures = new PlayerTextures();
|
||||||
this.superLoad = new SuperLoaderPlugin(this);
|
this.superLoad = new SuperLoaderPlugin(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
preload() {
|
||||||
|
this.input.dragDistanceThreshold = 10;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -54,7 +54,7 @@ export class CustomizeScene extends AbstractCharacterScene {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public preload(): void {
|
public preload(): void {
|
||||||
this.input.dragDistanceThreshold = 10;
|
super.preload();
|
||||||
|
|
||||||
this.load.image("iconClothes", "/resources/icons/icon_clothes.png");
|
this.load.image("iconClothes", "/resources/icons/icon_clothes.png");
|
||||||
this.load.image("iconAccessory", "/resources/icons/icon_accessory.png");
|
this.load.image("iconAccessory", "/resources/icons/icon_accessory.png");
|
||||||
@ -94,6 +94,7 @@ export class CustomizeScene extends AbstractCharacterScene {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public create(): void {
|
public create(): void {
|
||||||
|
this.tryLoadLastUsedWokaLayers();
|
||||||
waScaleManager.zoomModifier = 1;
|
waScaleManager.zoomModifier = 1;
|
||||||
this.createSlotBackgroundTextures();
|
this.createSlotBackgroundTextures();
|
||||||
this.initializeCustomWokaPreviewer();
|
this.initializeCustomWokaPreviewer();
|
||||||
@ -112,7 +113,6 @@ export class CustomizeScene extends AbstractCharacterScene {
|
|||||||
this.onResize();
|
this.onResize();
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
public update(time: number, dt: number): void {
|
public update(time: number, dt: number): void {
|
||||||
this.customWokaPreviewer.update();
|
this.customWokaPreviewer.update();
|
||||||
}
|
}
|
||||||
@ -150,12 +150,35 @@ export class CustomizeScene extends AbstractCharacterScene {
|
|||||||
this.scene.run(SelectCharacterSceneName);
|
this.scene.run(SelectCharacterSceneName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private tryLoadLastUsedWokaLayers(): void {
|
||||||
|
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])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
console.warn("Cannot load previous WOKA");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private createSlotBackgroundTextures(): void {
|
private createSlotBackgroundTextures(): void {
|
||||||
for (let i = 0; i < 4; i += 1) {
|
for (let i = 0; i < 4; i += 1) {
|
||||||
if (this.textures.getTextureKeys().includes(`floorTexture${i}`)) {
|
if (this.textures.getTextureKeys().includes(`floorTexture${i}`)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
TexturesHelper.createFloorRectangleTexture(this, `floorTexture${i}`, 50, 50, "floorTiles", i);
|
TexturesHelper.createFloorRectangleTexture(
|
||||||
|
this,
|
||||||
|
`floorTexture${i}`,
|
||||||
|
WokaBodyPartSlot.SIZE,
|
||||||
|
WokaBodyPartSlot.SIZE,
|
||||||
|
"floorTiles",
|
||||||
|
i
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -214,15 +237,16 @@ export class CustomizeScene extends AbstractCharacterScene {
|
|||||||
),
|
),
|
||||||
[CustomWokaBodyPart.Body]: new IconButton(this, 0, 0, this.getDefaultIconButtonConfig("iconBody")),
|
[CustomWokaBodyPart.Body]: new IconButton(this, 0, 0, this.getDefaultIconButtonConfig("iconBody")),
|
||||||
[CustomWokaBodyPart.Clothes]: new IconButton(this, 0, 0, this.getDefaultIconButtonConfig("iconClothes")),
|
[CustomWokaBodyPart.Clothes]: new IconButton(this, 0, 0, this.getDefaultIconButtonConfig("iconClothes")),
|
||||||
[CustomWokaBodyPart.Eyes]: new IconButton(this, 0, 0, this.getDefaultIconButtonConfig("iconEyes")),
|
[CustomWokaBodyPart.Eyes]: new IconButton(this, 0, 0, this.getDefaultIconButtonConfig("iconEyes", 0.7)),
|
||||||
[CustomWokaBodyPart.Hair]: new IconButton(this, 0, 0, this.getDefaultIconButtonConfig("iconHair")),
|
[CustomWokaBodyPart.Hair]: new IconButton(this, 0, 0, this.getDefaultIconButtonConfig("iconHair")),
|
||||||
[CustomWokaBodyPart.Hat]: new IconButton(this, 0, 0, this.getDefaultIconButtonConfig("iconHat")),
|
[CustomWokaBodyPart.Hat]: new IconButton(this, 0, 0, this.getDefaultIconButtonConfig("iconHat")),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private getDefaultIconButtonConfig(iconTextureKey: string): IconButtonConfig {
|
private getDefaultIconButtonConfig(iconTextureKey: string, iconScale?: number): IconButtonConfig {
|
||||||
return {
|
return {
|
||||||
iconTextureKey,
|
iconTextureKey,
|
||||||
|
iconScale,
|
||||||
width: 25,
|
width: 25,
|
||||||
height: 25,
|
height: 25,
|
||||||
idle: {
|
idle: {
|
||||||
@ -328,13 +352,14 @@ export class CustomizeScene extends AbstractCharacterScene {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private handleCustomWokaPreviewerOnResize(): void {
|
private handleCustomWokaPreviewerOnResize(): void {
|
||||||
|
const ratio = innerHeight / innerWidth;
|
||||||
this.customWokaPreviewer.x = this.cameras.main.worldView.x + this.cameras.main.width / 2;
|
this.customWokaPreviewer.x = this.cameras.main.worldView.x + this.cameras.main.width / 2;
|
||||||
this.customWokaPreviewer.y = this.customWokaPreviewer.displayHeight * 0.5 + 10;
|
this.customWokaPreviewer.y = this.customWokaPreviewer.displayHeight * 0.5 + (ratio > 1.6 ? 40 : 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleBodyPartButtonsOnResize(): void {
|
private handleBodyPartButtonsOnResize(): void {
|
||||||
const ratio = innerHeight / innerWidth;
|
const ratio = innerHeight / innerWidth;
|
||||||
const slotDimension = 50;
|
const slotDimension = WokaBodyPartSlot.SIZE;
|
||||||
|
|
||||||
for (const part in this.bodyPartsButtons) {
|
for (const part in this.bodyPartsButtons) {
|
||||||
this.bodyPartsButtons[part as CustomWokaBodyPart].setDisplaySize(slotDimension, slotDimension);
|
this.bodyPartsButtons[part as CustomWokaBodyPart].setDisplaySize(slotDimension, slotDimension);
|
||||||
@ -421,7 +446,7 @@ export class CustomizeScene extends AbstractCharacterScene {
|
|||||||
|
|
||||||
private handleRandomizeButtonOnResize(): void {
|
private handleRandomizeButtonOnResize(): void {
|
||||||
const x =
|
const x =
|
||||||
this.customWokaPreviewer.x +
|
this.customWokaPreviewer.x -
|
||||||
(this.customWokaPreviewer.displayWidth - this.randomizeButton.displayWidth) * 0.5;
|
(this.customWokaPreviewer.displayWidth - this.randomizeButton.displayWidth) * 0.5;
|
||||||
const y =
|
const y =
|
||||||
this.customWokaPreviewer.y +
|
this.customWokaPreviewer.y +
|
||||||
@ -432,7 +457,7 @@ export class CustomizeScene extends AbstractCharacterScene {
|
|||||||
|
|
||||||
private handleFinishButtonOnResize(): void {
|
private handleFinishButtonOnResize(): void {
|
||||||
const x =
|
const x =
|
||||||
this.customWokaPreviewer.x -
|
this.customWokaPreviewer.x +
|
||||||
(this.customWokaPreviewer.displayWidth - this.randomizeButton.displayWidth) * 0.5;
|
(this.customWokaPreviewer.displayWidth - this.randomizeButton.displayWidth) * 0.5;
|
||||||
const y =
|
const y =
|
||||||
this.customWokaPreviewer.y +
|
this.customWokaPreviewer.y +
|
||||||
|
@ -13,6 +13,5 @@ export class EmptyScene extends Scene {
|
|||||||
|
|
||||||
create() {}
|
create() {}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
update(time: number, delta: number): void {}
|
update(time: number, delta: number): void {}
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,6 @@ export class EnableCameraScene extends ResizableScene {
|
|||||||
|
|
||||||
public onResize(): void {}
|
public onResize(): void {}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
update(time: number, delta: number): void {}
|
update(time: number, delta: number): void {}
|
||||||
|
|
||||||
public login(): void {
|
public login(): void {
|
||||||
|
@ -5,6 +5,7 @@ import { waScaleManager } from "../Services/WaScaleManager";
|
|||||||
import { ReconnectingTextures } from "../Reconnecting/ReconnectingScene";
|
import { ReconnectingTextures } from "../Reconnecting/ReconnectingScene";
|
||||||
import { localeDetector } from "../../i18n/locales";
|
import { localeDetector } from "../../i18n/locales";
|
||||||
import { errorScreenStore } from "../../Stores/ErrorScreenStore";
|
import { errorScreenStore } from "../../Stores/ErrorScreenStore";
|
||||||
|
import { isErrorApiData } from "../../Messages/JsonMessages/ErrorApiData";
|
||||||
|
|
||||||
export const EntrySceneName = "EntryScene";
|
export const EntrySceneName = "EntryScene";
|
||||||
|
|
||||||
@ -45,8 +46,11 @@ export class EntryScene extends Scene {
|
|||||||
this.scene.start(nextSceneName);
|
this.scene.start(nextSceneName);
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
if (err.response.data?.code) {
|
const errorType = isErrorApiData.safeParse(err?.response?.data);
|
||||||
errorScreenStore.setError(err.response.data);
|
if (errorType.success) {
|
||||||
|
if (errorType.data.type === "redirect") {
|
||||||
|
window.location.assign(errorType.data.urlToRedirect);
|
||||||
|
} else errorScreenStore.setError(err?.response?.data);
|
||||||
} else {
|
} else {
|
||||||
ErrorScene.showError(err, this.scene);
|
ErrorScene.showError(err, this.scene);
|
||||||
}
|
}
|
||||||
|
@ -49,7 +49,6 @@ export class LoginScene extends ResizableScene {
|
|||||||
loginSceneVisibleStore.set(false);
|
loginSceneVisibleStore.set(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
update(time: number, delta: number): void {}
|
update(time: number, delta: number): void {}
|
||||||
|
|
||||||
public onResize(): void {}
|
public onResize(): void {}
|
||||||
|
@ -1,58 +0,0 @@
|
|||||||
import { SelectCharacterScene } from "./SelectCharacterScene";
|
|
||||||
|
|
||||||
export class SelectCharacterMobileScene extends SelectCharacterScene {
|
|
||||||
create() {
|
|
||||||
super.create();
|
|
||||||
this.onResize();
|
|
||||||
this.selectedRectangle.destroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected defineSetupPlayer(num: number) {
|
|
||||||
const deltaX = 30;
|
|
||||||
const deltaY = 2;
|
|
||||||
let [playerX, playerY] = this.getCharacterPosition();
|
|
||||||
let playerVisible = true;
|
|
||||||
let playerScale = 1.5;
|
|
||||||
let playerOpacity = 1;
|
|
||||||
|
|
||||||
if (this.currentSelectUser !== num) {
|
|
||||||
playerVisible = false;
|
|
||||||
}
|
|
||||||
if (num === this.currentSelectUser + 1) {
|
|
||||||
playerY -= deltaY;
|
|
||||||
playerX += deltaX;
|
|
||||||
playerScale = 0.8;
|
|
||||||
playerOpacity = 0.6;
|
|
||||||
playerVisible = true;
|
|
||||||
}
|
|
||||||
if (num === this.currentSelectUser + 2) {
|
|
||||||
playerY -= deltaY;
|
|
||||||
playerX += deltaX * 2;
|
|
||||||
playerScale = 0.8;
|
|
||||||
playerOpacity = 0.6;
|
|
||||||
playerVisible = true;
|
|
||||||
}
|
|
||||||
if (num === this.currentSelectUser - 1) {
|
|
||||||
playerY -= deltaY;
|
|
||||||
playerX -= deltaX;
|
|
||||||
playerScale = 0.8;
|
|
||||||
playerOpacity = 0.6;
|
|
||||||
playerVisible = true;
|
|
||||||
}
|
|
||||||
if (num === this.currentSelectUser - 2) {
|
|
||||||
playerY -= deltaY;
|
|
||||||
playerX -= deltaX * 2;
|
|
||||||
playerScale = 0.8;
|
|
||||||
playerOpacity = 0.6;
|
|
||||||
playerVisible = true;
|
|
||||||
}
|
|
||||||
return { playerX, playerY, playerScale, playerOpacity, playerVisible };
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns pixel position by on column and row number
|
|
||||||
*/
|
|
||||||
protected getCharacterPosition(): [number, number] {
|
|
||||||
return [this.game.renderer.width / 2, this.game.renderer.height / 3];
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,5 +1,4 @@
|
|||||||
import { gameManager } from "../Game/GameManager";
|
import { gameManager } from "../Game/GameManager";
|
||||||
import Rectangle = Phaser.GameObjects.Rectangle;
|
|
||||||
import { EnableCameraSceneName } from "./EnableCameraScene";
|
import { EnableCameraSceneName } from "./EnableCameraScene";
|
||||||
import { CustomizeSceneName } from "./CustomizeScene";
|
import { CustomizeSceneName } from "./CustomizeScene";
|
||||||
import { localUserStore } from "../../Connexion/LocalUserStore";
|
import { localUserStore } from "../../Connexion/LocalUserStore";
|
||||||
@ -13,25 +12,26 @@ import { PinchManager } from "../UserInput/PinchManager";
|
|||||||
import { selectCharacterSceneVisibleStore } from "../../Stores/SelectCharacterStore";
|
import { selectCharacterSceneVisibleStore } from "../../Stores/SelectCharacterStore";
|
||||||
import { waScaleManager } from "../Services/WaScaleManager";
|
import { waScaleManager } from "../Services/WaScaleManager";
|
||||||
import { analyticsClient } from "../../Administration/AnalyticsClient";
|
import { analyticsClient } from "../../Administration/AnalyticsClient";
|
||||||
import { isMediaBreakpointUp } from "../../Utils/BreakpointsUtils";
|
|
||||||
import { PUSHER_URL } from "../../Enum/EnvironmentVariable";
|
import { PUSHER_URL } from "../../Enum/EnvironmentVariable";
|
||||||
import { customizeAvailableStore } from "../../Stores/SelectCharacterSceneStore";
|
import { customizeAvailableStore, selectedCollection } from "../../Stores/SelectCharacterSceneStore";
|
||||||
|
import { DraggableGrid } from "@home-based-studio/phaser3-utils";
|
||||||
|
import { WokaSlot } from "../Components/SelectWoka/WokaSlot";
|
||||||
|
import { DraggableGridEvent } from "@home-based-studio/phaser3-utils/lib/utils/gui/containers/grids/DraggableGrid";
|
||||||
import { wokaList } from "../../Messages/JsonMessages/PlayerTextures";
|
import { wokaList } from "../../Messages/JsonMessages/PlayerTextures";
|
||||||
|
import { myCameraVisibilityStore } from "../../Stores/MyCameraStoreVisibility";
|
||||||
|
|
||||||
//todo: put this constants in a dedicated file
|
//todo: put this constants in a dedicated file
|
||||||
export const SelectCharacterSceneName = "SelectCharacterScene";
|
export const SelectCharacterSceneName = "SelectCharacterScene";
|
||||||
|
|
||||||
export class SelectCharacterScene extends AbstractCharacterScene {
|
export class SelectCharacterScene extends AbstractCharacterScene {
|
||||||
protected readonly nbCharactersPerRow = 6;
|
protected selectedWoka!: Phaser.GameObjects.Sprite | null; // null if we are selecting the "customize" option
|
||||||
protected selectedPlayer!: Phaser.Physics.Arcade.Sprite | null; // null if we are selecting the "customize" option
|
|
||||||
protected players: Array<Phaser.Physics.Arcade.Sprite> = new Array<Phaser.Physics.Arcade.Sprite>();
|
|
||||||
protected playerModels!: BodyResourceDescriptionInterface[];
|
protected playerModels!: BodyResourceDescriptionInterface[];
|
||||||
|
|
||||||
protected selectedRectangle!: Rectangle;
|
private charactersDraggableGrid!: DraggableGrid;
|
||||||
|
private collectionKeys!: string[];
|
||||||
protected currentSelectUser = 0;
|
private selectedCollectionIndex!: number;
|
||||||
protected pointerClicked: boolean = false;
|
private selectedGridItemIndex?: number;
|
||||||
protected pointerTimer: number = 0;
|
private gridRowsCount: number = 1;
|
||||||
|
|
||||||
protected lazyloadingAttempt = true; //permit to update texture loaded after renderer
|
protected lazyloadingAttempt = true; //permit to update texture loaded after renderer
|
||||||
private loader: Loader;
|
private loader: Loader;
|
||||||
@ -44,7 +44,8 @@ export class SelectCharacterScene extends AbstractCharacterScene {
|
|||||||
this.playerTextures = new PlayerTextures();
|
this.playerTextures = new PlayerTextures();
|
||||||
}
|
}
|
||||||
|
|
||||||
preload() {
|
public preload() {
|
||||||
|
super.preload();
|
||||||
const wokaMetadataKey = "woka-list" + gameManager.currentStartedRoom.href;
|
const wokaMetadataKey = "woka-list" + gameManager.currentStartedRoom.href;
|
||||||
this.cache.json.remove(wokaMetadataKey);
|
this.cache.json.remove(wokaMetadataKey);
|
||||||
|
|
||||||
@ -67,228 +68,237 @@ export class SelectCharacterScene extends AbstractCharacterScene {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
.catch((e) => console.error(e));
|
.catch((e) => console.error(e));
|
||||||
|
this.playerModels = loadAllDefaultModels(this.load, this.playerTextures);
|
||||||
|
this.lazyloadingAttempt = false;
|
||||||
|
|
||||||
//this function must stay at the end of preload function
|
//this function must stay at the end of preload function
|
||||||
this.loader.addLoader();
|
this.loader.addLoader();
|
||||||
}
|
}
|
||||||
|
|
||||||
create() {
|
public create() {
|
||||||
|
waScaleManager.zoomModifier = 1;
|
||||||
|
this.selectedWoka = null;
|
||||||
|
this.selectedCollectionIndex = 0;
|
||||||
|
this.collectionKeys = this.playerTextures.getCollectionsKeys();
|
||||||
|
selectedCollection.set(this.getSelectedCollectionName());
|
||||||
|
|
||||||
customizeAvailableStore.set(this.isCustomizationAvailable());
|
customizeAvailableStore.set(this.isCustomizationAvailable());
|
||||||
selectCharacterSceneVisibleStore.set(true);
|
selectCharacterSceneVisibleStore.set(true);
|
||||||
this.events.addListener("wake", () => {
|
|
||||||
waScaleManager.saveZoom();
|
|
||||||
waScaleManager.zoomModifier = isMediaBreakpointUp("md") ? 2 : 1;
|
|
||||||
selectCharacterSceneVisibleStore.set(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (touchScreenManager.supportTouchScreen) {
|
if (touchScreenManager.supportTouchScreen) {
|
||||||
new PinchManager(this);
|
new PinchManager(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
waScaleManager.saveZoom();
|
this.charactersDraggableGrid = new DraggableGrid(this, {
|
||||||
waScaleManager.zoomModifier = isMediaBreakpointUp("md") ? 2 : 1;
|
position: { x: 0, y: 0 },
|
||||||
|
maskPosition: { x: 0, y: 0 },
|
||||||
const rectangleXStart = this.game.renderer.width / 2 - (this.nbCharactersPerRow / 2) * 32 + 16;
|
dimension: { x: 485, y: 165 },
|
||||||
this.selectedRectangle = this.add.rectangle(rectangleXStart, 90, 32, 32).setStrokeStyle(2, 0xffffff);
|
horizontal: true,
|
||||||
this.selectedRectangle.setDepth(2);
|
repositionToCenter: true,
|
||||||
|
itemsInRow: 1,
|
||||||
/*create user*/
|
margin: {
|
||||||
this.createCurrentPlayer();
|
left: ((innerWidth - 200) / waScaleManager.getActualZoom()) * 0.5,
|
||||||
|
right: ((innerWidth - 200) / waScaleManager.getActualZoom()) * 0.5,
|
||||||
this.input.keyboard.on("keyup-ENTER", () => {
|
},
|
||||||
return this.nextSceneToCameraScene();
|
spacing: 5,
|
||||||
|
debug: {
|
||||||
|
showDraggableSpace: false,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
this.input.keyboard.on("keydown-RIGHT", () => {
|
this.bindEventHandlers();
|
||||||
this.moveToRight();
|
|
||||||
});
|
this.onResize();
|
||||||
this.input.keyboard.on("keydown-LEFT", () => {
|
|
||||||
this.moveToLeft();
|
|
||||||
});
|
|
||||||
this.input.keyboard.on("keydown-UP", () => {
|
|
||||||
this.moveToUp();
|
|
||||||
});
|
|
||||||
this.input.keyboard.on("keydown-DOWN", () => {
|
|
||||||
this.moveToDown();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public nextSceneToCameraScene(): void {
|
public nextSceneToCameraScene(): void {
|
||||||
if (this.selectedPlayer !== null && !areCharacterLayersValid([this.selectedPlayer.texture.key])) {
|
if (this.selectedWoka !== null && !areCharacterLayersValid([this.selectedWoka.texture.key])) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!this.selectedPlayer) {
|
if (!this.selectedWoka) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
analyticsClient.validationWoka("SelectWoka");
|
analyticsClient.validationWoka("SelectWoka");
|
||||||
|
|
||||||
|
gameManager.setCharacterLayers([this.selectedWoka.texture.key]);
|
||||||
|
this.selectedWoka = null;
|
||||||
this.scene.stop(SelectCharacterSceneName);
|
this.scene.stop(SelectCharacterSceneName);
|
||||||
waScaleManager.restoreZoom();
|
|
||||||
gameManager.setCharacterLayers([this.selectedPlayer.texture.key]);
|
|
||||||
gameManager.tryResumingGame(EnableCameraSceneName);
|
gameManager.tryResumingGame(EnableCameraSceneName);
|
||||||
this.players = [];
|
|
||||||
selectCharacterSceneVisibleStore.set(false);
|
selectCharacterSceneVisibleStore.set(false);
|
||||||
this.events.removeListener("wake");
|
this.events.removeListener("wake");
|
||||||
}
|
}
|
||||||
|
|
||||||
public nextSceneToCustomizeScene(): void {
|
public nextSceneToCustomizeScene(): void {
|
||||||
if (this.selectedPlayer !== null && !areCharacterLayersValid([this.selectedPlayer.texture.key])) {
|
if (this.selectedWoka !== null && !areCharacterLayersValid([this.selectedWoka.texture.key])) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
this.selectedWoka = null;
|
||||||
|
myCameraVisibilityStore.set(false);
|
||||||
this.scene.sleep(SelectCharacterSceneName);
|
this.scene.sleep(SelectCharacterSceneName);
|
||||||
waScaleManager.restoreZoom();
|
|
||||||
this.scene.run(CustomizeSceneName);
|
this.scene.run(CustomizeSceneName);
|
||||||
selectCharacterSceneVisibleStore.set(false);
|
selectCharacterSceneVisibleStore.set(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
createCurrentPlayer(): void {
|
public update(): void {
|
||||||
for (let i = 0; i < this.playerModels.length; i++) {
|
|
||||||
const playerResource = this.playerModels[i];
|
|
||||||
//check already exist texture
|
|
||||||
if (this.players.find((c) => c.texture.key === playerResource.id)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const [middleX, middleY] = this.getCharacterPosition();
|
|
||||||
const player = this.physics.add.sprite(middleX, middleY, playerResource.id, 0);
|
|
||||||
this.setUpPlayer(player, i);
|
|
||||||
this.anims.create({
|
|
||||||
key: playerResource.id,
|
|
||||||
frames: this.anims.generateFrameNumbers(playerResource.id, { start: 0, end: 11 }),
|
|
||||||
frameRate: 8,
|
|
||||||
repeat: -1,
|
|
||||||
});
|
|
||||||
player.setInteractive().on("pointerdown", () => {
|
|
||||||
if (this.pointerClicked) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (this.currentSelectUser === i) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
//To not trigger two time the pointerdown events :
|
|
||||||
// We set a boolean to true so that pointerdown events does nothing when the boolean is true
|
|
||||||
// We set a timer that we decrease in update function to not trigger the pointerdown events twice
|
|
||||||
this.pointerClicked = true;
|
|
||||||
this.pointerTimer = 250;
|
|
||||||
this.currentSelectUser = i;
|
|
||||||
this.moveUser();
|
|
||||||
});
|
|
||||||
this.players.push(player);
|
|
||||||
}
|
|
||||||
if (this.currentSelectUser >= this.players.length) {
|
|
||||||
this.currentSelectUser = 0;
|
|
||||||
}
|
|
||||||
this.selectedPlayer = this.players[this.currentSelectUser];
|
|
||||||
this.selectedPlayer.play(this.playerModels[this.currentSelectUser].id);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected moveUser() {
|
|
||||||
for (let i = 0; i < this.players.length; i++) {
|
|
||||||
const player = this.players[i];
|
|
||||||
this.setUpPlayer(player, i);
|
|
||||||
}
|
|
||||||
this.updateSelectedPlayer();
|
|
||||||
}
|
|
||||||
|
|
||||||
public moveToLeft() {
|
|
||||||
if (this.currentSelectUser === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.currentSelectUser -= 1;
|
|
||||||
this.moveUser();
|
|
||||||
}
|
|
||||||
|
|
||||||
public moveToRight() {
|
|
||||||
if (this.currentSelectUser === this.players.length - 1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.currentSelectUser += 1;
|
|
||||||
this.moveUser();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected moveToUp() {
|
|
||||||
if (this.currentSelectUser < this.nbCharactersPerRow) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.currentSelectUser -= this.nbCharactersPerRow;
|
|
||||||
this.moveUser();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected moveToDown() {
|
|
||||||
if (this.currentSelectUser + this.nbCharactersPerRow > this.players.length - 1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.currentSelectUser += this.nbCharactersPerRow;
|
|
||||||
this.moveUser();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected defineSetupPlayer(num: number) {
|
|
||||||
const deltaX = 32;
|
|
||||||
const deltaY = 32;
|
|
||||||
let [playerX, playerY] = this.getCharacterPosition(); // player X and player y are middle of the
|
|
||||||
|
|
||||||
playerX = playerX - deltaX * 2.5 + deltaX * (num % this.nbCharactersPerRow); // calcul position on line users
|
|
||||||
playerY = playerY - deltaY * 2 + deltaY * Math.floor(num / this.nbCharactersPerRow); // calcul position on column users
|
|
||||||
|
|
||||||
const playerVisible = true;
|
|
||||||
const playerScale = 1;
|
|
||||||
const playerOpacity = 1;
|
|
||||||
|
|
||||||
// if selected
|
|
||||||
if (num === this.currentSelectUser) {
|
|
||||||
this.selectedRectangle.setX(playerX);
|
|
||||||
this.selectedRectangle.setY(playerY);
|
|
||||||
}
|
|
||||||
|
|
||||||
return { playerX, playerY, playerScale, playerOpacity, playerVisible };
|
|
||||||
}
|
|
||||||
|
|
||||||
protected setUpPlayer(player: Phaser.Physics.Arcade.Sprite, num: number) {
|
|
||||||
const { playerX, playerY, playerScale, playerOpacity, playerVisible } = this.defineSetupPlayer(num);
|
|
||||||
player.setBounce(0.2);
|
|
||||||
player.setCollideWorldBounds(false);
|
|
||||||
player.setVisible(playerVisible);
|
|
||||||
player.setScale(playerScale, playerScale);
|
|
||||||
player.setAlpha(playerOpacity);
|
|
||||||
player.setX(playerX);
|
|
||||||
player.setY(playerY);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns pixel position by on column and row number
|
|
||||||
*/
|
|
||||||
protected getCharacterPosition(): [number, number] {
|
|
||||||
return [this.game.renderer.width / 2, this.game.renderer.height / 2.5];
|
|
||||||
}
|
|
||||||
|
|
||||||
protected updateSelectedPlayer(): void {
|
|
||||||
this.selectedPlayer?.anims?.pause(this.selectedPlayer?.anims.currentAnim.frames[0]);
|
|
||||||
const player = this.players[this.currentSelectUser];
|
|
||||||
player?.play(this.playerModels[this.currentSelectUser].id);
|
|
||||||
this.selectedPlayer = player;
|
|
||||||
localUserStore.setPlayerCharacterIndex(this.currentSelectUser);
|
|
||||||
}
|
|
||||||
|
|
||||||
update(time: number, delta: number): void {
|
|
||||||
// pointerTimer is set to 250 when pointerdown events is trigger
|
|
||||||
// After 250ms, pointerClicked is set to false and the pointerdown events can be trigger again
|
|
||||||
this.pointerTimer -= delta;
|
|
||||||
if (this.pointerTimer <= 0) {
|
|
||||||
this.pointerClicked = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.lazyloadingAttempt) {
|
if (this.lazyloadingAttempt) {
|
||||||
//re-render players list
|
|
||||||
this.createCurrentPlayer();
|
|
||||||
this.moveUser();
|
|
||||||
this.lazyloadingAttempt = false;
|
this.lazyloadingAttempt = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public onResize(): void {
|
public onResize(): void {
|
||||||
//move position of user
|
this.handleCharactersGridOnResize();
|
||||||
this.moveUser();
|
}
|
||||||
|
|
||||||
|
public getSelectedCollectionName(): string {
|
||||||
|
return this.collectionKeys[this.selectedCollectionIndex] ?? "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public getCollectionKeysSize(): number {
|
||||||
|
return this.playerTextures.getCollectionsKeys().length;
|
||||||
|
}
|
||||||
|
|
||||||
|
public selectPreviousCollection(): void {
|
||||||
|
this.selectedCollectionIndex = (this.selectedCollectionIndex + 1) % this.collectionKeys.length;
|
||||||
|
selectedCollection.set(this.getSelectedCollectionName());
|
||||||
|
this.populateGrid();
|
||||||
|
}
|
||||||
|
|
||||||
|
public selectNextCollection(): void {
|
||||||
|
if (this.collectionKeys.length === 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.selectedCollectionIndex =
|
||||||
|
this.selectedCollectionIndex - 1 < 0 ? this.collectionKeys.length - 1 : this.selectedCollectionIndex - 1;
|
||||||
|
selectedCollection.set(this.getSelectedCollectionName());
|
||||||
|
this.populateGrid();
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleCharactersGridOnResize(): void {
|
||||||
|
const ratio = innerHeight / innerWidth;
|
||||||
|
this.gridRowsCount = ratio > 1 || innerHeight > 900 ? 2 : 1;
|
||||||
|
const gridHeight = this.gridRowsCount === 2 ? 210 : 105;
|
||||||
|
const gridWidth = innerWidth / waScaleManager.getActualZoom();
|
||||||
|
const gridPos = {
|
||||||
|
x: this.cameras.main.worldView.x + this.cameras.main.width / 2,
|
||||||
|
y: this.cameras.main.worldView.y + this.cameras.main.height * (ratio > 1 ? 0.5 : 0.575),
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.charactersDraggableGrid.changeDraggableSpacePosAndSize(
|
||||||
|
gridPos,
|
||||||
|
{ x: gridWidth, y: gridHeight },
|
||||||
|
gridPos
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
console.warn(error);
|
||||||
|
}
|
||||||
|
this.charactersDraggableGrid.setItemsInRow(this.gridRowsCount);
|
||||||
|
this.populateGrid();
|
||||||
|
}
|
||||||
|
|
||||||
|
private populateGrid(): void {
|
||||||
|
const wokaDimension = 100;
|
||||||
|
|
||||||
|
this.selectedWoka = null;
|
||||||
|
this.charactersDraggableGrid.clearAllItems();
|
||||||
|
const textures = this.playerTextures.getWokaCollectionTextures(this.getSelectedCollectionName());
|
||||||
|
for (let i = 0; i < textures.length; i += 1) {
|
||||||
|
const slot = new WokaSlot(this, textures[i].id).setDisplaySize(wokaDimension, wokaDimension);
|
||||||
|
this.charactersDraggableGrid.addItem(slot);
|
||||||
|
}
|
||||||
|
this.charactersDraggableGrid.moveContentToBeginning();
|
||||||
|
void this.charactersDraggableGrid.moveContentTo(0.5, textures.length * 50);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bindEventHandlers(): void {
|
||||||
|
this.bindKeyboardEventHandlers();
|
||||||
|
this.events.addListener("wake", () => {
|
||||||
|
selectCharacterSceneVisibleStore.set(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.input.keyboard.on("keyup-ENTER", () => {
|
||||||
|
return this.nextSceneToCameraScene();
|
||||||
|
});
|
||||||
|
|
||||||
|
this.charactersDraggableGrid.on(DraggableGridEvent.ItemClicked, (item: WokaSlot) => {
|
||||||
|
this.selectGridItem(item);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private selectGridItem(item: WokaSlot): void {
|
||||||
|
this.selectedGridItemIndex = this.charactersDraggableGrid.getAllItems().indexOf(item);
|
||||||
|
if (this.charactersDraggableGrid.getDraggableSpaceWidth() < this.charactersDraggableGrid.getGridSize().x) {
|
||||||
|
void this.charactersDraggableGrid.centerOnItem(this.selectedGridItemIndex, 500);
|
||||||
|
}
|
||||||
|
this.charactersDraggableGrid.getAllItems().forEach((slot) => (slot as WokaSlot).select(false));
|
||||||
|
this.selectedWoka?.stop()?.setFrame(0);
|
||||||
|
this.selectedWoka = item.getSprite();
|
||||||
|
const wokaKey = this.selectedWoka.texture.key;
|
||||||
|
this.createWokaAnimation(wokaKey);
|
||||||
|
this.selectedWoka.play(wokaKey);
|
||||||
|
item.select(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bindKeyboardEventHandlers(): void {
|
||||||
|
this.input.keyboard.on("keyup-SPACE", () => {
|
||||||
|
this.selectNextCollection();
|
||||||
|
});
|
||||||
|
this.input.keyboard.on("keydown-LEFT", () => {
|
||||||
|
this.selectNextGridItem(true, true);
|
||||||
|
});
|
||||||
|
this.input.keyboard.on("keydown-RIGHT", () => {
|
||||||
|
this.selectNextGridItem(false, true);
|
||||||
|
});
|
||||||
|
this.input.keyboard.on("keydown-UP", () => {
|
||||||
|
this.selectNextGridItem(true, false);
|
||||||
|
});
|
||||||
|
this.input.keyboard.on("keydown-DOWN", () => {
|
||||||
|
this.selectNextGridItem(false, false);
|
||||||
|
});
|
||||||
|
this.input.keyboard.on("keydown-W", () => {
|
||||||
|
this.selectNextGridItem(true, false);
|
||||||
|
});
|
||||||
|
this.input.keyboard.on("keydown-S", () => {
|
||||||
|
this.selectNextGridItem(false, false);
|
||||||
|
});
|
||||||
|
this.input.keyboard.on("keydown-A", () => {
|
||||||
|
this.selectNextGridItem(true, true);
|
||||||
|
});
|
||||||
|
this.input.keyboard.on("keydown-D", () => {
|
||||||
|
this.selectNextGridItem(false, true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private selectNextGridItem(previous: boolean = false, horizontally: boolean): void {
|
||||||
|
if (this.selectedGridItemIndex === undefined) {
|
||||||
|
this.selectedGridItemIndex = 0;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
previous
|
||||||
|
? this.selectedGridItemIndex > 0
|
||||||
|
: this.selectedGridItemIndex < this.charactersDraggableGrid.getAllItems().length - 1
|
||||||
|
) {
|
||||||
|
// NOTE: getItemsInRowCount() not working properly. Fix on lib side needed
|
||||||
|
const jump = horizontally ? this.gridRowsCount : 1;
|
||||||
|
const item = this.charactersDraggableGrid.getAllItems()[
|
||||||
|
this.selectedGridItemIndex + (previous ? -jump : jump)
|
||||||
|
] as WokaSlot;
|
||||||
|
if (!item) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.selectedGridItemIndex += previous ? -1 : 1;
|
||||||
|
this.selectGridItem(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private createWokaAnimation(key: string): void {
|
||||||
|
this.anims.create({
|
||||||
|
key,
|
||||||
|
frames: this.anims.generateFrameNumbers(key, { start: 0, end: 11 }),
|
||||||
|
frameRate: 8,
|
||||||
|
repeat: -1,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private isCustomizationAvailable(): boolean {
|
private isCustomizationAvailable(): boolean {
|
||||||
|
@ -28,7 +28,6 @@ export class Player extends Character {
|
|||||||
companionTexturePromise?: CancelablePromise<string>
|
companionTexturePromise?: CancelablePromise<string>
|
||||||
) {
|
) {
|
||||||
super(Scene, x, y, texturesPromise, name, direction, moving, 1, true, companion, companionTexturePromise);
|
super(Scene, x, y, texturesPromise, name, direction, moving, 1, true, companion, companionTexturePromise);
|
||||||
this.statusDot.setVisible(false);
|
|
||||||
//the current player model should be push away by other players to prevent conflict
|
//the current player model should be push away by other players to prevent conflict
|
||||||
this.getBody().setImmovable(false);
|
this.getBody().setImmovable(false);
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,6 @@ export class GameSceneUserInputHandler implements UserInputHandlerInterface {
|
|||||||
gameObjects: Phaser.GameObjects.GameObject[],
|
gameObjects: Phaser.GameObjects.GameObject[],
|
||||||
deltaX: number,
|
deltaX: number,
|
||||||
deltaY: number,
|
deltaY: number,
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
deltaZ: number
|
deltaZ: number
|
||||||
): void {
|
): void {
|
||||||
this.gameScene.zoomByFactor(1 - (deltaY / 53) * 0.1);
|
this.gameScene.zoomByFactor(1 - (deltaY / 53) * 0.1);
|
||||||
@ -51,7 +50,6 @@ export class GameSceneUserInputHandler implements UserInputHandlerInterface {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
public handlePointerDownEvent(pointer: Phaser.Input.Pointer, gameObjects: Phaser.GameObjects.GameObject[]): void {}
|
public handlePointerDownEvent(pointer: Phaser.Input.Pointer, gameObjects: Phaser.GameObjects.GameObject[]): void {}
|
||||||
|
|
||||||
public handleSpaceKeyUpEvent(event: Event): Event {
|
public handleSpaceKeyUpEvent(event: Event): Event {
|
||||||
|
@ -87,7 +87,10 @@ function createChatMessagesStore() {
|
|||||||
return list;
|
return list;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
addExternalMessage(authorId: number, text: string) {
|
/**
|
||||||
|
* @param origin The iframe that originated this message (if triggered from the Scripting API), or undefined otherwise.
|
||||||
|
*/
|
||||||
|
addExternalMessage(authorId: number, text: string, origin?: Window) {
|
||||||
update((list) => {
|
update((list) => {
|
||||||
const lastMessage = list[list.length - 1];
|
const lastMessage = list[list.length - 1];
|
||||||
if (
|
if (
|
||||||
@ -106,7 +109,7 @@ function createChatMessagesStore() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
iframeListener.sendUserInputChat(text);
|
iframeListener.sendUserInputChat(text, origin);
|
||||||
return list;
|
return list;
|
||||||
});
|
});
|
||||||
chatVisibilityStore.set(true);
|
chatVisibilityStore.set(true);
|
||||||
|
@ -9,7 +9,9 @@ function createErrorScreenStore() {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
subscribe,
|
subscribe,
|
||||||
setError: (e: ErrorScreenMessage): void => set(e),
|
setError: (e: ErrorScreenMessage): void => {
|
||||||
|
set(e);
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@ import { peerStore } from "./PeerStore";
|
|||||||
import { privacyShutdownStore } from "./PrivacyShutdownStore";
|
import { privacyShutdownStore } from "./PrivacyShutdownStore";
|
||||||
import { MediaStreamConstraintsError } from "./Errors/MediaStreamConstraintsError";
|
import { MediaStreamConstraintsError } from "./Errors/MediaStreamConstraintsError";
|
||||||
import { SoundMeter } from "../Phaser/Components/SoundMeter";
|
import { SoundMeter } from "../Phaser/Components/SoundMeter";
|
||||||
|
import { AvailabilityStatus } from "../Messages/ts-proto-generated/protos/messages";
|
||||||
import deepEqual from "fast-deep-equal";
|
import deepEqual from "fast-deep-equal";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -179,10 +180,19 @@ function createVideoConstraintStore() {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
export const inJitsiStore = writable(false);
|
||||||
* A store containing if user is silent, so if he is in silent zone. This permit to show et hide camera of user
|
export const silentStore = writable(false);
|
||||||
*/
|
|
||||||
export const isSilentStore = writable(false);
|
export const availabilityStatusStore = derived(
|
||||||
|
[inJitsiStore, silentStore, privacyShutdownStore],
|
||||||
|
([$inJitsiStore, $silentStore, $privacyShutdownStore]) => {
|
||||||
|
if ($inJitsiStore) return AvailabilityStatus.JITSI;
|
||||||
|
if ($silentStore) return AvailabilityStatus.SILENT;
|
||||||
|
if ($privacyShutdownStore) return AvailabilityStatus.AWAY;
|
||||||
|
return AvailabilityStatus.ONLINE;
|
||||||
|
},
|
||||||
|
AvailabilityStatus.ONLINE
|
||||||
|
);
|
||||||
|
|
||||||
export const videoConstraintStore = createVideoConstraintStore();
|
export const videoConstraintStore = createVideoConstraintStore();
|
||||||
|
|
||||||
@ -241,7 +251,7 @@ export const mediaStreamConstraintsStore = derived(
|
|||||||
audioConstraintStore,
|
audioConstraintStore,
|
||||||
privacyShutdownStore,
|
privacyShutdownStore,
|
||||||
cameraEnergySavingStore,
|
cameraEnergySavingStore,
|
||||||
isSilentStore,
|
availabilityStatusStore,
|
||||||
],
|
],
|
||||||
(
|
(
|
||||||
[
|
[
|
||||||
@ -253,7 +263,7 @@ export const mediaStreamConstraintsStore = derived(
|
|||||||
$audioConstraintStore,
|
$audioConstraintStore,
|
||||||
$privacyShutdownStore,
|
$privacyShutdownStore,
|
||||||
$cameraEnergySavingStore,
|
$cameraEnergySavingStore,
|
||||||
$isSilentStore,
|
$availabilityStatusStore,
|
||||||
],
|
],
|
||||||
set
|
set
|
||||||
) => {
|
) => {
|
||||||
@ -310,7 +320,7 @@ export const mediaStreamConstraintsStore = derived(
|
|||||||
//currentAudioConstraint = false;
|
//currentAudioConstraint = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($isSilentStore === true) {
|
if ($availabilityStatusStore === AvailabilityStatus.SILENT) {
|
||||||
currentVideoConstraint = false;
|
currentVideoConstraint = false;
|
||||||
currentAudioConstraint = false;
|
currentAudioConstraint = false;
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ import { writable } from "svelte/store";
|
|||||||
import type { PlayerInterface } from "../Phaser/Game/PlayerInterface";
|
import type { PlayerInterface } from "../Phaser/Game/PlayerInterface";
|
||||||
import type { RoomConnection } from "../Connexion/RoomConnection";
|
import type { RoomConnection } from "../Connexion/RoomConnection";
|
||||||
import { getRandomColor } from "../WebRtc/ColorGenerator";
|
import { getRandomColor } from "../WebRtc/ColorGenerator";
|
||||||
|
import { AvailabilityStatus } from "../Messages/ts-proto-generated/protos/messages";
|
||||||
|
|
||||||
let idCount = 0;
|
let idCount = 0;
|
||||||
|
|
||||||
@ -28,7 +29,7 @@ function createPlayersStore() {
|
|||||||
visitCardUrl: message.visitCardUrl,
|
visitCardUrl: message.visitCardUrl,
|
||||||
companion: message.companion,
|
companion: message.companion,
|
||||||
userUuid: message.userUuid,
|
userUuid: message.userUuid,
|
||||||
away: message.away,
|
status: message.status,
|
||||||
color: getRandomColor(),
|
color: getRandomColor(),
|
||||||
});
|
});
|
||||||
return users;
|
return users;
|
||||||
@ -58,7 +59,7 @@ function createPlayersStore() {
|
|||||||
characterLayers: [],
|
characterLayers: [],
|
||||||
visitCardUrl: null,
|
visitCardUrl: null,
|
||||||
companion: null,
|
companion: null,
|
||||||
away: false,
|
status: AvailabilityStatus.ONLINE,
|
||||||
userUuid: "dummy",
|
userUuid: "dummy",
|
||||||
color: getRandomColor(),
|
color: getRandomColor(),
|
||||||
});
|
});
|
||||||
|
@ -2,6 +2,7 @@ import { derived, Readable, readable, writable } from "svelte/store";
|
|||||||
import { peerStore } from "./PeerStore";
|
import { peerStore } from "./PeerStore";
|
||||||
import type { LocalStreamStoreValue } from "./MediaStore";
|
import type { LocalStreamStoreValue } from "./MediaStore";
|
||||||
import { myCameraVisibilityStore } from "./MyCameraStoreVisibility";
|
import { myCameraVisibilityStore } from "./MyCameraStoreVisibility";
|
||||||
|
import type { DesktopCapturerSource } from "@wa-preload-app";
|
||||||
|
|
||||||
declare const navigator: any; // eslint-disable-line @typescript-eslint/no-explicit-any
|
declare const navigator: any; // eslint-disable-line @typescript-eslint/no-explicit-any
|
||||||
|
|
||||||
@ -91,6 +92,25 @@ export const screenSharingConstraintsStore = derived(
|
|||||||
} as MediaStreamConstraints
|
} as MediaStreamConstraints
|
||||||
);
|
);
|
||||||
|
|
||||||
|
async function getDesktopCapturerSources() {
|
||||||
|
showDesktopCapturerSourcePicker.set(true);
|
||||||
|
const source = await new Promise<DesktopCapturerSource | null>((resolve) => {
|
||||||
|
desktopCapturerSourcePromiseResolve = resolve;
|
||||||
|
});
|
||||||
|
if (source === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return navigator.mediaDevices.getUserMedia({
|
||||||
|
audio: false,
|
||||||
|
video: {
|
||||||
|
mandatory: {
|
||||||
|
chromeMediaSource: "desktop",
|
||||||
|
chromeMediaSourceId: source.id,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A store containing the MediaStream object for ScreenSharing (or null if nothing requested, or Error if an error occurred)
|
* A store containing the MediaStream object for ScreenSharing (or null if nothing requested, or Error if an error occurred)
|
||||||
*/
|
*/
|
||||||
@ -110,7 +130,9 @@ export const screenSharingLocalStreamStore = derived<Readable<MediaStreamConstra
|
|||||||
}
|
}
|
||||||
|
|
||||||
let currentStreamPromise: Promise<MediaStream>;
|
let currentStreamPromise: Promise<MediaStream>;
|
||||||
if (navigator.getDisplayMedia) {
|
if (window.WAD?.getDesktopCapturerSources) {
|
||||||
|
currentStreamPromise = getDesktopCapturerSources();
|
||||||
|
} else if (navigator.getDisplayMedia) {
|
||||||
currentStreamPromise = navigator.getDisplayMedia({ constraints });
|
currentStreamPromise = navigator.getDisplayMedia({ constraints });
|
||||||
} else if (navigator.mediaDevices && navigator.mediaDevices.getDisplayMedia) {
|
} else if (navigator.mediaDevices && navigator.mediaDevices.getDisplayMedia) {
|
||||||
currentStreamPromise = navigator.mediaDevices.getDisplayMedia({ constraints });
|
currentStreamPromise = navigator.mediaDevices.getDisplayMedia({ constraints });
|
||||||
@ -200,3 +222,7 @@ export const screenSharingLocalMedia = readable<ScreenSharingLocalMedia | null>(
|
|||||||
unsubscribe();
|
unsubscribe();
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const showDesktopCapturerSourcePicker = writable(false);
|
||||||
|
|
||||||
|
export let desktopCapturerSourcePromiseResolve: ((source: DesktopCapturerSource | null) => void) | undefined;
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
import { writable } from "svelte/store";
|
import { writable } from "svelte/store";
|
||||||
|
|
||||||
export const customizeAvailableStore = writable(false);
|
export const customizeAvailableStore = writable(false);
|
||||||
|
|
||||||
|
export const selectedCollection = writable<string>();
|
||||||
|
@ -14,15 +14,6 @@ export function getColorRgbFromHue(hue: number): { r: number; g: number; b: numb
|
|||||||
return hsv_to_rgb(hue, 0.5, 0.95);
|
return hsv_to_rgb(hue, 0.5, 0.95);
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
function stringToDouble(string: string): number {
|
|
||||||
let num = 1;
|
|
||||||
for (const char of string.split("")) {
|
|
||||||
num *= char.charCodeAt(0);
|
|
||||||
}
|
|
||||||
return (num % 255) / 255;
|
|
||||||
}
|
|
||||||
|
|
||||||
//todo: test this.
|
//todo: test this.
|
||||||
function hsv_to_rgb(hue: number, saturation: number, brightness: number): { r: number; g: number; b: number } {
|
function hsv_to_rgb(hue: number, saturation: number, brightness: number): { r: number; g: number; b: number } {
|
||||||
const h_i = Math.floor(hue * 6);
|
const h_i = Math.floor(hue * 6);
|
||||||
|
4
front/src/i18n/.gitignore
vendored
4
front/src/i18n/.gitignore
vendored
@ -1,3 +1 @@
|
|||||||
i18n-svelte.ts
|
i18n-*.ts
|
||||||
i18n-types.ts
|
|
||||||
i18n-util.ts
|
|
||||||
|
@ -16,8 +16,6 @@ import trigger from "./trigger";
|
|||||||
|
|
||||||
const de_DE: Translation = {
|
const de_DE: Translation = {
|
||||||
...(en_US as Translation),
|
...(en_US as Translation),
|
||||||
language: "Deutsch",
|
|
||||||
country: "Deutschland",
|
|
||||||
audio,
|
audio,
|
||||||
camera,
|
camera,
|
||||||
chat,
|
chat,
|
||||||
|
@ -14,8 +14,6 @@ import emoji from "./emoji";
|
|||||||
import trigger from "./trigger";
|
import trigger from "./trigger";
|
||||||
|
|
||||||
const en_US: BaseTranslation = {
|
const en_US: BaseTranslation = {
|
||||||
language: "English",
|
|
||||||
country: "United States",
|
|
||||||
audio,
|
audio,
|
||||||
camera,
|
camera,
|
||||||
chat,
|
chat,
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import type { AsyncFormattersInitializer } from "typesafe-i18n";
|
import type { FormattersInitializer } from "typesafe-i18n";
|
||||||
import type { Locales, Formatters } from "./i18n-types";
|
import type { Locales, Formatters } from "./i18n-types";
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/require-await
|
// eslint-disable-next-line @typescript-eslint/require-await
|
||||||
export const initFormatters: AsyncFormattersInitializer<Locales, Formatters> = async () => {
|
export const initFormatters: FormattersInitializer<Locales, Formatters> = async () => {
|
||||||
const formatters: Formatters = {
|
const formatters: Formatters = {
|
||||||
// add your formatter functions here
|
// add your formatter functions here
|
||||||
};
|
};
|
||||||
|
@ -16,8 +16,6 @@ import trigger from "./trigger";
|
|||||||
|
|
||||||
const fr_FR: Translation = {
|
const fr_FR: Translation = {
|
||||||
...(en_US as Translation),
|
...(en_US as Translation),
|
||||||
language: "Français",
|
|
||||||
country: "France",
|
|
||||||
audio,
|
audio,
|
||||||
camera,
|
camera,
|
||||||
chat,
|
chat,
|
||||||
|
@ -1,52 +1,44 @@
|
|||||||
import { detectLocale, navigatorDetector, initLocalStorageDetector } from "typesafe-i18n/detectors";
|
import { detectLocale, navigatorDetector, initLocalStorageDetector } from "typesafe-i18n/detectors";
|
||||||
import { FALLBACK_LOCALE } from "../Enum/EnvironmentVariable";
|
import { FALLBACK_LOCALE } from "../Enum/EnvironmentVariable";
|
||||||
import { initI18n, setLocale } from "./i18n-svelte";
|
import { setLocale } from "./i18n-svelte";
|
||||||
import type { Locales } from "./i18n-types";
|
import type { Locales } from "./i18n-types";
|
||||||
import { baseLocale, getTranslationForLocale, locales } from "./i18n-util";
|
import { baseLocale, locales } from "./i18n-util";
|
||||||
|
import { loadLocaleAsync } from "./i18n-util.async";
|
||||||
|
|
||||||
const fallbackLocale = FALLBACK_LOCALE || baseLocale;
|
const fallbackLocale = (FALLBACK_LOCALE || baseLocale) as Locales;
|
||||||
const localStorageProperty = "language";
|
const localStorageProperty = "language";
|
||||||
|
|
||||||
export const localeDetector = async () => {
|
export const localeDetector = async () => {
|
||||||
const exist = localStorage.getItem(localStorageProperty);
|
const exist = localStorage.getItem(localStorageProperty);
|
||||||
let detectedLocale: Locales = fallbackLocale as Locales;
|
let detectedLocale: Locales = fallbackLocale;
|
||||||
|
|
||||||
if (exist) {
|
if (exist) {
|
||||||
const localStorageDetector = initLocalStorageDetector(localStorageProperty);
|
const localStorageDetector = initLocalStorageDetector(localStorageProperty);
|
||||||
detectedLocale = detectLocale(fallbackLocale, locales, localStorageDetector) as Locales;
|
detectedLocale = detectLocale(fallbackLocale, locales, localStorageDetector);
|
||||||
} else {
|
} else {
|
||||||
detectedLocale = detectLocale(fallbackLocale, locales, navigatorDetector) as Locales;
|
detectedLocale = detectLocale(fallbackLocale, locales, navigatorDetector);
|
||||||
}
|
}
|
||||||
|
|
||||||
await initI18n(detectedLocale);
|
await setCurrentLocale(detectedLocale);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const setCurrentLocale = (locale: Locales) => {
|
export const setCurrentLocale = async (locale: Locales) => {
|
||||||
localStorage.setItem(localStorageProperty, locale);
|
localStorage.setItem(localStorageProperty, locale);
|
||||||
setLocale(locale).catch(() => {
|
await loadLocaleAsync(locale);
|
||||||
console.log("Cannot reload the locale!");
|
setLocale(locale);
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type DisplayableLocale = { id: Locales; language: string; country: string };
|
export const displayableLocales: { id: Locales; language: string; region: string }[] = locales.map((locale) => {
|
||||||
|
const [language, region] = locale.split("-");
|
||||||
|
|
||||||
function getDisplayableLocales() {
|
// backwards compatibility
|
||||||
const localesObject: DisplayableLocale[] = [];
|
if (!Intl.DisplayNames) {
|
||||||
locales.forEach((locale) => {
|
return { id: locale, language, region };
|
||||||
getTranslationForLocale(locale)
|
|
||||||
.then((translations) => {
|
|
||||||
localesObject.push({
|
|
||||||
id: locale,
|
|
||||||
language: translations.language,
|
|
||||||
country: translations.country,
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
console.log(error);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
return localesObject;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const displayableLocales = getDisplayableLocales();
|
return {
|
||||||
|
id: locale,
|
||||||
|
language: new Intl.DisplayNames(locale, { type: "language" }).of(language),
|
||||||
|
region: new Intl.DisplayNames(locale, { type: "region" }).of(region),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
@ -16,8 +16,6 @@ import trigger from "./trigger";
|
|||||||
|
|
||||||
const zh_CN: Translation = {
|
const zh_CN: Translation = {
|
||||||
...(en_US as Translation),
|
...(en_US as Translation),
|
||||||
language: "中文",
|
|
||||||
country: "中国",
|
|
||||||
audio,
|
audio,
|
||||||
camera,
|
camera,
|
||||||
chat,
|
chat,
|
||||||
|
@ -17,14 +17,12 @@ import { localUserStore } from "./Connexion/LocalUserStore";
|
|||||||
import { ErrorScene } from "./Phaser/Reconnecting/ErrorScene";
|
import { ErrorScene } from "./Phaser/Reconnecting/ErrorScene";
|
||||||
import { iframeListener } from "./Api/IframeListener";
|
import { iframeListener } from "./Api/IframeListener";
|
||||||
import { desktopApi } from "./Api/desktop/index";
|
import { desktopApi } from "./Api/desktop/index";
|
||||||
import { SelectCharacterMobileScene } from "./Phaser/Login/SelectCharacterMobileScene";
|
|
||||||
import { HdpiManager } from "./Phaser/Services/HdpiManager";
|
import { HdpiManager } from "./Phaser/Services/HdpiManager";
|
||||||
import { waScaleManager } from "./Phaser/Services/WaScaleManager";
|
import { waScaleManager } from "./Phaser/Services/WaScaleManager";
|
||||||
import { Game } from "./Phaser/Game/Game";
|
import { Game } from "./Phaser/Game/Game";
|
||||||
import App from "./Components/App.svelte";
|
import App from "./Components/App.svelte";
|
||||||
import { HtmlUtils } from "./WebRtc/HtmlUtils";
|
import { HtmlUtils } from "./WebRtc/HtmlUtils";
|
||||||
import WebGLRenderer = Phaser.Renderer.WebGL.WebGLRenderer;
|
import WebGLRenderer = Phaser.Renderer.WebGL.WebGLRenderer;
|
||||||
import { isMediaBreakpointUp } from "./Utils/BreakpointsUtils";
|
|
||||||
|
|
||||||
const { width, height } = coWebsiteManager.getGameSize();
|
const { width, height } = coWebsiteManager.getGameSize();
|
||||||
const valueGameQuality = localUserStore.getGameQualityValue();
|
const valueGameQuality = localUserStore.getGameQualityValue();
|
||||||
@ -91,7 +89,7 @@ const config: GameConfig = {
|
|||||||
scene: [
|
scene: [
|
||||||
EntryScene,
|
EntryScene,
|
||||||
LoginScene,
|
LoginScene,
|
||||||
isMediaBreakpointUp("md") ? SelectCharacterMobileScene : SelectCharacterScene,
|
SelectCharacterScene,
|
||||||
SelectCompanionScene,
|
SelectCompanionScene,
|
||||||
EnableCameraScene,
|
EnableCameraScene,
|
||||||
ReconnectingScene,
|
ReconnectingScene,
|
||||||
|
@ -92,7 +92,7 @@
|
|||||||
|
|
||||||
&-buffer {
|
&-buffer {
|
||||||
iframe {
|
iframe {
|
||||||
z-index: 45 !important;
|
z-index: 201 !important;
|
||||||
pointer-events: none !important;
|
pointer-events: none !important;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
border: 0;
|
border: 0;
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
"moduleResolution": "node",
|
"moduleResolution": "node",
|
||||||
//"module": "CommonJS",
|
//"module": "CommonJS",
|
||||||
"module": "ESNext",
|
"module": "ESNext",
|
||||||
"target": "ES2017",
|
"target": "ES2020",
|
||||||
"declaration": false,
|
"declaration": false,
|
||||||
"downlevelIteration": true,
|
"downlevelIteration": true,
|
||||||
"jsx": "react",
|
"jsx": "react",
|
||||||
|
@ -77,10 +77,10 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@geprog/vite-plugin-env-config/-/vite-plugin-env-config-4.0.3.tgz#ca04bd9ad9f55fe568917db79266afe8e766e25e"
|
resolved "https://registry.yarnpkg.com/@geprog/vite-plugin-env-config/-/vite-plugin-env-config-4.0.3.tgz#ca04bd9ad9f55fe568917db79266afe8e766e25e"
|
||||||
integrity sha512-2HDCV+6XXJjSuBAhDWLRr111buMQ3bIZrKo3dymIhEJ4oJCC/3yDqg7HDQIn8Y8KKbsM0AtuHMZW4yz2tPBsYg==
|
integrity sha512-2HDCV+6XXJjSuBAhDWLRr111buMQ3bIZrKo3dymIhEJ4oJCC/3yDqg7HDQIn8Y8KKbsM0AtuHMZW4yz2tPBsYg==
|
||||||
|
|
||||||
"@home-based-studio/phaser3-utils@^0.4.2":
|
"@home-based-studio/phaser3-utils@^0.4.7":
|
||||||
version "0.4.2"
|
version "0.4.7"
|
||||||
resolved "https://registry.yarnpkg.com/@home-based-studio/phaser3-utils/-/phaser3-utils-0.4.2.tgz#b2c1815a6b51321ea8dab027b5badcf714d99fd6"
|
resolved "https://registry.yarnpkg.com/@home-based-studio/phaser3-utils/-/phaser3-utils-0.4.7.tgz#d0464c81cb27328657d3fd048396f6936e200c48"
|
||||||
integrity sha512-S0VkAq3z0Kf0vEUUyCDes911icvc+nkUq7lGp23zD/5lk7LTGM51NswSAfel7Rm/DLY8IBxvDTBJADTf/De82w==
|
integrity sha512-gYt1mkuad85uzYwHK0+wp+mrsGASV4sRZPaHZHnO8A2ofTAnX36S3PcI+BqKchdJ0I7jvBQcfh0yp1Ug0BHT+A==
|
||||||
dependencies:
|
dependencies:
|
||||||
phaser "3.55.1"
|
phaser "3.55.1"
|
||||||
|
|
||||||
@ -806,10 +806,10 @@ debug@~3.1.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
ms "2.0.0"
|
ms "2.0.0"
|
||||||
|
|
||||||
deep-copy-ts@^0.5.0:
|
deep-copy-ts@^0.5.4:
|
||||||
version "0.5.0"
|
version "0.5.4"
|
||||||
resolved "https://registry.yarnpkg.com/deep-copy-ts/-/deep-copy-ts-0.5.0.tgz#b9493d8e2bae85ef7d659c16eb707c13efb84499"
|
resolved "https://registry.yarnpkg.com/deep-copy-ts/-/deep-copy-ts-0.5.4.tgz#e81b15797e4075cb3a690a1a7ac30179f2d72562"
|
||||||
integrity sha512-/3cgBcMkznRf5BM8wu6YWz3SQUkHzgh/v1TZFjevztLj9sMjFvNFBtpN4uUtPzw/rA/TldyD6c6LRL1zno4+YA==
|
integrity sha512-YJbPjw0YqdosorpCsa6copy1p/gJsFT9Q6Zq0tLi7D0nXh6Y/usjeIQZfkzV3HVuqY0Hl/5gM7TwgIbIWvEjlA==
|
||||||
|
|
||||||
deep-equal@^1.0.1:
|
deep-equal@^1.0.1:
|
||||||
version "1.1.1"
|
version "1.1.1"
|
||||||
@ -2991,10 +2991,10 @@ type-fest@^0.21.3:
|
|||||||
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37"
|
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37"
|
||||||
integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==
|
integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==
|
||||||
|
|
||||||
typesafe-i18n@^2.59.0:
|
typesafe-i18n@^5.4.0:
|
||||||
version "2.59.0"
|
version "5.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/typesafe-i18n/-/typesafe-i18n-2.59.0.tgz#09a9a32e61711418d927a389fa52e1c06a5fa5c4"
|
resolved "https://registry.yarnpkg.com/typesafe-i18n/-/typesafe-i18n-5.4.0.tgz#cab696160bb144c387d7cbd13f7a728aa8371777"
|
||||||
integrity sha512-Qv3Mrwmb8b73VNzQDPHPECzwymdBRVyDiZ3w2qnp4c2iv/7TGuiJegNHT/l3MooEN7IPbSpc5tbXw2x3MbGtFg==
|
integrity sha512-htewpld3FzZQv3Y1G31w54bofaaKR11MCkDK0FIYuXCpX72y1G6fkXUDslqzZCyVkZWRnIhY8leviNDxLwEzRw==
|
||||||
|
|
||||||
typescript@*:
|
typescript@*:
|
||||||
version "4.3.2"
|
version "4.3.2"
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
let menuIframeApi = undefined;
|
let menuIframeApi = undefined;
|
||||||
|
|
||||||
WA.ui.registerMenuCommand('custom callback menu', () => {
|
WA.ui.registerMenuCommand('custom callback menu', () => {
|
||||||
WA.nav.openTab("https://workadventu.re/");
|
WA.chat.sendChatMessage('Custom menu clicked', 'Mr Robot');
|
||||||
})
|
})
|
||||||
|
|
||||||
WA.ui.registerMenuCommand('custom iframe menu', {iframe: 'customIframeMenu.html'});
|
WA.ui.registerMenuCommand('custom iframe menu', {iframe: 'customIframeMenu.html'});
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user