Migrating LoginScene to Svelte
This commit is contained in:
parent
120a488121
commit
13d3227323
@ -22,6 +22,10 @@
|
|||||||
- Mouse wheel support to zoom in / out
|
- Mouse wheel support to zoom in / out
|
||||||
- Pinch support on mobile to zoom in / out
|
- Pinch support on mobile to zoom in / out
|
||||||
- Improved virtual joystick size (adapts to the zoom level)
|
- Improved virtual joystick size (adapts to the zoom level)
|
||||||
|
- Redesigned intermediate scenes
|
||||||
|
- Redesigned Select Companion scene
|
||||||
|
- Redesigned Enter Your Name scene
|
||||||
|
- Added a new `DISPLAY_TERMS_OF_USE` environment variable to trigger the display of terms of use
|
||||||
- New scripting API features:
|
- New scripting API features:
|
||||||
- Use `WA.loadSound(): Sound` to load / play / stop a sound
|
- Use `WA.loadSound(): Sound` to load / play / stop a sound
|
||||||
|
|
||||||
|
117
front/dist/resources/html/loginScene.html
vendored
117
front/dist/resources/html/loginScene.html
vendored
@ -1,117 +0,0 @@
|
|||||||
<style>
|
|
||||||
#loginScene {
|
|
||||||
background: #000000;
|
|
||||||
/*border: 1px solid #ebeeee;*/
|
|
||||||
border-radius: 6px;
|
|
||||||
margin: 20px auto 0;
|
|
||||||
width: 90%;
|
|
||||||
max-width: 200px;
|
|
||||||
color: #ebeeee;
|
|
||||||
max-height: 40vh;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
#loginScene h1 {
|
|
||||||
background-image: linear-gradient(top, #f1f3f3, #d4dae0);
|
|
||||||
border-bottom: 1px solid #a6abaf;
|
|
||||||
border-radius: 6px 6px 0 0;
|
|
||||||
box-sizing: border-box;
|
|
||||||
color: #727678;
|
|
||||||
display: block;
|
|
||||||
height: 43px;
|
|
||||||
padding-top: 10px;
|
|
||||||
margin: 0;
|
|
||||||
text-align: center;
|
|
||||||
text-shadow: 0 -1px 0 rgba(0,0,0,0.2), 0 1px 0 #fff;
|
|
||||||
}
|
|
||||||
#loginScene input {
|
|
||||||
font-size: 70%;
|
|
||||||
background: linear-gradient(top, #d6d7d7, #dee0e0);
|
|
||||||
border: 1px solid #a1a3a3;
|
|
||||||
border-radius: 4px;
|
|
||||||
box-shadow: 0 1px #fff;
|
|
||||||
box-sizing: border-box;
|
|
||||||
color: #696969;
|
|
||||||
height: 30px;
|
|
||||||
transition: box-shadow 0.3s;
|
|
||||||
width: 100%;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
#loginScene section {
|
|
||||||
margin: 10px;
|
|
||||||
}
|
|
||||||
#loginScene section.action{
|
|
||||||
text-align: center;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
#loginScene button {
|
|
||||||
margin: 10px;
|
|
||||||
background-color: black;;
|
|
||||||
color: #ebeeee;
|
|
||||||
border-radius: 7px;
|
|
||||||
padding-bottom: 4px;
|
|
||||||
width: 100px;
|
|
||||||
}
|
|
||||||
#loginScene button#loginSceneFormCancel {
|
|
||||||
background-color: #c7c7c700;
|
|
||||||
color: #292929;
|
|
||||||
}
|
|
||||||
#loginScene section h6,
|
|
||||||
#loginScene section h5{
|
|
||||||
margin: 1px;
|
|
||||||
}
|
|
||||||
#loginScene section.text-center{
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
#loginScene section a{
|
|
||||||
font-size: 8px;
|
|
||||||
text-decoration: underline;
|
|
||||||
color: #ebeeee;
|
|
||||||
}
|
|
||||||
#loginScene section a:hover{
|
|
||||||
font-weight: 700;
|
|
||||||
}
|
|
||||||
#loginScene section p{
|
|
||||||
text-align: left;
|
|
||||||
font-size: 8px;
|
|
||||||
margin: 10px 10px;
|
|
||||||
}
|
|
||||||
#loginScene section p.err{
|
|
||||||
color: red;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
#loginScene section p.info{
|
|
||||||
display: none;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
#loginScene section input#loginSceneLink{
|
|
||||||
background-color: #a1a3a3;
|
|
||||||
}
|
|
||||||
#loginScene section img{
|
|
||||||
width: 160px;
|
|
||||||
margin: 20px 0;
|
|
||||||
}
|
|
||||||
@media only screen and (max-width: 800px),
|
|
||||||
only screen and (max-height: 600px) {
|
|
||||||
#loginScene{
|
|
||||||
overflow-y: scroll;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<form id="loginScene" hidden>
|
|
||||||
<section class="text-center">
|
|
||||||
<img src="resources/logos/logo.png">
|
|
||||||
</section>
|
|
||||||
<section class="text-center">
|
|
||||||
<h5>Enter your name</h5>
|
|
||||||
<p class="info">9 chars maximum</p>
|
|
||||||
<p class="err" id="errorLoginScene"></p>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
<input type="text" name="email" id="loginSceneName">
|
|
||||||
<p>By continuing, you are agreeing our <a href="https://workadventu.re/terms-of-use" target="_blank">terms of use</a>, <a href="https://workadventu.re/privacy-policy" target="_blank">privacy policy</a> and <a href="https://workadventu.re/cookie-policy" target="_blank">cookie policy</a>.</p>
|
|
||||||
</section>
|
|
||||||
<section class="action">
|
|
||||||
<button type="submit" id="loginSceneFormSubmit">Continue</button>
|
|
||||||
</section>
|
|
||||||
</form>
|
|
@ -7,12 +7,17 @@
|
|||||||
import SelectCompanionScene from "./SelectCompanion/SelectCompanionScene.svelte";
|
import SelectCompanionScene from "./SelectCompanion/SelectCompanionScene.svelte";
|
||||||
import {selectCompanionSceneVisibleStore} from "../Stores/SelectCompanionStore";
|
import {selectCompanionSceneVisibleStore} from "../Stores/SelectCompanionStore";
|
||||||
import {Game} from "../Phaser/Game/Game";
|
import {Game} from "../Phaser/Game/Game";
|
||||||
|
import LoginScene from "./Login/LoginScene.svelte";
|
||||||
|
import {loginSceneVisibleStore} from "../Stores/LoginSceneStore";
|
||||||
|
|
||||||
export let game: Game;
|
export let game: Game;
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
|
{#if $loginSceneVisibleStore}
|
||||||
|
<LoginScene game={game}></LoginScene>
|
||||||
|
{/if}
|
||||||
{#if $selectCompanionSceneVisibleStore}
|
{#if $selectCompanionSceneVisibleStore}
|
||||||
<SelectCompanionScene game={game}></SelectCompanionScene>
|
<SelectCompanionScene game={game}></SelectCompanionScene>
|
||||||
{/if}
|
{/if}
|
||||||
|
119
front/src/Components/Login/LoginScene.svelte
Normal file
119
front/src/Components/Login/LoginScene.svelte
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
<script lang="typescript">
|
||||||
|
import {Game} from "../../Phaser/Game/Game";
|
||||||
|
import {LoginScene} from "../../Phaser/Login/LoginScene";
|
||||||
|
import {DISPLAY_TERMS_OF_USE, MAX_USERNAME_LENGTH} from "../../Enum/EnvironmentVariable";
|
||||||
|
import logoImg from "../images/logo.png";
|
||||||
|
import {gameManager} from "../../Phaser/Game/GameManager";
|
||||||
|
import {maxUserNameLength} from "../../Connexion/LocalUser";
|
||||||
|
|
||||||
|
export let game: Game;
|
||||||
|
|
||||||
|
const loginScene = game.scene.scenes.find((scene) => scene instanceof LoginScene);
|
||||||
|
|
||||||
|
let name = gameManager.getPlayerName() || '';
|
||||||
|
let startValidating = false;
|
||||||
|
|
||||||
|
function submit() {
|
||||||
|
startValidating = true;
|
||||||
|
|
||||||
|
let finalName = name.trim();
|
||||||
|
if (finalName !== '') {
|
||||||
|
loginScene.login(finalName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<form class="loginScene" on:submit|preventDefault={submit}>
|
||||||
|
<section class="text-center">
|
||||||
|
<img src={logoImg} alt="WorkAdventure logo" />
|
||||||
|
</section>
|
||||||
|
<section class="text-center">
|
||||||
|
<h2>Enter your name</h2>
|
||||||
|
</section>
|
||||||
|
<input type="text" name="loginSceneName" class="nes-input is-dark" maxlength={MAX_USERNAME_LENGTH} autofocus bind:value={name} on:keypress={() => {startValidating = true}} class:is-error={name.trim() === '' && startValidating} />
|
||||||
|
<section class="error-section">
|
||||||
|
{#if name.trim() === '' && startValidating }
|
||||||
|
<p class="err">The name is empty</p>
|
||||||
|
{/if}
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{#if !DISPLAY_TERMS_OF_USE}
|
||||||
|
<section class="terms-and-conditions">
|
||||||
|
<p>By continuing, you are agreeing our <a href="https://workadventu.re/terms-of-use" target="_blank">terms of use</a>, <a href="https://workadventu.re/privacy-policy" target="_blank">privacy policy</a> and <a href="https://workadventu.re/cookie-policy" target="_blank">cookie policy</a>.</p>
|
||||||
|
</section>
|
||||||
|
{/if}
|
||||||
|
<section class="action">
|
||||||
|
<button type="submit" class="nes-btn is-primary loginSceneFormSubmit">Continue</button>
|
||||||
|
</section>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.loginScene {
|
||||||
|
font-family: "Press Start 2P";
|
||||||
|
pointer-events: auto;
|
||||||
|
margin: 20px auto 0;
|
||||||
|
width: 90%;
|
||||||
|
color: #ebeeee;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-flow: column wrap;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
input {
|
||||||
|
text-align: center;
|
||||||
|
font-family: "Press Start 2P";
|
||||||
|
max-width: 400px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.terms-and-conditions {
|
||||||
|
max-width: 400px;
|
||||||
|
}
|
||||||
|
|
||||||
|
section.error-section {
|
||||||
|
min-height: 2rem;
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.loginScene section {
|
||||||
|
margin: 10px;
|
||||||
|
}
|
||||||
|
.loginScene section.action{
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
.loginScene section h2{
|
||||||
|
font-family: "Press Start 2P";
|
||||||
|
margin: 1px;
|
||||||
|
}
|
||||||
|
.loginScene section.text-center{
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.loginScene section a{
|
||||||
|
text-decoration: underline;
|
||||||
|
color: #ebeeee;
|
||||||
|
}
|
||||||
|
.loginScene section a:hover{
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
.loginScene section p{
|
||||||
|
text-align: left;
|
||||||
|
margin: 10px 10px;
|
||||||
|
}
|
||||||
|
.loginScene p.err{
|
||||||
|
color: #ce372b;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
/*.loginScene section p.info{
|
||||||
|
display: none;
|
||||||
|
text-align: center;
|
||||||
|
}*/
|
||||||
|
.loginScene section img{
|
||||||
|
width: 100%;
|
||||||
|
margin: 20px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
@ -41,9 +41,6 @@
|
|||||||
pointer-events: auto;
|
pointer-events: auto;
|
||||||
color: #ebeeee;
|
color: #ebeeee;
|
||||||
}
|
}
|
||||||
.selectCompanionScene button {
|
|
||||||
font-family: "Press Start 2P";
|
|
||||||
}
|
|
||||||
.selectCompanionScene section {
|
.selectCompanionScene section {
|
||||||
margin: 10px;
|
margin: 10px;
|
||||||
}
|
}
|
||||||
|
BIN
front/src/Components/images/logo.png
Normal file
BIN
front/src/Components/images/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 16 KiB |
@ -10,7 +10,7 @@ export interface CharacterTexture {
|
|||||||
export const maxUserNameLength: number = MAX_USERNAME_LENGTH;
|
export const maxUserNameLength: number = MAX_USERNAME_LENGTH;
|
||||||
|
|
||||||
export function isUserNameValid(value: unknown): boolean {
|
export function isUserNameValid(value: unknown): boolean {
|
||||||
return typeof value === "string" && value.length > 0 && value.length < maxUserNameLength && value.indexOf(' ') === -1;
|
return typeof value === "string" && value.length > 0 && value.length <= maxUserNameLength && value.indexOf(' ') === -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function areCharacterLayersValid(value: string[] | null): boolean {
|
export function areCharacterLayersValid(value: string[] | null): boolean {
|
||||||
|
@ -14,6 +14,7 @@ const POSITION_DELAY = 200; // Wait 200ms between sending position events
|
|||||||
const MAX_EXTRAPOLATION_TIME = 100; // Extrapolate a maximum of 250ms if no new movement is sent by the player
|
const MAX_EXTRAPOLATION_TIME = 100; // Extrapolate a maximum of 250ms if no new movement is sent by the player
|
||||||
export const MAX_USERNAME_LENGTH = parseInt(process.env.MAX_USERNAME_LENGTH || '') || 8;
|
export const MAX_USERNAME_LENGTH = parseInt(process.env.MAX_USERNAME_LENGTH || '') || 8;
|
||||||
export const MAX_PER_GROUP = parseInt(process.env.MAX_PER_GROUP || '4');
|
export const MAX_PER_GROUP = parseInt(process.env.MAX_PER_GROUP || '4');
|
||||||
|
export const DISPLAY_TERMS_OF_USE = process.env.DISPLAY_TERMS_OF_USE == 'true';
|
||||||
|
|
||||||
export const isMobile = ():boolean => ( ( window.innerWidth <= 800 ) || ( window.innerHeight <= 600 ) );
|
export const isMobile = ():boolean => ( ( window.innerWidth <= 800 ) || ( window.innerHeight <= 600 ) );
|
||||||
|
|
||||||
|
@ -1,17 +1,12 @@
|
|||||||
import {gameManager} from "../Game/GameManager";
|
import {gameManager} from "../Game/GameManager";
|
||||||
import {SelectCharacterSceneName} from "./SelectCharacterScene";
|
import {SelectCharacterSceneName} from "./SelectCharacterScene";
|
||||||
import {ResizableScene} from "./ResizableScene";
|
import {ResizableScene} from "./ResizableScene";
|
||||||
import { localUserStore } from "../../Connexion/LocalUserStore";
|
import {loginSceneVisibleStore} from "../../Stores/LoginSceneStore";
|
||||||
import {MenuScene} from "../Menu/MenuScene";
|
|
||||||
import { isUserNameValid } from "../../Connexion/LocalUser";
|
|
||||||
|
|
||||||
export const LoginSceneName = "LoginScene";
|
export const LoginSceneName = "LoginScene";
|
||||||
|
|
||||||
const loginSceneKey = 'loginScene';
|
|
||||||
|
|
||||||
export class LoginScene extends ResizableScene {
|
export class LoginScene extends ResizableScene {
|
||||||
|
|
||||||
private loginSceneElement!: Phaser.GameObjects.DOMElement;
|
|
||||||
private name: string = '';
|
private name: string = '';
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
@ -22,65 +17,25 @@ export class LoginScene extends ResizableScene {
|
|||||||
}
|
}
|
||||||
|
|
||||||
preload() {
|
preload() {
|
||||||
this.load.html(loginSceneKey, 'resources/html/loginScene.html');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
create() {
|
create() {
|
||||||
this.loginSceneElement = this.add.dom(-1000, 0).createFromCache(loginSceneKey);
|
loginSceneVisibleStore.set(true);
|
||||||
this.centerXDomElement(this.loginSceneElement, 200);
|
|
||||||
MenuScene.revealMenusAfterInit(this.loginSceneElement, loginSceneKey);
|
|
||||||
|
|
||||||
const pErrorElement = this.loginSceneElement.getChildByID('errorLoginScene') as HTMLInputElement;
|
|
||||||
const inputElement = this.loginSceneElement.getChildByID('loginSceneName') as HTMLInputElement;
|
|
||||||
inputElement.value = localUserStore.getName() ?? '';
|
|
||||||
inputElement.focus();
|
|
||||||
inputElement.addEventListener('keypress', (event: KeyboardEvent) => {
|
|
||||||
if(inputElement.value.length > 7){
|
|
||||||
event.preventDefault();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
pErrorElement.innerHTML = '';
|
|
||||||
if(inputElement.value && !isUserNameValid(inputElement.value)){
|
|
||||||
pErrorElement.innerHTML = 'Invalid user name: No spaces are allowed.';
|
|
||||||
}
|
|
||||||
if (event.key === 'Enter') {
|
|
||||||
event.preventDefault();
|
|
||||||
this.login(inputElement);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const continuingButton = this.loginSceneElement.getChildByID('loginSceneFormSubmit') as HTMLButtonElement;
|
|
||||||
continuingButton.addEventListener('click', (e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
this.login(inputElement);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private login(inputElement: HTMLInputElement): void {
|
public login(name: string): void {
|
||||||
const pErrorElement = this.loginSceneElement.getChildByID('errorLoginScene') as HTMLInputElement;
|
name = name.trim();
|
||||||
this.name = inputElement.value;
|
gameManager.setPlayerName(name);
|
||||||
if (this.name === '') {
|
|
||||||
pErrorElement.innerHTML = 'The name is empty';
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if(!isUserNameValid(this.name)){
|
|
||||||
pErrorElement.innerHTML = 'Invalid user name: only letters and numbers are allowed. No spaces.';
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (this.name === '') return
|
|
||||||
gameManager.setPlayerName(this.name);
|
|
||||||
|
|
||||||
this.scene.stop(LoginSceneName)
|
this.scene.stop(LoginSceneName)
|
||||||
gameManager.tryResumingGame(this, SelectCharacterSceneName);
|
gameManager.tryResumingGame(this, SelectCharacterSceneName);
|
||||||
this.scene.remove(LoginSceneName)
|
this.scene.remove(LoginSceneName);
|
||||||
|
loginSceneVisibleStore.set(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
update(time: number, delta: number): void {
|
update(time: number, delta: number): void {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public onResize(): void {
|
public onResize(): void {
|
||||||
this.centerXDomElement(this.loginSceneElement, 200);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
3
front/src/Stores/LoginSceneStore.ts
Normal file
3
front/src/Stores/LoginSceneStore.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import { writable } from "svelte/store";
|
||||||
|
|
||||||
|
export const loginSceneVisibleStore = writable(false);
|
@ -60,6 +60,7 @@ export class ScreenSharingPeer extends Peer {
|
|||||||
const message = JSON.parse(chunk.toString('utf8'));
|
const message = JSON.parse(chunk.toString('utf8'));
|
||||||
if (message.streamEnded !== true) {
|
if (message.streamEnded !== true) {
|
||||||
console.error('Unexpected message on screen sharing peer connection');
|
console.error('Unexpected message on screen sharing peer connection');
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
mediaManager.removeActiveScreenSharingVideo("" + this.userId);
|
mediaManager.removeActiveScreenSharingVideo("" + this.userId);
|
||||||
});
|
});
|
||||||
|
@ -3,3 +3,7 @@
|
|||||||
*{
|
*{
|
||||||
font-family: PixelFont-7,monospace;
|
font-family: PixelFont-7,monospace;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.nes-btn {
|
||||||
|
font-family: "Press Start 2P";
|
||||||
|
}
|
||||||
|
@ -7,6 +7,7 @@ import MiniCssExtractPlugin from 'mini-css-extract-plugin';
|
|||||||
import sveltePreprocess from 'svelte-preprocess';
|
import sveltePreprocess from 'svelte-preprocess';
|
||||||
import ForkTsCheckerWebpackPlugin from "fork-ts-checker-webpack-plugin";
|
import ForkTsCheckerWebpackPlugin from "fork-ts-checker-webpack-plugin";
|
||||||
import NodePolyfillPlugin from 'node-polyfill-webpack-plugin';
|
import NodePolyfillPlugin from 'node-polyfill-webpack-plugin';
|
||||||
|
import {DISPLAY_TERMS_OF_USE} from "./src/Enum/EnvironmentVariable";
|
||||||
|
|
||||||
const mode = process.env.NODE_ENV ?? 'development';
|
const mode = process.env.NODE_ENV ?? 'development';
|
||||||
const isProduction = mode === 'production';
|
const isProduction = mode === 'production';
|
||||||
@ -175,7 +176,8 @@ module.exports = {
|
|||||||
'JITSI_PRIVATE_MODE': null,
|
'JITSI_PRIVATE_MODE': null,
|
||||||
'START_ROOM_URL': null,
|
'START_ROOM_URL': null,
|
||||||
'MAX_USERNAME_LENGTH': 8,
|
'MAX_USERNAME_LENGTH': 8,
|
||||||
'MAX_PER_GROUP': 4
|
'MAX_PER_GROUP': 4,
|
||||||
|
'DISPLAY_TERMS_OF_USE': false,
|
||||||
})
|
})
|
||||||
],
|
],
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user