From 2a1af2a131f72ad5a00b6f4a4990a12fcedb0342 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?gr=C3=A9goire=20parant?= Date: Thu, 29 Jul 2021 16:42:31 +0200 Subject: [PATCH] PWA service workers (#1319) * PWA services worker - [x] Register service worker of PWA to install WorkAdventure application on desktop and mobile - [x] Create webpage specifique for PWA - [ ] Add register service to save and redirect on a card - [ ] Add possibilities to install PWA for one World (with register token if existing) * Finish PWA strategy to load last map visited * Fix feedback @Kharhamel * Fix feedback @Kharhamel --- front/dist/index.tmpl.html | 1 + front/dist/resources/service-worker.html | 62 ++++++++++++++ front/dist/resources/service-worker.js | 12 ++- .../dist/static/images/favicons/manifest.json | 5 +- front/src/Connexion/ConnectionManager.ts | 21 +++-- front/src/Connexion/LocalUserStore.ts | 80 ++++++++++--------- front/src/Network/ServiceWorker.ts | 20 +++++ front/src/index.ts | 13 --- 8 files changed, 156 insertions(+), 58 deletions(-) create mode 100644 front/dist/resources/service-worker.html create mode 100644 front/src/Network/ServiceWorker.ts diff --git a/front/dist/index.tmpl.html b/front/dist/index.tmpl.html index 30ea8353..187e513a 100644 --- a/front/dist/index.tmpl.html +++ b/front/dist/index.tmpl.html @@ -34,6 +34,7 @@ WorkAdventure +
diff --git a/front/dist/resources/service-worker.html b/front/dist/resources/service-worker.html new file mode 100644 index 00000000..45615b1a --- /dev/null +++ b/front/dist/resources/service-worker.html @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + WorkAdventure PWA + + + + + WorkAdventure logo +

Charging your workspace ...

+ + + \ No newline at end of file diff --git a/front/dist/resources/service-worker.js b/front/dist/resources/service-worker.js index e496f7fc..d9509b6f 100644 --- a/front/dist/resources/service-worker.js +++ b/front/dist/resources/service-worker.js @@ -48,6 +48,14 @@ self.addEventListener('fetch', function(event) { ); }); -self.addEventListener('activate', function(event) { - //TODO activate service worker +self.addEventListener('wait', function(event) { + //TODO wait +}); + +self.addEventListener('update', function(event) { + //TODO update +}); + +self.addEventListener('beforeinstallprompt', (e) => { + //TODO change prompt }); \ No newline at end of file diff --git a/front/dist/static/images/favicons/manifest.json b/front/dist/static/images/favicons/manifest.json index 30d08769..9f9e9af1 100644 --- a/front/dist/static/images/favicons/manifest.json +++ b/front/dist/static/images/favicons/manifest.json @@ -128,11 +128,12 @@ "type": "image\/png" } ], - "start_url": "/", + "start_url": "/resources/service-worker.html", "background_color": "#000000", "display_override": ["window-control-overlay", "minimal-ui"], "display": "standalone", - "scope": "/", + "orientation": "portrait-primary", + "scope": "/resources/", "lang": "en", "theme_color": "#000000", "shortcuts": [ diff --git a/front/src/Connexion/ConnectionManager.ts b/front/src/Connexion/ConnectionManager.ts index 0c459629..bca7f692 100644 --- a/front/src/Connexion/ConnectionManager.ts +++ b/front/src/Connexion/ConnectionManager.ts @@ -6,6 +6,7 @@ import { GameConnexionTypes, urlManager } from "../Url/UrlManager"; import { localUserStore } from "./LocalUserStore"; import { CharacterTexture, LocalUser } from "./LocalUser"; import { Room } from "./Room"; +import { _ServiceWorker } from "../Network/ServiceWorker"; class ConnectionManager { private localUser!: LocalUser; @@ -14,6 +15,8 @@ class ConnectionManager { private reconnectingTimeout: NodeJS.Timeout | null = null; private _unloading: boolean = false; + private serviceWorker?: _ServiceWorker; + get unloading() { return this._unloading; } @@ -30,6 +33,8 @@ class ConnectionManager { public async initGameConnexion(): Promise { const connexionType = urlManager.getGameConnexionType(); this.connexionType = connexionType; + + let room: Room | null = null; if (connexionType === GameConnexionTypes.register) { const organizationMemberToken = urlManager.getOrganizationToken(); const data = await Axios.post(`${PUSHER_URL}/register`, { organizationMemberToken }).then( @@ -40,7 +45,7 @@ class ConnectionManager { const roomUrl = data.roomUrl; - const room = await Room.createRoom( + room = await Room.createRoom( new URL( window.location.protocol + "//" + @@ -51,7 +56,6 @@ class ConnectionManager { ) ); urlManager.pushRoomIdToUrl(room); - return Promise.resolve(room); } else if ( connexionType === GameConnexionTypes.organization || connexionType === GameConnexionTypes.anonymous || @@ -90,7 +94,7 @@ class ConnectionManager { } //get detail map for anonymous login and set texture in local storage - const room = await Room.createRoom(new URL(roomPath)); + room = await Room.createRoom(new URL(roomPath)); if (room.textures != undefined && room.textures.length > 0) { //check if texture was changed if (localUser.textures.length === 0) { @@ -107,10 +111,13 @@ class ConnectionManager { this.localUser = localUser; localUserStore.saveUser(localUser); } - return Promise.resolve(room); + } + if (room == undefined) { + return Promise.reject(new Error("Invalid URL")); } - return Promise.reject(new Error("Invalid URL")); + this.serviceWorker = new _ServiceWorker(); + return Promise.resolve(room); } private async verifyToken(token: string): Promise { @@ -148,6 +155,7 @@ class ConnectionManager { viewport, companion ); + connection.onConnectError((error: object) => { console.log("An error occurred while connecting to socket server. Retrying"); reject(error); @@ -166,6 +174,9 @@ class ConnectionManager { }); connection.onConnect((connect: OnConnectInterface) => { + //save last room url connected + localUserStore.setLastRoomUrl(roomUrl); + resolve(connect); }); }).catch((err) => { diff --git a/front/src/Connexion/LocalUserStore.ts b/front/src/Connexion/LocalUserStore.ts index ace7b17e..065c8839 100644 --- a/front/src/Connexion/LocalUserStore.ts +++ b/front/src/Connexion/LocalUserStore.ts @@ -1,60 +1,61 @@ -import {areCharacterLayersValid, isUserNameValid, LocalUser} from "./LocalUser"; +import { areCharacterLayersValid, isUserNameValid, LocalUser } from "./LocalUser"; -const playerNameKey = 'playerName'; -const selectedPlayerKey = 'selectedPlayer'; -const customCursorPositionKey = 'customCursorPosition'; -const characterLayersKey = 'characterLayers'; -const companionKey = 'companion'; -const gameQualityKey = 'gameQuality'; -const videoQualityKey = 'videoQuality'; -const audioPlayerVolumeKey = 'audioVolume'; -const audioPlayerMuteKey = 'audioMute'; -const helpCameraSettingsShown = 'helpCameraSettingsShown'; -const fullscreenKey = 'fullscreen'; +const playerNameKey = "playerName"; +const selectedPlayerKey = "selectedPlayer"; +const customCursorPositionKey = "customCursorPosition"; +const characterLayersKey = "characterLayers"; +const companionKey = "companion"; +const gameQualityKey = "gameQuality"; +const videoQualityKey = "videoQuality"; +const audioPlayerVolumeKey = "audioVolume"; +const audioPlayerMuteKey = "audioMute"; +const helpCameraSettingsShown = "helpCameraSettingsShown"; +const fullscreenKey = "fullscreen"; +const lastRoomUrl = "lastRoomUrl"; class LocalUserStore { saveUser(localUser: LocalUser) { - localStorage.setItem('localUser', JSON.stringify(localUser)); + localStorage.setItem("localUser", JSON.stringify(localUser)); } - getLocalUser(): LocalUser|null { - const data = localStorage.getItem('localUser'); + getLocalUser(): LocalUser | null { + const data = localStorage.getItem("localUser"); return data ? JSON.parse(data) : null; } - setName(name:string): void { + setName(name: string): void { localStorage.setItem(playerNameKey, name); } - getName(): string|null { - const value = localStorage.getItem(playerNameKey) || ''; + getName(): string | null { + const value = localStorage.getItem(playerNameKey) || ""; return isUserNameValid(value) ? value : null; } setPlayerCharacterIndex(playerCharacterIndex: number): void { - localStorage.setItem(selectedPlayerKey, ''+playerCharacterIndex); + localStorage.setItem(selectedPlayerKey, "" + playerCharacterIndex); } getPlayerCharacterIndex(): number { - return parseInt(localStorage.getItem(selectedPlayerKey) || ''); + return parseInt(localStorage.getItem(selectedPlayerKey) || ""); } - setCustomCursorPosition(activeRow:number, selectedLayers: number[]): void { - localStorage.setItem(customCursorPositionKey, JSON.stringify({activeRow, selectedLayers})); + setCustomCursorPosition(activeRow: number, selectedLayers: number[]): void { + localStorage.setItem(customCursorPositionKey, JSON.stringify({ activeRow, selectedLayers })); } - getCustomCursorPosition(): {activeRow:number, selectedLayers:number[]}|null { + getCustomCursorPosition(): { activeRow: number; selectedLayers: number[] } | null { return JSON.parse(localStorage.getItem(customCursorPositionKey) || "null"); } setCharacterLayers(layers: string[]): void { localStorage.setItem(characterLayersKey, JSON.stringify(layers)); } - getCharacterLayers(): string[]|null { + getCharacterLayers(): string[] | null { const value = JSON.parse(localStorage.getItem(characterLayersKey) || "null"); return areCharacterLayersValid(value) ? value : null; } - setCompanion(companion: string|null): void { + setCompanion(companion: string | null): void { return localStorage.setItem(companionKey, JSON.stringify(companion)); } - getCompanion(): string|null { + getCompanion(): string | null { const companion = JSON.parse(localStorage.getItem(companionKey) || "null"); if (typeof companion !== "string" || companion === "") { @@ -68,45 +69,52 @@ class LocalUserStore { } setGameQualityValue(value: number): void { - localStorage.setItem(gameQualityKey, '' + value); + localStorage.setItem(gameQualityKey, "" + value); } getGameQualityValue(): number { - return parseInt(localStorage.getItem(gameQualityKey) || '60'); + return parseInt(localStorage.getItem(gameQualityKey) || "60"); } setVideoQualityValue(value: number): void { - localStorage.setItem(videoQualityKey, '' + value); + localStorage.setItem(videoQualityKey, "" + value); } getVideoQualityValue(): number { - return parseInt(localStorage.getItem(videoQualityKey) || '20'); + return parseInt(localStorage.getItem(videoQualityKey) || "20"); } setAudioPlayerVolume(value: number): void { - localStorage.setItem(audioPlayerVolumeKey, '' + value); + localStorage.setItem(audioPlayerVolumeKey, "" + value); } getAudioPlayerVolume(): number { - return parseFloat(localStorage.getItem(audioPlayerVolumeKey) || '1'); + return parseFloat(localStorage.getItem(audioPlayerVolumeKey) || "1"); } setAudioPlayerMuted(value: boolean): void { localStorage.setItem(audioPlayerMuteKey, value.toString()); } getAudioPlayerMuted(): boolean { - return localStorage.getItem(audioPlayerMuteKey) === 'true'; + return localStorage.getItem(audioPlayerMuteKey) === "true"; } setHelpCameraSettingsShown(): void { - localStorage.setItem(helpCameraSettingsShown, '1'); + localStorage.setItem(helpCameraSettingsShown, "1"); } getHelpCameraSettingsShown(): boolean { - return localStorage.getItem(helpCameraSettingsShown) === '1'; + return localStorage.getItem(helpCameraSettingsShown) === "1"; } setFullscreen(value: boolean): void { localStorage.setItem(fullscreenKey, value.toString()); } getFullscreen(): boolean { - return localStorage.getItem(fullscreenKey) === 'true'; + return localStorage.getItem(fullscreenKey) === "true"; + } + + setLastRoomUrl(roomUrl: string): void { + localStorage.setItem(lastRoomUrl, roomUrl.toString()); + } + getLastRoomUrl(): string { + return localStorage.getItem(lastRoomUrl) ?? ""; } } diff --git a/front/src/Network/ServiceWorker.ts b/front/src/Network/ServiceWorker.ts new file mode 100644 index 00000000..9bbcca85 --- /dev/null +++ b/front/src/Network/ServiceWorker.ts @@ -0,0 +1,20 @@ +export class _ServiceWorker { + constructor() { + if ("serviceWorker" in navigator) { + this.init(); + } + } + + init() { + window.addEventListener("load", () => { + navigator.serviceWorker + .register("/resources/service-worker.js") + .then((serviceWorker) => { + console.info("Service Worker registered: ", serviceWorker); + }) + .catch((error) => { + console.error("Error registering the Service Worker: ", error); + }); + }); + } +} diff --git a/front/src/index.ts b/front/src/index.ts index da243bde..6d2931a7 100644 --- a/front/src/index.ts +++ b/front/src/index.ts @@ -162,16 +162,3 @@ const app = new App({ }); export default app; - -if ("serviceWorker" in navigator) { - window.addEventListener("load", function () { - navigator.serviceWorker - .register("/resources/service-worker.js") - .then((serviceWorker) => { - console.log("Service Worker registered: ", serviceWorker); - }) - .catch((error) => { - console.error("Error registering the Service Worker: ", error); - }); - }); -}