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,
+ },
+ };
+ }
}