Compare commits

..

No commits in common. "6983aeeb2d7d6658108af114c2f0e73d413352e7" and "1797d8f7ccc86d77690aed590aff8f322242fbff" have entirely different histories.

30 changed files with 46 additions and 398 deletions

View File

@ -17,11 +17,6 @@
- now use the 'Press Start 2P' font family and added an outline - now use the 'Press Start 2P' font family and added an outline
- As a result, we can now allow non-standard letters like french accents or chinese characters! - As a result, we can now allow non-standard letters like french accents or chinese characters!
- Added the contact card feature. (@Kharhamel)
- Click on another player to see its contact info.
- Premium-only feature unfortunately. I need to find a way to make it available for all.
- If no contact data is found (either because the user is anonymous or because no admin backend), display an error card.
- Mobile support has been improved - Mobile support has been improved
- WorkAdventure automatically sets the zoom level based on the viewport size to ensure a sensible size of the map is visible, whatever the viewport used - WorkAdventure automatically sets the zoom level based on the viewport size to ensure a sensible size of the map is visible, whatever the viewport used
- Mouse wheel support to zoom in / out - Mouse wheel support to zoom in / out

View File

@ -89,9 +89,6 @@ export class GameRoom {
public getUserByUuid(uuid: string): User|undefined { public getUserByUuid(uuid: string): User|undefined {
return this.usersByUuid.get(uuid); return this.usersByUuid.get(uuid);
} }
public getUserById(id: number): User|undefined {
return this.users.get(id);
}
public join(socket : UserSocket, joinRoomMessage: JoinRoomMessage): User { public join(socket : UserSocket, joinRoomMessage: JoinRoomMessage): User {
const positionMessage = joinRoomMessage.getPositionmessage(); const positionMessage = joinRoomMessage.getPositionmessage();

View File

@ -11,7 +11,7 @@ import {
JoinRoomMessage, JoinRoomMessage,
PlayGlobalMessage, PlayGlobalMessage,
PusherToBackMessage, PusherToBackMessage,
QueryJitsiJwtMessage, RefreshRoomPromptMessage, RequestVisitCardMessage, QueryJitsiJwtMessage, RefreshRoomPromptMessage,
ServerToAdminClientMessage, ServerToAdminClientMessage,
ServerToClientMessage, ServerToClientMessage,
SilentMessage, SilentMessage,
@ -74,8 +74,6 @@ const roomManager: IRoomManagerServer = {
socketManager.handleQueryJitsiJwtMessage(user, message.getQueryjitsijwtmessage() as QueryJitsiJwtMessage); socketManager.handleQueryJitsiJwtMessage(user, message.getQueryjitsijwtmessage() as QueryJitsiJwtMessage);
} else if (message.hasEmotepromptmessage()){ } else if (message.hasEmotepromptmessage()){
socketManager.handleEmoteEventMessage(room, user, message.getEmotepromptmessage() as EmotePromptMessage); socketManager.handleEmoteEventMessage(room, user, message.getEmotepromptmessage() as EmotePromptMessage);
} else if (message.hasRequestvisitcardmessage()) {
socketManager.handleRequestVisitCardMessage(room, user, message.getRequestvisitcardmessage() as RequestVisitCardMessage);
}else if (message.hasSendusermessage()) { }else if (message.hasSendusermessage()) {
const sendUserMessage = message.getSendusermessage(); const sendUserMessage = message.getSendusermessage();
if(sendUserMessage !== undefined) { if(sendUserMessage !== undefined) {

View File

@ -1,22 +0,0 @@
import {ADMIN_API_TOKEN, ADMIN_API_URL} from "../Enum/EnvironmentVariable";
import Axios from "axios";
class AdminApi {
fetchVisitCardUrl(membershipUuid: string): Promise<string> {
if (ADMIN_API_URL) {
return Axios.get(ADMIN_API_URL + '/api/membership/'+membershipUuid,
{headers: {"Authorization": `${ADMIN_API_TOKEN}`}}
).then((res) => {
return res.data;
}).catch(() => {
return 'INVALID';
});
} else {
return Promise.resolve('INVALID')
}
}
}
export const adminApi = new AdminApi();

View File

@ -27,7 +27,7 @@ import {
WorldFullWarningMessage, WorldFullWarningMessage,
UserLeftZoneMessage, UserLeftZoneMessage,
EmoteEventMessage, EmoteEventMessage,
BanUserMessage, RefreshRoomMessage, EmotePromptMessage, RequestVisitCardMessage, VisitCardMessage, BanUserMessage, RefreshRoomMessage, EmotePromptMessage,
} from "../Messages/generated/messages_pb"; } from "../Messages/generated/messages_pb";
import {User, UserSocket} from "../Model/User"; import {User, UserSocket} from "../Model/User";
import {ProtobufUtils} from "../Model/Websocket/ProtobufUtils"; import {ProtobufUtils} from "../Model/Websocket/ProtobufUtils";
@ -51,7 +51,6 @@ import {Zone} from "_Model/Zone";
import Debug from "debug"; import Debug from "debug";
import {Admin} from "_Model/Admin"; import {Admin} from "_Model/Admin";
import crypto from "crypto"; import crypto from "crypto";
import {adminApi} from "./AdminApi";
const debug = Debug('sockermanager'); const debug = Debug('sockermanager');
@ -770,21 +769,6 @@ export class SocketManager {
emoteEventMessage.setActoruserid(user.id); emoteEventMessage.setActoruserid(user.id);
room.emitEmoteEvent(user, emoteEventMessage); room.emitEmoteEvent(user, emoteEventMessage);
} }
async handleRequestVisitCardMessage(room: GameRoom, user: User, requestvisitcardmessage: RequestVisitCardMessage): Promise<void> {
const targetUser = room.getUserById(requestvisitcardmessage.getTargetuserid());
if (!targetUser) {
throw 'Could not find user for id '+requestvisitcardmessage.getTargetuserid();
}
const url = await adminApi.fetchVisitCardUrl(targetUser.uuid);
const visitCardMessage = new VisitCardMessage();
visitCardMessage.setUrl(url);
const clientMessage = new ServerToClientMessage();
clientMessage.setVisitcardmessage(visitCardMessage);
user.socket.write(clientMessage);
}
} }
export const socketManager = new SocketManager(); export const socketManager = new SocketManager();

View File

@ -112,6 +112,12 @@
<div id="activeScreenSharing" class="active-screen-sharing active"> <div id="activeScreenSharing" class="active-screen-sharing active">
</div> </div>
<audio id="audio-webrtc-in">
<source src="/resources/objects/webrtc-in.mp3" type="audio/mp3">
</audio>
<audio id="audio-webrtc-out">
<source src="/resources/objects/webrtc-out.mp3" type="audio/mp3">
</audio>
<audio id="report-message"> <audio id="report-message">
<source src="/resources/objects/report-message.mp3" type="audio/mp3"> <source src="/resources/objects/report-message.mp3" type="audio/mp3">
</audio> </audio>

View File

@ -44,13 +44,7 @@ export class TypeMessageExt implements TypeMessageInterface{
mainSectionDiv.appendChild(div); mainSectionDiv.appendChild(div);
const reportMessageAudio = HtmlUtils.getElementByIdOrFail<HTMLAudioElement>('report-message'); const reportMessageAudio = HtmlUtils.getElementByIdOrFail<HTMLAudioElement>('report-message');
// FIXME: this will fail on iOS reportMessageAudio.play();
// We should move the sound playing into the GameScene and listen to the event of a report using a store
try {
reportMessageAudio.play();
} catch (e) {
console.error(e);
}
this.nbSecond = this.maxNbSecond; this.nbSecond = this.maxNbSecond;
setTimeout((c) => { setTimeout((c) => {

View File

@ -9,20 +9,16 @@
import {selectCharacterSceneVisibleStore} from "../Stores/SelectCharacterStore"; import {selectCharacterSceneVisibleStore} from "../Stores/SelectCharacterStore";
import SelectCharacterScene from "./selectCharacter/SelectCharacterScene.svelte"; import SelectCharacterScene from "./selectCharacter/SelectCharacterScene.svelte";
import {customCharacterSceneVisibleStore} from "../Stores/CustomCharacterStore"; import {customCharacterSceneVisibleStore} from "../Stores/CustomCharacterStore";
import {errorStore} from "../Stores/ErrorStore";
import CustomCharacterScene from "./CustomCharacterScene/CustomCharacterScene.svelte"; import CustomCharacterScene from "./CustomCharacterScene/CustomCharacterScene.svelte";
import LoginScene from "./Login/LoginScene.svelte"; import LoginScene from "./Login/LoginScene.svelte";
import {loginSceneVisibleStore} from "../Stores/LoginSceneStore"; import {loginSceneVisibleStore} from "../Stores/LoginSceneStore";
import EnableCameraScene from "./EnableCamera/EnableCameraScene.svelte"; import EnableCameraScene from "./EnableCamera/EnableCameraScene.svelte";
import VisitCard from "./VisitCard/VisitCard.svelte";
import {requestVisitCardsStore} from "../Stores/GameStore";
import {Game} from "../Phaser/Game/Game"; import {Game} from "../Phaser/Game/Game";
import {helpCameraSettingsVisibleStore} from "../Stores/HelpCameraSettingsStore"; import {helpCameraSettingsVisibleStore} from "../Stores/HelpCameraSettingsStore";
import HelpCameraSettingsPopup from "./HelpCameraSettings/HelpCameraSettingsPopup.svelte"; import HelpCameraSettingsPopup from "./HelpCameraSettings/HelpCameraSettingsPopup.svelte";
import AudioPlaying from "./UI/AudioPlaying.svelte"; import AudioPlaying from "./UI/AudioPlaying.svelte";
import {soundPlayingStore} from "../Stores/SoundPlayingStore"; import {soundPlayingStore} from "../Stores/SoundPlayingStore";
import ErrorDialog from "./UI/ErrorDialog.svelte";
export let game: Game; export let game: Game;
</script> </script>
@ -77,12 +73,4 @@
<HelpCameraSettingsPopup game={ game }></HelpCameraSettingsPopup> <HelpCameraSettingsPopup game={ game }></HelpCameraSettingsPopup>
</div> </div>
{/if} {/if}
{#if $requestVisitCardsStore}
<VisitCard visitCardUrl={$requestVisitCardsStore}></VisitCard>
{/if}
{#if $errorStore.length > 0}
<div>
<ErrorDialog></ErrorDialog>
</div>
{/if}
</div> </div>

View File

@ -1,48 +0,0 @@
<script lang="ts">
import {errorStore} from "../../Stores/ErrorStore";
function close(): boolean {
errorStore.clearMessages();
return false;
}
</script>
<div class="error-div nes-container is-dark is-rounded" open>
<p class="nes-text is-error title">Error</p>
<div class="body">
{#each $errorStore as error}
<p>{error}</p>
{/each}
</div>
<div class="button-bar">
<button class="nes-btn is-error" on:click={close}>Close</button>
</div>
</div>
<style lang="scss">
div.error-div {
pointer-events: auto;
margin-top: 10vh;
margin-right: auto;
margin-left: auto;
width: max-content;
max-width: 80vw;
.button-bar {
text-align: center;
}
.body {
max-height: 50vh;
}
p {
font-family: "Press Start 2P";
&.title {
text-align: center;
}
}
}
</style>

View File

@ -1,64 +0,0 @@
<script lang="typescript">
import { fly } from 'svelte/transition';
import {requestVisitCardsStore} from "../../Stores/GameStore";
export let visitCardUrl: string;
function closeCard() {
requestVisitCardsStore.set(null);
}
</script>
<style lang="scss">
.visitCard {
pointer-events: all;
margin-left: auto;
margin-right: auto;
width: 530px;
margin-top: 200px;
max-width: 100vw;
.defaultCard {
border-radius: 5px;
border: 2px black solid;
background-color: whitesmoke;
width: 530px;
header {
padding: 5px;
}
}
iframe {
border: 0;
width: 530px;
height: 270px;
overflow: hidden;
}
button {
float: right;
}
}
</style>
<section class="visitCard" transition:fly="{{ y: -200, duration: 1000 }}">
{#if visitCardUrl === 'INVALID'}
<div class="defaultCard">
<header>
<h2>Sorry</h2>
<p style="font-style: italic;">This user doesn't have a contact card.</p>
</header>
<main style="padding: 5px; background-color: gray">
<p>Maybe he is offline, or this feature is deactivated.</p>
</main>
</div>
{:else}
<iframe title="visitCardTitle" src={visitCardUrl}></iframe>
{/if}
<div class="buttonContainer">
<button class="nes-btn is-popUpElement" on:click={closeCard}>Close</button>
</div>
</section>

View File

@ -30,7 +30,7 @@ import {
EmoteEventMessage, EmoteEventMessage,
EmotePromptMessage, EmotePromptMessage,
SendUserMessage, SendUserMessage,
BanUserMessage, RequestVisitCardMessage BanUserMessage
} from "../Messages/generated/messages_pb" } from "../Messages/generated/messages_pb"
import type {UserSimplePeerInterface} from "../WebRtc/SimplePeer"; import type {UserSimplePeerInterface} from "../WebRtc/SimplePeer";
@ -50,7 +50,6 @@ import {worldFullMessageStream} from "./WorldFullMessageStream";
import {worldFullWarningStream} from "./WorldFullWarningStream"; import {worldFullWarningStream} from "./WorldFullWarningStream";
import {connectionManager} from "./ConnectionManager"; import {connectionManager} from "./ConnectionManager";
import {emoteEventStream} from "./EmoteEventStream"; import {emoteEventStream} from "./EmoteEventStream";
import {requestVisitCardsStore} from "../Stores/GameStore";
const manualPingDelay = 20000; const manualPingDelay = 20000;
@ -204,8 +203,6 @@ export class RoomConnection implements RoomConnection {
adminMessagesService.onSendusermessage(message.getBanusermessage() as BanUserMessage); adminMessagesService.onSendusermessage(message.getBanusermessage() as BanUserMessage);
} else if (message.hasWorldfullwarningmessage()) { } else if (message.hasWorldfullwarningmessage()) {
worldFullWarningStream.onMessage(); worldFullWarningStream.onMessage();
} else if (message.hasVisitcardmessage()) {
requestVisitCardsStore.set(message?.getVisitcardmessage()?.getUrl() as unknown as string);
} else if (message.hasRefreshroommessage()) { } else if (message.hasRefreshroommessage()) {
//todo: implement a way to notify the user the room was refreshed. //todo: implement a way to notify the user the room was refreshed.
} else { } else {
@ -620,14 +617,4 @@ export class RoomConnection implements RoomConnection {
this.socket.send(clientToServerMessage.serializeBinary().buffer); this.socket.send(clientToServerMessage.serializeBinary().buffer);
} }
public requestVisitCardUrl(targetUserId: number): void {
const message = new RequestVisitCardMessage();
message.setTargetuserid(targetUserId);
const clientToServerMessage = new ClientToServerMessage();
clientToServerMessage.setRequestvisitcardmessage(message);
this.socket.send(clientToServerMessage.serializeBinary().buffer);
}
} }

View File

@ -3,8 +3,6 @@ import type {PointInterface} from "../../Connexion/ConnexionModels";
import {Character} from "../Entity/Character"; import {Character} from "../Entity/Character";
import type {PlayerAnimationDirections} from "../Player/Animation"; import type {PlayerAnimationDirections} from "../Player/Animation";
export const playerClickedEvent = 'playerClickedEvent';
/** /**
* Class representing the sprite of a remote player (a player that plays on another computer) * Class representing the sprite of a remote player (a player that plays on another computer)
*/ */
@ -27,10 +25,6 @@ export class RemotePlayer extends Character {
//set data //set data
this.userId = userId; this.userId = userId;
this.on('pointerdown', () => {
this.emit(playerClickedEvent, this.userId);
})
} }
updatePosition(position: PointInterface): void { updatePosition(position: PointInterface): void {
@ -46,6 +40,6 @@ export class RemotePlayer extends Character {
} }
isClickable(): boolean { isClickable(): boolean {
return true; //todo: make remote players clickable if they are logged in. return false; //todo: make remote players clickable if they are logged in.
} }
} }

View File

@ -31,7 +31,7 @@ import type {AddPlayerInterface} from "./AddPlayerInterface";
import {PlayerAnimationDirections} from "../Player/Animation"; import {PlayerAnimationDirections} from "../Player/Animation";
import {PlayerMovement} from "./PlayerMovement"; import {PlayerMovement} from "./PlayerMovement";
import {PlayersPositionInterpolator} from "./PlayersPositionInterpolator"; import {PlayersPositionInterpolator} from "./PlayersPositionInterpolator";
import {playerClickedEvent, RemotePlayer} from "../Entity/RemotePlayer"; import {RemotePlayer} from "../Entity/RemotePlayer";
import {Queue} from 'queue-typescript'; import {Queue} from 'queue-typescript';
import {SimplePeer, UserSimplePeerInterface} from "../../WebRtc/SimplePeer"; import {SimplePeer, UserSimplePeerInterface} from "../../WebRtc/SimplePeer";
import {ReconnectingSceneName} from "../Reconnecting/ReconnectingScene"; import {ReconnectingSceneName} from "../Reconnecting/ReconnectingScene";
@ -184,7 +184,6 @@ export class GameScene extends DirtyScene implements CenterListener {
private createPromise: Promise<void>; private createPromise: Promise<void>;
private createPromiseResolve!: (value?: void | PromiseLike<void>) => void; private createPromiseResolve!: (value?: void | PromiseLike<void>) => void;
private iframeSubscriptionList! : Array<Subscription>; private iframeSubscriptionList! : Array<Subscription>;
private peerStoreUnsubscribe!: () => void;
MapUrlFile: string; MapUrlFile: string;
RoomId: string; RoomId: string;
instance: string; instance: string;
@ -254,11 +253,6 @@ export class GameScene extends DirtyScene implements CenterListener {
this.load.image(joystickBaseKey, joystickBaseImg); this.load.image(joystickBaseKey, joystickBaseImg);
this.load.image(joystickThumbKey, joystickThumbImg); this.load.image(joystickThumbKey, joystickThumbImg);
} }
this.load.audio('audio-webrtc-in', '/resources/objects/webrtc-in.mp3');
this.load.audio('audio-webrtc-out', '/resources/objects/webrtc-out.mp3');
//this.load.audio('audio-report-message', '/resources/objects/report-message.mp3');
this.sound.pauseOnBlur = false;
this.load.on(FILE_LOAD_ERROR, (file: {src: string}) => { this.load.on(FILE_LOAD_ERROR, (file: {src: string}) => {
// If we happen to be in HTTP and we are trying to load a URL in HTTPS only... (this happens only in dev environments) // If we happen to be in HTTP and we are trying to load a URL in HTTPS only... (this happens only in dev environments)
if (window.location.protocol === 'http:' && file.src === this.MapUrlFile && file.src.startsWith('http:') && this.originalMapUrl === undefined) { if (window.location.protocol === 'http:' && file.src === this.MapUrlFile && file.src.startsWith('http:') && this.originalMapUrl === undefined) {
@ -563,21 +557,6 @@ export class GameScene extends DirtyScene implements CenterListener {
} }
this.emoteManager = new EmoteManager(this); this.emoteManager = new EmoteManager(this);
let oldPeerNumber = 0;
this.peerStoreUnsubscribe = peerStore.subscribe((peers) => {
const newPeerNumber = peers.size;
if (newPeerNumber > oldPeerNumber) {
this.sound.play('audio-webrtc-in', {
volume: 0.2
});
} else if (newPeerNumber < oldPeerNumber) {
this.sound.play('audio-webrtc-out', {
volume: 0.2
});
}
oldPeerNumber = newPeerNumber;
});
} }
/** /**
@ -1067,7 +1046,6 @@ ${escapedMessage}
this.userInputManager.destroy(); this.userInputManager.destroy();
this.pinchManager?.destroy(); this.pinchManager?.destroy();
this.emoteManager.destroy(); this.emoteManager.destroy();
this.peerStoreUnsubscribe();
mediaManager.hideGameOverlay(); mediaManager.hideGameOverlay();
@ -1477,9 +1455,6 @@ ${escapedMessage}
addPlayerData.companion, addPlayerData.companion,
addPlayerData.companion !== null ? lazyLoadCompanionResource(this.load, addPlayerData.companion) : undefined addPlayerData.companion !== null ? lazyLoadCompanionResource(this.load, addPlayerData.companion) : undefined
); );
player.on(playerClickedEvent, (userID:number) => {
this.connection?.requestVisitCardUrl(userID);
})
this.MapPlayers.add(player); this.MapPlayers.add(player);
this.MapPlayersByKey.set(player.userId, player); this.MapPlayersByKey.set(player.userId, player);
player.updatePosition(addPlayerData.position); player.updatePosition(addPlayerData.position);

View File

@ -236,9 +236,6 @@ export class CustomizeScene extends AbstractCharacterScene {
} }
} }
public onResize(): void { public onResize(): void {
this.moveLayers(); this.moveLayers();

View File

@ -244,7 +244,7 @@ export class SelectCharacterScene extends AbstractCharacterScene {
update(time: number, delta: number): void { update(time: number, delta: number): void {
if(this.lazyloadingAttempt){ if(this.lazyloadingAttempt){
this.moveUser(); this.createCurrentPlayer();
this.lazyloadingAttempt = false; this.lazyloadingAttempt = false;
} }
} }

View File

@ -60,6 +60,7 @@ class WaScaleManager {
public saveZoom(): void { public saveZoom(): void {
this._saveZoom = this.hdpiManager.zoomModifier; this._saveZoom = this.hdpiManager.zoomModifier;
console.log(this._saveZoom);
} }
public restoreZoom(): void{ public restoreZoom(): void{

View File

@ -1,33 +0,0 @@
import {writable} from "svelte/store";
/**
* A store that contains a list of error messages to be displayed.
*/
function createErrorStore() {
const { subscribe, set, update } = writable<string[]>([]);
return {
subscribe,
addErrorMessage: (e: string|Error): void => {
update((messages: string[]) => {
let message: string;
if (e instanceof Error) {
message = e.message;
} else {
message = e;
}
if (!messages.includes(message)) {
messages.push(message);
}
return messages;
});
},
clearMessages: (): void => {
set([]);
}
};
}
export const errorStore = createErrorStore();

View File

@ -1,8 +0,0 @@
export class BrowserTooOldError extends Error {
static NAME = 'BrowserTooOldError';
constructor() {
super('Unable to access your camera or microphone. Your browser is too old. Please consider upgrading your browser or try using a recent version of Chrome.');
this.name = BrowserTooOldError.NAME;
}
}

View File

@ -1,8 +0,0 @@
export class WebviewOnOldIOS extends Error {
static NAME = 'WebviewOnOldIOS';
constructor() {
super('Your iOS version cannot use video/audio in the browser unless you are using Safari. Please switch to Safari or upgrade iOS to 14.3 or above.');
this.name = WebviewOnOldIOS.NAME;
}
}

View File

@ -1,5 +1,3 @@
import { writable } from "svelte/store"; import { derived, writable, Writable } from "svelte/store";
export const userMovingStore = writable(false); export const userMovingStore = writable(false);
export const requestVisitCardsStore = writable<string|null>(null);

View File

@ -4,10 +4,6 @@ import {localUserStore} from "../Connexion/LocalUserStore";
import {ITiledMapGroupLayer, ITiledMapObjectLayer, ITiledMapTileLayer} from "../Phaser/Map/ITiledMap"; import {ITiledMapGroupLayer, ITiledMapObjectLayer, ITiledMapTileLayer} from "../Phaser/Map/ITiledMap";
import {userMovingStore} from "./GameStore"; import {userMovingStore} from "./GameStore";
import {HtmlUtils} from "../WebRtc/HtmlUtils"; import {HtmlUtils} from "../WebRtc/HtmlUtils";
import {BrowserTooOldError} from "./Errors/BrowserTooOldError";
import {errorStore} from "./ErrorStore";
import {isIOS} from "../WebRtc/DeviceUtils";
import {WebviewOnOldIOS} from "./Errors/WebviewOnOldIOS";
/** /**
* A store that contains the camera state requested by the user (on or off). * A store that contains the camera state requested by the user (on or off).
@ -423,17 +419,11 @@ export const localStreamStore = derived<Readable<MediaStreamConstraints>, LocalS
constraints constraints
}); });
return; return;
} else if (isIOS()) {
set({
type: 'error',
error: new WebviewOnOldIOS(),
constraints
});
return;
} else { } else {
//throw new Error('Unable to access your camera or microphone. Your browser is too old.');
set({ set({
type: 'error', type: 'error',
error: new BrowserTooOldError(), error: new Error('Unable to access your camera or microphone. Your browser is too old. Please consider upgrading your browser or try using a recent version of Chrome.'),
constraints constraints
}); });
return; return;
@ -604,11 +594,3 @@ microphoneListStore.subscribe((devices) => {
audioConstraintStore.setDeviceId(undefined); audioConstraintStore.setDeviceId(undefined);
} }
}); });
localStreamStore.subscribe(streamResult => {
if (streamResult.type === 'error') {
if (streamResult.error.name === BrowserTooOldError.NAME || streamResult.error.name === WebviewOnOldIOS.NAME) {
errorStore.addErrorMessage(streamResult.error);
}
}
});

View File

@ -11,7 +11,7 @@ enum iframeStates {
const cowebsiteDivId = 'cowebsite'; // the id of the whole container. const cowebsiteDivId = 'cowebsite'; // the id of the whole container.
const cowebsiteMainDomId = 'cowebsite-main'; // the id of the parent div of the iframe. const cowebsiteMainDomId = 'cowebsite-main'; // the id of the parent div of the iframe.
const cowebsiteAsideDomId = 'cowebsite-aside'; // the id of the parent div of the iframe. const cowebsiteAsideDomId = 'cowebsite-aside'; // the id of the parent div of the iframe.
export const cowebsiteCloseButtonId = 'cowebsite-close'; const cowebsiteCloseButtonId = 'cowebsite-close';
const cowebsiteFullScreenButtonId = 'cowebsite-fullscreen'; const cowebsiteFullScreenButtonId = 'cowebsite-fullscreen';
const cowebsiteOpenFullScreenImageId = 'cowebsite-fullscreen-open'; const cowebsiteOpenFullScreenImageId = 'cowebsite-fullscreen-open';
const cowebsiteCloseFullScreenImageId = 'cowebsite-fullscreen-close'; const cowebsiteCloseFullScreenImageId = 'cowebsite-fullscreen-close';
@ -64,15 +64,10 @@ class CoWebsiteManager {
this.initResizeListeners(); this.initResizeListeners();
const buttonCloseFrame = HtmlUtils.getElementByIdOrFail(cowebsiteCloseButtonId); HtmlUtils.getElementByIdOrFail(cowebsiteCloseButtonId).addEventListener('click', () => {
buttonCloseFrame.addEventListener('click', () => {
buttonCloseFrame.blur();
this.closeCoWebsite(); this.closeCoWebsite();
}); });
HtmlUtils.getElementByIdOrFail(cowebsiteFullScreenButtonId).addEventListener('click', () => {
const buttonFullScreenFrame = HtmlUtils.getElementByIdOrFail(cowebsiteFullScreenButtonId);
buttonFullScreenFrame.addEventListener('click', () => {
buttonFullScreenFrame.blur();
this.fullscreen(); this.fullscreen();
}); });
} }
@ -157,10 +152,7 @@ class CoWebsiteManager {
setTimeout(() => { setTimeout(() => {
this.fire(); this.fire();
}, animationTime) }, animationTime)
}).catch((err) => { }).catch(() => this.closeCoWebsite());
console.error('Error loadCoWebsite => ', err);
this.closeCoWebsite()
});
} }
/** /**
@ -174,10 +166,7 @@ class CoWebsiteManager {
setTimeout(() => { setTimeout(() => {
this.fire(); this.fire();
}, animationTime); }, animationTime);
}).catch((err) => { }).catch(() => this.closeCoWebsite());
console.error('Error insertCoWebsite => ', err);
this.closeCoWebsite();
});
} }
public closeCoWebsite(): Promise<void> { public closeCoWebsite(): Promise<void> {

View File

@ -1,12 +0,0 @@
export function isIOS(): boolean {
return [
'iPad Simulator',
'iPhone Simulator',
'iPod Simulator',
'iPad',
'iPhone',
'iPod'
].includes(navigator.platform)
// iPad on iOS 13 detection
|| (navigator.userAgent.includes("Mac") && "ontouchend" in document)
}

View File

@ -35,12 +35,7 @@ export class HtmlUtils {
const urlRegex = /(https?:\/\/[^\s]+)/g; const urlRegex = /(https?:\/\/[^\s]+)/g;
text = HtmlUtils.escapeHtml(text); text = HtmlUtils.escapeHtml(text);
return text.replace(urlRegex, (url: string) => { return text.replace(urlRegex, (url: string) => {
const link = document.createElement('a'); return '<a href="' + url + '" target="_blank" style=":visited {color: white}">' + url + '</a>';
link.href = url;
link.target = "_blank";
const text = document.createTextNode(url);
link.appendChild(text);
return link.outerHTML;
}); });
} }

View File

@ -21,12 +21,12 @@ export type ReportCallback = (message: string) => void;
export type ShowReportCallBack = (userId: string, userName: string|undefined) => void; export type ShowReportCallBack = (userId: string, userName: string|undefined) => void;
export type HelpCameraSettingsCallBack = () => void; export type HelpCameraSettingsCallBack = () => void;
import {cowebsiteCloseButtonId} from "./CoWebsiteManager";
export class MediaManager { export class MediaManager {
private remoteVideo: Map<string, HTMLVideoElement> = new Map<string, HTMLVideoElement>(); private remoteVideo: Map<string, HTMLVideoElement> = new Map<string, HTMLVideoElement>();
webrtcInAudio: HTMLAudioElement;
//FIX ME SOUNDMETER: check stalability of sound meter calculation //FIX ME SOUNDMETER: check stalability of sound meter calculation
//mySoundMeterElement: HTMLDivElement; //mySoundMeterElement: HTMLDivElement;
private webrtcOutAudio: HTMLAudioElement;
startScreenSharingCallBacks : Set<StartScreenSharingCallback> = new Set<StartScreenSharingCallback>(); startScreenSharingCallBacks : Set<StartScreenSharingCallback> = new Set<StartScreenSharingCallback>();
stopScreenSharingCallBacks : Set<StopScreenSharingCallback> = new Set<StopScreenSharingCallback>(); stopScreenSharingCallBacks : Set<StopScreenSharingCallback> = new Set<StopScreenSharingCallback>();
showReportModalCallBacks : Set<ShowReportCallBack> = new Set<ShowReportCallBack>(); showReportModalCallBacks : Set<ShowReportCallBack> = new Set<ShowReportCallBack>();
@ -44,6 +44,11 @@ export class MediaManager {
constructor() { constructor() {
this.webrtcInAudio = HtmlUtils.getElementByIdOrFail<HTMLAudioElement>('audio-webrtc-in');
this.webrtcOutAudio = HtmlUtils.getElementByIdOrFail<HTMLAudioElement>('audio-webrtc-out');
this.webrtcInAudio.volume = 0.2;
this.webrtcOutAudio.volume = 0.2;
this.pingCameraStatus(); this.pingCameraStatus();
//FIX ME SOUNDMETER: check stability of sound meter calculation //FIX ME SOUNDMETER: check stability of sound meter calculation
@ -65,7 +70,6 @@ export class MediaManager {
} }
}); });
let isScreenSharing = false;
screenSharingLocalStreamStore.subscribe((result) => { screenSharingLocalStreamStore.subscribe((result) => {
if (result.type === 'error') { if (result.type === 'error') {
console.error(result.error); console.error(result.error);
@ -76,14 +80,10 @@ export class MediaManager {
} }
if (result.stream !== null) { if (result.stream !== null) {
isScreenSharing = true;
this.addScreenSharingActiveVideo('me', DivImportance.Normal); this.addScreenSharingActiveVideo('me', DivImportance.Normal);
HtmlUtils.getElementByIdOrFail<HTMLVideoElement>('screen-sharing-me').srcObject = result.stream; HtmlUtils.getElementByIdOrFail<HTMLVideoElement>('screen-sharing-me').srcObject = result.stream;
} else { } else {
if (isScreenSharing) { this.removeActiveScreenSharingVideo('me');
isScreenSharing = false;
this.removeActiveScreenSharingVideo('me');
}
} }
}); });
@ -106,14 +106,11 @@ export class MediaManager {
const gameOverlay = HtmlUtils.getElementByIdOrFail('game-overlay'); const gameOverlay = HtmlUtils.getElementByIdOrFail('game-overlay');
gameOverlay.classList.add('active'); gameOverlay.classList.add('active');
const buttonCloseFrame = HtmlUtils.getElementByIdOrFail(cowebsiteCloseButtonId); const buttonCloseFrame = HtmlUtils.getElementByIdOrFail('cowebsite-close');
const functionTrigger = () => { const functionTrigger = () => {
this.triggerCloseJitsiFrameButton(); this.triggerCloseJitsiFrameButton();
} }
buttonCloseFrame.removeEventListener('click', () => { buttonCloseFrame.removeEventListener('click', functionTrigger);
buttonCloseFrame.blur();
functionTrigger();
});
gameOverlayVisibilityStore.showGameOverlay(); gameOverlayVisibilityStore.showGameOverlay();
} }
@ -122,19 +119,17 @@ export class MediaManager {
const gameOverlay = HtmlUtils.getElementByIdOrFail('game-overlay'); const gameOverlay = HtmlUtils.getElementByIdOrFail('game-overlay');
gameOverlay.classList.remove('active'); gameOverlay.classList.remove('active');
const buttonCloseFrame = HtmlUtils.getElementByIdOrFail(cowebsiteCloseButtonId); const buttonCloseFrame = HtmlUtils.getElementByIdOrFail('cowebsite-close');
const functionTrigger = () => { const functionTrigger = () => {
this.triggerCloseJitsiFrameButton(); this.triggerCloseJitsiFrameButton();
} }
buttonCloseFrame.addEventListener('click', () => { buttonCloseFrame.addEventListener('click', functionTrigger);
buttonCloseFrame.blur();
functionTrigger();
});
gameOverlayVisibilityStore.hideGameOverlay(); gameOverlayVisibilityStore.hideGameOverlay();
} }
addActiveVideo(user: UserSimplePeerInterface, userName: string = ""){ addActiveVideo(user: UserSimplePeerInterface, userName: string = ""){
this.webrtcInAudio.play();
const userId = ''+user.userId const userId = ''+user.userId
userName = userName.toUpperCase(); userName = userName.toUpperCase();
@ -150,7 +145,7 @@ export class MediaManager {
<img title="report this user" src="resources/logos/report.svg"> <img title="report this user" src="resources/logos/report.svg">
<span>Report/Block</span> <span>Report/Block</span>
</button> </button>
<video id="${userId}" autoplay playsinline></video> <video id="${userId}" autoplay></video>
<img src="resources/logos/blockSign.svg" id="blocking-${userId}" class="block-logo"> <img src="resources/logos/blockSign.svg" id="blocking-${userId}" class="block-logo">
<div id="soundMeter-${userId}" class="sound-progress"> <div id="soundMeter-${userId}" class="sound-progress">
<span></span> <span></span>
@ -187,7 +182,7 @@ export class MediaManager {
userId = this.getScreenSharingId(userId); userId = this.getScreenSharingId(userId);
const html = ` const html = `
<div id="div-${userId}" class="video-container"> <div id="div-${userId}" class="video-container">
<video id="${userId}" autoplay playsinline></video> <video id="${userId}" autoplay></video>
</div> </div>
`; `;
@ -282,6 +277,10 @@ export class MediaManager {
this.removeActiveVideo(this.getScreenSharingId(userId)) this.removeActiveVideo(this.getScreenSharingId(userId))
} }
playWebrtcOutSound(): void {
this.webrtcOutAudio.play();
}
isConnecting(userId: string): void { isConnecting(userId: string): void {
const connectingSpinnerDiv = this.getSpinner(userId); const connectingSpinnerDiv = this.getSpinner(userId);
if (connectingSpinnerDiv === null) { if (connectingSpinnerDiv === null) {

View File

@ -246,6 +246,7 @@ export class SimplePeer {
* This is triggered twice. Once by the server, and once by a remote client disconnecting * This is triggered twice. Once by the server, and once by a remote client disconnecting
*/ */
private closeConnection(userId : number) { private closeConnection(userId : number) {
mediaManager.playWebrtcOutSound();
try { try {
const peer = this.PeerConnectionArray.get(userId); const peer = this.PeerConnectionArray.get(userId);
if (peer === undefined) { if (peer === undefined) {

View File

@ -1146,10 +1146,6 @@ div.modal-report-user{
color: white; color: white;
} }
.discussion .messages .message p a:visited{
color: white;
}
.discussion .send-message{ .discussion .send-message{
position: absolute; position: absolute;
bottom: 45px; bottom: 45px;

View File

@ -75,14 +75,6 @@ message EmoteEventMessage {
string emote = 2; string emote = 2;
} }
message RequestVisitCardMessage {
int32 targetUserId = 1;
}
message VisitCardMessage {
string url = 1;
}
message QueryJitsiJwtMessage { message QueryJitsiJwtMessage {
string jitsiRoom = 1; string jitsiRoom = 1;
string tag = 2; // FIXME: rather than reading the tag from the query, we should read it from the current map! string tag = 2; // FIXME: rather than reading the tag from the query, we should read it from the current map!
@ -102,7 +94,6 @@ message ClientToServerMessage {
ReportPlayerMessage reportPlayerMessage = 11; ReportPlayerMessage reportPlayerMessage = 11;
QueryJitsiJwtMessage queryJitsiJwtMessage = 12; QueryJitsiJwtMessage queryJitsiJwtMessage = 12;
EmotePromptMessage emotePromptMessage = 13; EmotePromptMessage emotePromptMessage = 13;
RequestVisitCardMessage requestVisitCardMessage = 14;
} }
} }
@ -268,7 +259,6 @@ message ServerToClientMessage {
RefreshRoomMessage refreshRoomMessage = 17; RefreshRoomMessage refreshRoomMessage = 17;
WorldConnexionMessage worldConnexionMessage = 18; WorldConnexionMessage worldConnexionMessage = 18;
EmoteEventMessage emoteEventMessage = 19; EmoteEventMessage emoteEventMessage = 19;
VisitCardMessage visitCardMessage = 20;
} }
} }
@ -340,7 +330,6 @@ message PusherToBackMessage {
SendUserMessage sendUserMessage = 12; SendUserMessage sendUserMessage = 12;
BanUserMessage banUserMessage = 13; BanUserMessage banUserMessage = 13;
EmotePromptMessage emotePromptMessage = 14; EmotePromptMessage emotePromptMessage = 14;
RequestVisitCardMessage requestVisitCardMessage = 15;
} }
} }

View File

@ -13,12 +13,7 @@ import {
PlayGlobalMessage, PlayGlobalMessage,
ReportPlayerMessage, ReportPlayerMessage,
EmoteEventMessage, EmoteEventMessage,
QueryJitsiJwtMessage, QueryJitsiJwtMessage, SendUserMessage, ServerToClientMessage, CompanionMessage, EmotePromptMessage
SendUserMessage,
ServerToClientMessage,
CompanionMessage,
EmotePromptMessage,
RequestVisitCardMessage
} from "../Messages/generated/messages_pb"; } from "../Messages/generated/messages_pb";
import {UserMovesMessage} from "../Messages/generated/messages_pb"; import {UserMovesMessage} from "../Messages/generated/messages_pb";
import {TemplatedApp} from "uWebSockets.js" import {TemplatedApp} from "uWebSockets.js"
@ -338,9 +333,6 @@ export class IoSocketController {
socketManager.handleQueryJitsiJwtMessage(client, message.getQueryjitsijwtmessage() as QueryJitsiJwtMessage); socketManager.handleQueryJitsiJwtMessage(client, message.getQueryjitsijwtmessage() as QueryJitsiJwtMessage);
} else if (message.hasEmotepromptmessage()){ } else if (message.hasEmotepromptmessage()){
socketManager.handleEmotePromptMessage(client, message.getEmotepromptmessage() as EmotePromptMessage); socketManager.handleEmotePromptMessage(client, message.getEmotepromptmessage() as EmotePromptMessage);
} else if (message.hasRequestvisitcardmessage()) {
socketManager.handleRequestVisitCardMessage(client, message.getRequestvisitcardmessage() as RequestVisitCardMessage);
} }
/* Ok is false if backpressure was built up, wait for drain */ /* Ok is false if backpressure was built up, wait for drain */

View File

@ -24,13 +24,7 @@ import {
AdminPusherToBackMessage, AdminPusherToBackMessage,
ServerToAdminClientMessage, ServerToAdminClientMessage,
EmoteEventMessage, EmoteEventMessage,
UserJoinedRoomMessage, UserJoinedRoomMessage, UserLeftRoomMessage, AdminMessage, BanMessage, RefreshRoomMessage, EmotePromptMessage
UserLeftRoomMessage,
AdminMessage,
BanMessage,
RefreshRoomMessage,
EmotePromptMessage,
RequestVisitCardMessage
} from "../Messages/generated/messages_pb"; } from "../Messages/generated/messages_pb";
import {ProtobufUtils} from "../Model/Websocket/ProtobufUtils"; import {ProtobufUtils} from "../Model/Websocket/ProtobufUtils";
import {JITSI_ISS, SECRET_JITSI_KEY} from "../Enum/EnvironmentVariable"; import {JITSI_ISS, SECRET_JITSI_KEY} from "../Enum/EnvironmentVariable";
@ -300,7 +294,6 @@ export class SocketManager implements ZoneEventListener {
throw 'reported socket user not found'; throw 'reported socket user not found';
} }
//TODO report user on admin application //TODO report user on admin application
//todo: move to back because this fail if the reported player is in another pusher.
await adminApi.reportPlayer(reportedSocket.userUuid, reportPlayerMessage.getReportcomment(), client.userUuid, client.roomId.split('/')[2]) await adminApi.reportPlayer(reportedSocket.userUuid, reportPlayerMessage.getReportcomment(), client.userUuid, client.roomId.split('/')[2])
} catch (e) { } catch (e) {
console.error('An error occurred on "handleReportMessage"'); console.error('An error occurred on "handleReportMessage"');
@ -604,13 +597,6 @@ export class SocketManager implements ZoneEventListener {
client.backConnection.write(pusherToBackMessage); client.backConnection.write(pusherToBackMessage);
} }
handleRequestVisitCardMessage(client: ExSocketInterface, requestVisitCardMessage: RequestVisitCardMessage) {
const pusherToBackMessage = new PusherToBackMessage();
pusherToBackMessage.setRequestvisitcardmessage(requestVisitCardMessage);
client.backConnection.write(pusherToBackMessage);
}
} }
export const socketManager = new SocketManager(); export const socketManager = new SocketManager();