svg-layout-designer-react/docs/#Project/Pages/Application.md
2022-10-17 15:42:53 +02:00

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é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 :

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.