Refactoring Error Screen
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
import type { Game } from "../Phaser/Game/Game";
|
||||
import { chatVisibilityStore } from "../Stores/ChatStore";
|
||||
import { errorStore } from "../Stores/ErrorStore";
|
||||
import { errorScreenStore } from "../Stores/ErrorScreenStore";
|
||||
import { loginSceneVisibleStore } from "../Stores/LoginSceneStore";
|
||||
import { enableCameraSceneVisibilityStore } from "../Stores/MediaStore";
|
||||
import { selectCharacterSceneVisibleStore } from "../Stores/SelectCharacterStore";
|
||||
@@ -13,11 +14,17 @@
|
||||
import SelectCharacterScene from "./selectCharacter/SelectCharacterScene.svelte";
|
||||
import SelectCompanionScene from "./SelectCompanion/SelectCompanionScene.svelte";
|
||||
import ErrorDialog from "./UI/ErrorDialog.svelte";
|
||||
import ErrorScreen from "./UI/ErrorScreen.svelte";
|
||||
|
||||
|
||||
export let game: Game;
|
||||
</script>
|
||||
|
||||
{#if $errorStore.length > 0}
|
||||
{#if $errorScreenStore !== undefined}
|
||||
<div>
|
||||
<ErrorScreen />
|
||||
</div>
|
||||
{:else if $errorStore.length > 0}
|
||||
<div>
|
||||
<ErrorDialog />
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,158 @@
|
||||
<script lang="ts">
|
||||
import { fly } from "svelte/transition";
|
||||
import { errorScreenStore } from "../../Stores/ErrorScreenStore";
|
||||
|
||||
import logo from "../images/logo-min-white.png";
|
||||
import error from "../images/error.png";
|
||||
import cup from "../images/cup.png";
|
||||
import reload from "../images/reload.png";
|
||||
import external from "../images/external-link.png";
|
||||
import {get} from "svelte/store";
|
||||
|
||||
let errorScreen = get(errorScreenStore);
|
||||
|
||||
function click(){
|
||||
if(errorScreen.urlToRedirect) window.location.replace(errorScreen.urlToRedirect);
|
||||
else if(errorScreen.type === 'redirect' && window.history.length > 2) history.back();
|
||||
else window.location.reload(true);
|
||||
}
|
||||
let details = errorScreen.details;
|
||||
let timeVar = errorScreen.timeToRetry ?? 0;
|
||||
if(errorScreen.type === 'retry') {
|
||||
setInterval(() => {
|
||||
if (timeVar <= 1000) click();
|
||||
timeVar -= 1000;
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
$: detailsStylized = details.replace("{time}", `${timeVar/1000}`);
|
||||
|
||||
</script>
|
||||
|
||||
<main class="errorScreen" transition:fly={{ y: -200, duration: 500 }}>
|
||||
<div style="width: 90%;">
|
||||
<img src={logo} alt="WorkAdventure" class="logo"/>
|
||||
<div><img src={$errorScreenStore.type === 'retry'?cup:error} alt="" class="icon"/></div>
|
||||
{#if $errorScreenStore.type !== 'retry'}<h2>{$errorScreenStore.title}</h2>{/if}
|
||||
<p>{$errorScreenStore.subtitle}</p>
|
||||
{#if $errorScreenStore.type !== 'retry'}<p class="code">Code : {$errorScreenStore.code}</p>{/if}
|
||||
<p class="details">{detailsStylized}{#if $errorScreenStore.type === 'retry'}<div class="loading"></div>{/if}</p>
|
||||
{#if ($errorScreenStore.type === 'retry' && $errorScreenStore.canRetryManual) || ($errorScreenStore.type === 'redirect' && (window.history.length > 2 || $errorScreenStore.urlToRedirect))}
|
||||
<div class="button" on:click={click}>
|
||||
<img src={$errorScreenStore.type === 'retry'?reload:external} alt="" class="reload"/>
|
||||
{$errorScreenStore.buttonTitle}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
</main>
|
||||
|
||||
<style lang="scss">
|
||||
main.errorScreen {
|
||||
pointer-events: auto;
|
||||
width: 100%;
|
||||
background-color: #000000;
|
||||
color: #FFFFFF;
|
||||
text-align: center;
|
||||
position: absolute;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
min-width: 300px;
|
||||
z-index: 700;
|
||||
overflow-y: scroll;
|
||||
padding: 20px 0;
|
||||
.logo{
|
||||
width: 50%;
|
||||
margin-bottom: 50px;
|
||||
}
|
||||
.icon{
|
||||
height: 125px;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
h2 {
|
||||
font-family: "Press Start 2P";
|
||||
padding: 5px;
|
||||
font-size: 30px;
|
||||
}
|
||||
p {
|
||||
font-family: "Press Start 2P";
|
||||
}
|
||||
p.code{
|
||||
font-size: 12px;
|
||||
opacity: .6;
|
||||
user-select: text;
|
||||
}
|
||||
p.details{
|
||||
font-size: 12px;
|
||||
max-width: 80%;
|
||||
margin: 0 auto 35px auto;
|
||||
}
|
||||
.loading{
|
||||
display: inline-block;
|
||||
min-width: 20px;
|
||||
position: relative;
|
||||
margin-left: 5px;
|
||||
}
|
||||
.loading:after {
|
||||
overflow: hidden;
|
||||
display: inline-block;
|
||||
vertical-align: bottom;
|
||||
-webkit-animation: ellipsis steps(4,end) 900ms infinite;
|
||||
animation: ellipsis steps(4,end) 900ms infinite;
|
||||
content: "\2026";
|
||||
width: 0;
|
||||
font-family: "Press Start 2P";
|
||||
font-size: 16px;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: -19px;
|
||||
}
|
||||
|
||||
@keyframes ellipsis {
|
||||
to {
|
||||
width: 1.25em;
|
||||
}
|
||||
}
|
||||
|
||||
@-webkit-keyframes ellipsis {
|
||||
to {
|
||||
width: 1.25em;
|
||||
}
|
||||
}
|
||||
|
||||
.button{
|
||||
cursor: pointer;
|
||||
background-image: url('../images/button-large.png');
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: contain;
|
||||
padding: 16px 20px;
|
||||
margin-bottom: 2px;
|
||||
font-family: "Press Start 2P";
|
||||
font-size: 14px;
|
||||
.reload{
|
||||
margin-top: -4px;
|
||||
width: 22px;
|
||||
}
|
||||
}
|
||||
.button:hover{
|
||||
margin-bottom: 0;
|
||||
margin-top: -5px;
|
||||
padding: 18px 22px;
|
||||
font-size: 16px;
|
||||
.reload{
|
||||
margin-top: -5px;
|
||||
width: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 3.5 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.7 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 4.7 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 10 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 7.4 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 3.6 KiB |
@@ -25,6 +25,7 @@ import {
|
||||
TokenExpiredMessage,
|
||||
WorldConnexionMessage,
|
||||
ErrorMessage as ErrorMessageTsProto,
|
||||
ErrorV2Message as ErrorV2MessageTsProto,
|
||||
UserMovedMessage as UserMovedMessageTsProto,
|
||||
GroupUpdateMessage as GroupUpdateMessageTsProto,
|
||||
GroupDeleteMessage as GroupDeleteMessageTsProto,
|
||||
@@ -46,6 +47,8 @@ import { Subject } from "rxjs";
|
||||
import { selectCharacterSceneVisibleStore } from "../Stores/SelectCharacterStore";
|
||||
import { gameManager } from "../Phaser/Game/GameManager";
|
||||
import { SelectCharacterScene, SelectCharacterSceneName } from "../Phaser/Login/SelectCharacterScene";
|
||||
import {errorScreenStore} from "../Stores/ErrorScreenStore";
|
||||
import {WAError} from "../Phaser/Reconnecting/WAError";
|
||||
|
||||
const manualPingDelay = 20000;
|
||||
|
||||
@@ -61,6 +64,9 @@ export class RoomConnection implements RoomConnection {
|
||||
private readonly _errorMessageStream = new Subject<ErrorMessageTsProto>();
|
||||
public readonly errorMessageStream = this._errorMessageStream.asObservable();
|
||||
|
||||
private readonly _errorV2MessageStream = new Subject<ErrorV2MessageTsProto>();
|
||||
public readonly errorV2MessageStream = this._errorV2MessageStream.asObservable();
|
||||
|
||||
private readonly _roomJoinedMessageStream = new Subject<{
|
||||
connection: RoomConnection;
|
||||
room: RoomJoinedMessageInterface;
|
||||
@@ -476,6 +482,13 @@ export class RoomConnection implements RoomConnection {
|
||||
console.error("An error occurred server side: " + message.errorMessage.message);
|
||||
break;
|
||||
}
|
||||
case "errorV2Message": {
|
||||
this._errorV2MessageStream.next(message.errorV2Message);
|
||||
if(message.errorV2Message.code !== 'retry') this.closed = true;
|
||||
console.error("An error occurred server side: " + message.errorV2Message.code);
|
||||
errorScreenStore.setError(message.errorV2Message as unknown as WAError);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
// 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
|
||||
|
||||
@@ -4,9 +4,8 @@ import { ErrorScene } from "../Reconnecting/ErrorScene";
|
||||
import { WAError } from "../Reconnecting/WAError";
|
||||
import { waScaleManager } from "../Services/WaScaleManager";
|
||||
import { ReconnectingTextures } from "../Reconnecting/ReconnectingScene";
|
||||
import LL from "../../i18n/i18n-svelte";
|
||||
import { get } from "svelte/store";
|
||||
import { localeDetector } from "../../i18n/locales";
|
||||
import {errorScreenStore} from "../../Stores/ErrorScreenStore";
|
||||
|
||||
export const EntrySceneName = "EntryScene";
|
||||
|
||||
@@ -47,27 +46,18 @@ export class EntryScene extends Scene {
|
||||
this.scene.start(nextSceneName);
|
||||
})
|
||||
.catch((err) => {
|
||||
const $LL = get(LL);
|
||||
if (err.response && err.response.status == 404) {
|
||||
ErrorScene.showError(
|
||||
new WAError(
|
||||
$LL.error.accessLink.title(),
|
||||
$LL.error.accessLink.subTitle(),
|
||||
$LL.error.accessLink.details()
|
||||
),
|
||||
this.scene
|
||||
);
|
||||
} else if (err.response && err.response.status == 403) {
|
||||
ErrorScene.showError(
|
||||
new WAError(
|
||||
$LL.error.connectionRejected.title(),
|
||||
$LL.error.connectionRejected.subTitle({
|
||||
error: err.response.data ? ". \n\r \n\r" + `${err.response.data}` : "",
|
||||
}),
|
||||
$LL.error.connectionRejected.details()
|
||||
),
|
||||
this.scene
|
||||
);
|
||||
if (err.response.data?.code) {
|
||||
errorScreenStore.setError(new WAError(
|
||||
err.response.data.type,
|
||||
err.response.data.code,
|
||||
err.response.data.title,
|
||||
err.response.data.subtitle,
|
||||
err.response.data.details,
|
||||
err.response.data.timeToRetry,
|
||||
err.response.data.canRetryManual,
|
||||
err.response.data.urlToRedirect,
|
||||
err.response.data.buttonTitle
|
||||
));
|
||||
} else {
|
||||
ErrorScene.showError(err, this.scene);
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ import Image = Phaser.GameObjects.Image;
|
||||
import Sprite = Phaser.GameObjects.Sprite;
|
||||
import Text = Phaser.GameObjects.Text;
|
||||
import ScenePlugin = Phaser.Scenes.ScenePlugin;
|
||||
import { WAError } from "./WAError";
|
||||
import Axios from "axios";
|
||||
|
||||
export const ErrorSceneName = "ErrorScene";
|
||||
@@ -88,12 +87,6 @@ export class ErrorScene extends Phaser.Scene {
|
||||
title: "An error occurred",
|
||||
subTitle: error,
|
||||
});
|
||||
} else if (error instanceof WAError) {
|
||||
scene.start(ErrorSceneName, {
|
||||
title: error.title,
|
||||
subTitle: error.subTitle,
|
||||
message: error.details,
|
||||
});
|
||||
} else if (Axios.isAxiosError(error) && error.response) {
|
||||
// Axios HTTP error
|
||||
// client received an error response (5xx, 4xx)
|
||||
|
||||
@@ -1,26 +1,55 @@
|
||||
export class WAError extends Error {
|
||||
private _type: string;
|
||||
private _code: string;
|
||||
private _title: string;
|
||||
private _subTitle: string;
|
||||
private _subtitle: string;
|
||||
private _details: string;
|
||||
private _timeToRetry:number;
|
||||
private _canRetryManual: boolean;
|
||||
private _urlToRedirect: string;
|
||||
private _buttonTitle: string;
|
||||
|
||||
constructor(title: string, subTitle: string, details: string) {
|
||||
super(title + " - " + subTitle + " - " + details);
|
||||
constructor(type: string, code: string, title: string, subtitle: string, details: string, timeToRetry: number, canRetryManual: boolean, urlToRedirect: string, buttonTitle: string) {
|
||||
super(title + " - " + subtitle + " - " + details);
|
||||
|
||||
this._type = type;
|
||||
this._code = code;
|
||||
this._title = title;
|
||||
this._subTitle = subTitle;
|
||||
this._subtitle = subtitle;
|
||||
this._details = details;
|
||||
this._timeToRetry = timeToRetry;
|
||||
this._canRetryManual = canRetryManual;
|
||||
this._urlToRedirect = urlToRedirect;
|
||||
this._buttonTitle = buttonTitle;
|
||||
// Set the prototype explicitly.
|
||||
Object.setPrototypeOf(this, WAError.prototype);
|
||||
}
|
||||
|
||||
get type(): string {
|
||||
return this._type;
|
||||
}
|
||||
get code(): string {
|
||||
return this._code;
|
||||
}
|
||||
get title(): string {
|
||||
return this._title;
|
||||
}
|
||||
|
||||
get subTitle(): string {
|
||||
return this._subTitle;
|
||||
get subtitle(): string {
|
||||
return this._subtitle;
|
||||
}
|
||||
|
||||
get details(): string {
|
||||
return this._details;
|
||||
}
|
||||
get timeToRetry(): number {
|
||||
return this._timeToRetry;
|
||||
}
|
||||
get buttonTitle(): string {
|
||||
return this._buttonTitle;
|
||||
}
|
||||
get urlToRedirect(): string {
|
||||
return this._urlToRedirect;
|
||||
}
|
||||
get canRetryManual(): boolean {
|
||||
return this._canRetryManual;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
import {writable} from "svelte/store";
|
||||
import {WAError} from "../Phaser/Reconnecting/WAError";
|
||||
|
||||
/**
|
||||
* A store that contains one error of type WAError to be displayed.
|
||||
*/
|
||||
function createErrorScreenStore() {
|
||||
const { subscribe, set } = writable<WAError>(undefined);
|
||||
|
||||
return {
|
||||
subscribe,
|
||||
setError: (
|
||||
e: WAError
|
||||
): void => set(e),
|
||||
};
|
||||
}
|
||||
|
||||
export const errorScreenStore = createErrorScreenStore();
|
||||
Reference in New Issue
Block a user