179 lines
6.9 KiB
Markdown
179 lines
6.9 KiB
Markdown
# 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](./ComponentStructure.drawio)
|
|
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](#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érences
|
|
- `SetEditor` qui charge la configuration de l'application
|
|
- `SetHistory` 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 :
|
|
|
|
```typescript
|
|
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` et `symbols` 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.
|