From d2f035b1efa7d1aed7afc41f299371fd6f1a9323 Mon Sep 17 00:00:00 2001 From: JonathanMM Date: Thu, 18 May 2023 11:14:32 +0200 Subject: [PATCH] Ajout option pour exporter les statistiques --- public/jeu.css | 14 +++++-- ts/configurationPanel.ts | 49 ++++++++++++++++++++++++ ts/copieHelper.ts | 54 ++++++++++++++++++++++++++ ts/finDePartiePanel.ts | 37 +++--------------- ts/gestionnaire.ts | 24 ++++++------ ts/lienHelper.ts | 19 ++++++++++ ts/sauvegardeur.ts | 82 +++++++++++++++++++++++++++++++++++++++- 7 files changed, 232 insertions(+), 47 deletions(-) create mode 100644 ts/copieHelper.ts create mode 100644 ts/lienHelper.ts diff --git a/public/jeu.css b/public/jeu.css index c8b8128..ee92990 100644 --- a/public/jeu.css +++ b/public/jeu.css @@ -397,16 +397,16 @@ h1 { text-align: left; } -#fin-de-partie-panel-resume-bouton { +.bouton-partage { text-decoration: none; } -#fin-de-partie-panel-resume-bouton-icone { +.bouton-partage svg { width: 20px; height: 20px; } -#fin-de-partie-panel-resume-bouton-texte { +.bouton-partage-texte { text-decoration: underline; } @@ -415,6 +415,14 @@ h1 { padding-left: 0.5em; } +#config-sauvegarde-area { + text-align: start; +} + +#config-sauvegarde-area p { + margin: 0; +} + @media (max-width: 1024px) { #contenu { margin-left: 2px; diff --git a/ts/configurationPanel.ts b/ts/configurationPanel.ts index 63619c3..34e18be 100644 --- a/ts/configurationPanel.ts +++ b/ts/configurationPanel.ts @@ -7,6 +7,7 @@ import { ClavierDisposition } from "./entites/clavierDisposition"; import Input from "./input"; import ThemeManager from "./themeManager"; import { Theme } from "./entites/theme"; +import CopieHelper from "./copieHelper"; export default class ConfigurationPanel { private readonly _panelManager: PanelManager; @@ -128,6 +129,8 @@ export default class ConfigurationPanel { ) ); + contenu.appendChild(this.genererZoneExportSauvegarde()); + this._panelManager.setContenuHtmlElement(titre, contenu); this._panelManager.setClasses(["config-panel"]); this._panelManager.afficherPanel(); @@ -160,6 +163,52 @@ export default class ConfigurationPanel { return div; } + private genererZoneExportSauvegarde(): HTMLElement { + let div = document.createElement("div"); + div.id = "config-sauvegarde-area"; + + const titreSection = document.createElement("h3"); + titreSection.innerText = "Exporter vos statistiques"; + div.appendChild(titreSection); + + const explication = document.createElement("p"); + explication.innerText = "Pour transférer vos statistiques sur un autre navigateur, il est possible de suivre les étapes suivantes :"; + div.appendChild(explication); + + const listeEtape = document.createElement("ol"); + + const etape1 = document.createElement("li"); + + const etape1Texte = document.createElement("p"); + etape1Texte.innerText = "Copiez ce lien à usage unique."; + etape1.appendChild(etape1Texte); + + const etape1Input = document.createElement("input"); + const contenuLien = Sauvegardeur.genererLien(); + const lien = window.location.origin + window.location.pathname + "#" + btoa("s=" + contenuLien); + etape1Input.value = lien; + etape1Input.readOnly = true; + etape1.appendChild(etape1Input); + + const etape1Bouton = CopieHelper.creerBoutonPartage("config-sauvegarde-bouton"); + CopieHelper.attacheBoutonCopieLien(etape1Bouton, lien, "Lien copié dans le presse papier."); + etape1.appendChild(etape1Bouton); + + listeEtape.appendChild(etape1); + + const etape2 = document.createElement("li"); + etape2.innerText = "Envoyez le lien vers votre autre appareil."; + listeEtape.appendChild(etape2); + + const etape3 = document.createElement("li"); + etape3.innerText = "Ouvrez ce lien dans votre autre navigateur."; + listeEtape.appendChild(etape3); + + div.appendChild(listeEtape); + + return div; + } + public setInput(input: Input): void { this._input = input; } diff --git a/ts/copieHelper.ts b/ts/copieHelper.ts new file mode 100644 index 0000000..b4b598f --- /dev/null +++ b/ts/copieHelper.ts @@ -0,0 +1,54 @@ +import NotificationMessage from "./notificationMessage"; + +export default class CopieHelper { + public static attacheBoutonCopieLien(bouton: HTMLElement, lien: string, messageSucces: string): void { + bouton.addEventListener("click", (event) => { + event.stopPropagation(); + new Promise((resolve, reject) => { + if (window.navigator.clipboard !== undefined) { + return resolve(window.navigator.clipboard.writeText(lien)); + } + + return reject(); + }) + .catch( + () => + new Promise((resolve, reject) => { + if (window.navigator.share !== undefined) return resolve(navigator.share({ text: lien })); + + return reject(); + }) + ) + .then(() => { + NotificationMessage.ajouterNotificationPanel(messageSucces, bouton); + }) + .catch((raison) => { + NotificationMessage.ajouterNotificationPanel("Votre navigateur n'est pas compatible.", bouton); + }); + }); + } + + public static creerBoutonPartage(idBouton: string, label?: string): HTMLElement { + const lien = document.createElement("a"); + lien.id = idBouton; + lien.className = "bouton-partage"; + lien.href = "#"; + + const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg"); + const useSvg = document.createElementNS("http://www.w3.org/2000/svg", "use") as SVGUseElement; + useSvg.setAttribute("href", "#icone-copie"); + useSvg.setAttribute("stroke", "var(--couleur-icone)"); + useSvg.setAttribute("fill", "var(--couleur-icone)"); + svg.appendChild(useSvg); + lien.appendChild(svg); + + if (label) { + const texteBouton = document.createElement("span"); + texteBouton.className = "bouton-partage-texte"; + texteBouton.innerText = label; + lien.appendChild(texteBouton); + } + + return lien; + } +} diff --git a/ts/finDePartiePanel.ts b/ts/finDePartiePanel.ts index 47cd1fc..204fbca 100644 --- a/ts/finDePartiePanel.ts +++ b/ts/finDePartiePanel.ts @@ -1,8 +1,8 @@ +import CopieHelper from "./copieHelper"; import Configuration from "./entites/configuration"; import LettreResultat from "./entites/lettreResultat"; import { LettreStatut } from "./entites/lettreStatut"; import InstanceConfiguration from "./instanceConfiguration"; -import NotificationMessage from "./notificationMessage"; import PanelManager from "./panelManager"; import Sauvegardeur from "./sauvegardeur"; @@ -104,31 +104,8 @@ export default class FinDePartiePanel { } private attacherPartage(): void { - let resumeBouton = document.getElementById("fin-de-partie-panel-resume-bouton") as HTMLElement; - resumeBouton.addEventListener("click", (event) => { - event.stopPropagation(); - new Promise((resolve, reject) => { - if (window.navigator.clipboard !== undefined) { - return resolve(window.navigator.clipboard.writeText(this._resumeTexte + "\n\nhttps://sutom.nocle.fr")); - } - - return reject(); - }) - .catch( - () => - new Promise((resolve, reject) => { - if (window.navigator.share !== undefined) return resolve(navigator.share({ text: this._resumeTexte + "\n\nhttps://sutom.nocle.fr" })); - - return reject(); - }) - ) - .then(() => { - NotificationMessage.ajouterNotificationPanel("Résumé copié dans le presse-papier.", resumeBouton); - }) - .catch((raison) => { - NotificationMessage.ajouterNotificationPanel("Votre navigateur n'est pas compatible.", resumeBouton); - }); - }); + const resumeBouton = document.getElementById("fin-de-partie-panel-resume-bouton") as HTMLElement; + CopieHelper.attacheBoutonCopieLien(resumeBouton, this._resumeTexte + "\n\nhttps://sutom.nocle.fr", "Résumé copié dans le presse papier."); } public afficher(): void { @@ -152,12 +129,10 @@ export default class FinDePartiePanel { Peut-être feras-tu mieux demain ? \

