6.9 KiB
Cycle de vie de l'application
Menu principal
Lorsque l'on lance l'application pour la première fois,
le premier composant qui s'affiche est App.tsx
.
En fonction de la valeur de FAST_BOOT
,
il peut soit afficher un menu principal (composant MainMenu
) si FAST_BOOT=false
ou soit afficher l'editeur directement (composant Editor
).
Lorsque le menu principal est affiché il y a 3 états : Main, Load ou Loading.
Lorsqu'on est dans Main, nous avons les deux boutons principaux affiché : Start from scratch ou Load
Start from scratch
Lorsque l'on clique sur Start from scratch, l'action qui se fait est de charger une configuration depuis l'API (ou d'utiliser une configuration préchargée via un event custom). On passe à l'état Loading.
Après chargement, isLoaded
est true
et le composant Editor
est affiché.
Load
Quand on veut charger un json, on clique sur Load et l'état passe à Load.
Ici, le composant MainMenu
affichera un bouton pour charger un fichier.
Charger le fichier change l'état de la configuration dans App
et active isLoaded
qui enfin affiche Editor
.
Editor
Lorsque Editor
est affiché,
il reçoit en props la configuration que l'on a chargé au menu principal.
Il reçoit également un historique par défaut pour avoir au moins quelque chose d'affiché.
Il reçoit root
un element HTML où l'on insert SVGLayoutDesigner par main.tsx
.
root
est utilisé pour lui donner des events qui seront utilisé pour communiquer avec le SmartComponent.
Plusieurs sous-composant sont affichés ensuite. Vous pouvez en savoir plus sur la structure de composants avec dragrams.net.
Save and load
Pour enregistrer le travail fait, il existe plusieurs méthode de le faire.
SmartComponent
La première, en passant par le SmartComponent svg-layout-designer.ts
, est de stringifier l'état de l'éditeur
et de le sauvegarder quelque part.
On utilise GetEditorAsString
pour obtenir une version stringifié du projet
que l'on peut ensuite sauvegarder dans un fichier, bdd ou autre.
On peut également utiliser GetEditorState et le sauvegarder tel quel dans le JS
mais on ne peut pas le stringifier à un object json ne peut pas avoir deux références
(sauf si on a le code source de saveload.ts,
utilisant un replacer éliminant les références circulaires,
voir JSON par interaction.
Pour charger l'état de l'éditeur, il faut en premier le parser avec JSON.parse
(pas de soucis à ce niveau là);
Enfin, on peut ensuite utiliser LoadEditor
du SmartComponent pour charger l'état.
LoadEditor
est une macro de trois autres appels :
ReviveEditorState
qui fait revivre tous les doublons de référencesSetEditor
qui charge la configuration de l'applicationSetHistory
qui charge l'historique de l'editeur
La raison que l'on utilise SetHistory
en plus de SetEditor
est parce que,
SetEditor
ne fait que charger une configuration par défaut de App.tsx
(exemple: lorsque l'on crée un conteneur, l'éditeur va lire cette configuration).
Si l'application est déjà chargée, c'est-à-dire que isLoaded de App
est true
,
alors l'application ne va pas relire history
et historyCurrentStep
et l'application n'aura pas chargé.
C'est pourquoi on a besoin de SetHistory
pour charger l'état courant dans Editor.tsx
(à l'opposé de App.tsx
).
Note: Pour rappel, App n'est qu'un menu principal. Comme dans un menu principal de jeu vidéo, écraser une sauvegarde ne fait pas charger la sauvegarde. C'est en chargeant la sauvegarde en cours de jeu ou dans le menu principal, que la partie change.
JSON par interaction
On peut charger un fichier JSON manuellement.
Pour cela il faut que FAST_BOOT
de default.ts
soit désactivé.
Dans l'éditeur, on peut exporter une configuration par fichier JSON grâce au composant Settings.tsx
.
Cette sauvegarde, comme pour sauvegarder avec le SmartComponent, utilise JSON.stringify
.
Et donc utilise un replacer pour supprimer les dépendances circulaires.
Ce replacer se trouve dans worker.js
mais
un fallback est également donné dans saveload.ts
dans le cas où la fonctionnalité de Web Worker n'est pas supportée par le navigateur web.
Pour corriger des bugs sur la sauvegarde, il faudra donc modifier ces deux fichiers.
Décrivons rapidement ce qu'elle fait :
export function GetCircularReplacer(): (key: any, value: object | Map<string, any> | null) => object | null | undefined {
return (key: any, value: object | null) => {
if (key === 'parent') {
return;
}
if (key === 'containers') {
return Array.from((value as Map<string, any>).entries());
}
if (key === 'symbols') {
return Array.from((value as Map<string, any>).entries());
}
if (key === 'linkedContainers') {
return Array.from(value as Set<string>);
}
return value;
};
}
Nous faisons les actions suivantes pour supprimer les types non supportés par le stringify :
- nous transformons les Map
containers
etsymbols
en tableau de vecteur clés-valeurs - nous transformons les Set
linkedContainers
en tableau de clés.
Nous supprimons toutes les références de parent
qui sont déjà référencés dans containers
.
Enfin, nous retournons value
si tout est bon.
Normalement, le JSON retourné ressemble à l'objet qu'était EditorState mais sans les références de parent et avec des tableaux partout.
Pour le charger, il faut revenir au menu principal et cliquer sur Load pour charger avec l'input.
Charger le fichier JSON, utilisera ce qu'on appelle un reviver qui s'occupe de recréer les types non supportés par JSON et de remettre les références dupliquées.
Ce reviver se trouve dans saveload.ts
avec la fonction Revive()
.
Revive
change la valeur de historyCurrentStep
avec de faire revivre l'historique
ReviveHistory
itère sur chaque état de history
pour les revivre.
ReviveState
fait revivre en premier les Map et les Set.
Ensuite, il va itérer sur tous les conteneurs existants,
pour remettre leur parent.
Comme vous pouvez vous en douter cette algorithme coûte O(n) juste pour revivre les parents. avec n le nombre total de conteneurs sur toute la durée de vie de l'application. Surtout qu'il n'est plus très utile depuis que tous les conteneurs sont dans une hash Map (et que donc parent est accessible avec un coût O(1)).
On pourrait juste utiliser GetContainerById
au lieu de la référence.
JSON par requête GET HTTP
Pour charger un JSON via HTTP GET, il faut que le serveur web distant autorise notre domaine dans le méchanisme cross-origin resource sharing (CORS) et qu'il nous autorise à faire des requêtes GET.
Après cela pour charger le JSON, il faut que l'url de SVGLayoutDesigner soit paramétrée avec l'url de la resource :
http://localhost:5173/?state=http://other-server.com/state.json
Après cela, l'éditeur chargera directement le fichier.