"; } + contenu += "

Résumé de ta partie − "; + contenu += CopieHelper.creerBoutonPartage("fin-de-partie-panel-resume-bouton", "Partager").outerHTML; contenu += - '

Résumé de ta partie − \ - \ - \ - \ - Partager

\ + '

\
' +
         this._resumeTexteLegacy +
         "
"; diff --git a/ts/gestionnaire.ts b/ts/gestionnaire.ts index a1d4444..f6701be 100644 --- a/ts/gestionnaire.ts +++ b/ts/gestionnaire.ts @@ -15,6 +15,7 @@ import ConfigurationPanel from "./configurationPanel"; import AudioPanel from "./audioPanel"; import ThemeManager from "./themeManager"; import InstanceConfiguration from "./instanceConfiguration"; +import LienHelper from "./lienHelper"; export default class Gestionnaire { private _grille: Grille | null = null; @@ -83,19 +84,9 @@ export default class Gestionnaire { } private getIdPartie(partieEnCours: PartieEnCours) { - if (window.location.hash !== "" && window.location.hash !== "#") { - let hashPart = atob(window.location.hash.substring(1)).split("/"); - for (let infoPos in hashPart) { - let info = hashPart[infoPos]; - if (!info.includes("=")) continue; - let infoPart = info.split("="); - let infoKey = infoPart[0]; + const infoDansLocation = LienHelper.extraireInformation("p"); - if (infoKey !== "p") continue; - - return infoPart[1]; - } - } + if (infoDansLocation !== null) return infoDansLocation; if (partieEnCours.idPartie !== undefined) return partieEnCours.idPartie; @@ -120,6 +111,15 @@ export default class Gestionnaire { } private enregistrerPartieDansStats(): void { + // On regarde si c'est le même jour que la dernière partie dans les stats. + // Si c'est identique, on ne sauvegarde pas + if ( + this._stats.dernierePartie.getFullYear() === this._datePartieEnCours.getFullYear() && + this._stats.dernierePartie.getMonth() === this._datePartieEnCours.getMonth() && + this._stats.dernierePartie.getDate() === this._datePartieEnCours.getDate() + ) + return; + this._stats.partiesJouees++; let estVictoire = this._resultats.some((resultat) => resultat.every((item) => item.statut === LettreStatut.BienPlace)); if (estVictoire) { diff --git a/ts/lienHelper.ts b/ts/lienHelper.ts new file mode 100644 index 0000000..9aa16f0 --- /dev/null +++ b/ts/lienHelper.ts @@ -0,0 +1,19 @@ +export default class LienHelper { + public static extraireInformation(cle: string): string | null { + if (window.location.hash === "" || window.location.hash === "#") return null; + + let hashPart = atob(window.location.hash.substring(1)).split("/"); + for (let infoPos in hashPart) { + let info = hashPart[infoPos]; + if (!info.includes("=")) continue; + let infoPart = info.split("="); + let infoKey = infoPart[0]; + + if (infoKey !== cle) continue; + + return infoPart[1]; + } + + return null; + } +} diff --git a/ts/sauvegardeur.ts b/ts/sauvegardeur.ts index 588236d..79d1818 100644 --- a/ts/sauvegardeur.ts +++ b/ts/sauvegardeur.ts @@ -2,6 +2,8 @@ import Configuration from "./entites/configuration"; import PartieEnCours from "./entites/partieEnCours"; import SauvegardePartie from "./entites/sauvegardePartie"; import SauvegardeStats from "./entites/sauvegardeStats"; +import LienHelper from "./lienHelper"; +import NotificationMessage from "./notificationMessage"; export default class Sauvegardeur { private static readonly _cleStats = "statistiques"; @@ -13,7 +15,21 @@ export default class Sauvegardeur { } public static chargerSauvegardeStats(): SauvegardeStats | undefined { - let dataStats = localStorage.getItem(this._cleStats); + const contenuLocation = LienHelper.extraireInformation("s"); + + if (contenuLocation) { + const donneesDepuisLien = Sauvegardeur.chargerInformationDepuisLien(contenuLocation); + window.location.hash = ""; + if (donneesDepuisLien) { + NotificationMessage.ajouterNotification("Statistiques chargés avec succès."); + Sauvegardeur.sauvegarderStats(donneesDepuisLien); + return donneesDepuisLien; + } + + NotificationMessage.ajouterNotification("Impossible de charger les statistiques depuis le lien."); + } + + const dataStats = localStorage.getItem(this._cleStats); if (!dataStats) return; let stats = JSON.parse(dataStats) as SauvegardeStats; @@ -66,4 +82,68 @@ export default class Sauvegardeur { let config = JSON.parse(dataConfig) as Configuration; return config; } + + public static genererLien(): string { + const stats = Sauvegardeur.chargerSauvegardeStats() ?? SauvegardeStats.Default; + return [ + stats.repartition[1], + stats.repartition[2], + stats.repartition[3], + stats.repartition[4], + stats.repartition[5], + stats.repartition[6], + stats.repartition["-"], + stats.lettresRepartitions.bienPlace, + stats.lettresRepartitions.malPlace, + stats.lettresRepartitions.nonTrouve, + stats.dernierePartie, + ].join(","); + } + + private static chargerInformationDepuisLien(contenu: string): SauvegardeStats | null { + const [ + UnCoupString, + DeuxCoupsString, + TroisCoupsString, + QuatreCoupsString, + CinqCoupsString, + SixCoupsString, + PerduString, + LettresBienPlaceesString, + LettresMalPlaceesString, + LettresNonTrouveString, + dernierePartie, + ] = contenu.split(","); + + const UnCoup = parseInt(UnCoupString); + const DeuxCoups = parseInt(DeuxCoupsString); + const TroisCoups = parseInt(TroisCoupsString); + const QuatreCoups = parseInt(QuatreCoupsString); + const CinqCoups = parseInt(CinqCoupsString); + const SixCoups = parseInt(SixCoupsString); + const Perdu = parseInt(PerduString); + const LettresBienPlacees = parseInt(LettresBienPlaceesString); + const LettresMalPlacees = parseInt(LettresMalPlaceesString); + const LettresNonTrouve = parseInt(LettresNonTrouveString); + + return { + dernierePartie: new Date(dernierePartie), + partiesJouees: UnCoup + DeuxCoups + TroisCoups + QuatreCoups + CinqCoups + SixCoups + Perdu, + partiesGagnees: UnCoup + DeuxCoups + TroisCoups + QuatreCoups + CinqCoups + SixCoups, + repartition: { + 1: UnCoup, + 2: DeuxCoups, + 3: TroisCoups, + 4: QuatreCoups, + 5: CinqCoups, + 6: SixCoups, + "-": Perdu, + }, + lettresRepartitions: { + bienPlace: LettresBienPlacees, + malPlace: LettresMalPlacees, + nonTrouve: LettresNonTrouve, + }, + }; + } }