import { Component, ApplicationRef, OnInit, OnDestroy, HostListener, ViewChild, ComponentRef } from "@angular/core"; import { Router, Event, NavigationEnd, ActivationEnd } from "@angular/router"; import { Observer, jalhydDateRev } from "jalhyd"; import { environment } from "../environments/environment"; import { InternationalisationService } from "./services/internationalisation/internationalisation.service"; import { ErrorService } from "./services/error/error.service"; import { FormulaireService } from "./services/formulaire/formulaire.service"; import { FormulaireDefinition } from "./formulaire/definition/form-definition"; import { ServiceFactory } from "./services/service-factory"; import { ParamService } from "./services/param/param.service"; import { ApplicationSetupService } from "./services/app-setup/app-setup.service"; import { HttpService } from "./services/http/http.service"; import { LoadCalcDialogAnchorDirective } from "./components/load-calculator/load-calculator-anchor.directive"; import { LoadCalculatorComponent } from "./components/load-calculator/load-calculator.component"; import { SaveCalcDialogAnchorDirective } from "./components/save-calculator/save-calculator-anchor.directive"; import { SaveCalculatorComponent } from "./components/save-calculator/save-calculator.component"; import { nghydDateRev } from "../date_revision"; import { MatSidenav, MatToolbar, MatDialog } from "@angular/material"; import { DialogConfirmEmptySessionComponent } from "./components/dialog-confirm-empty-session/dialog-confirm-empty-session.component"; @Component({ selector: "nghyd-app", templateUrl: "./app.component.html", styleUrls: ["./app.component.scss"], providers: [ErrorService] }) export class AppComponent implements OnInit, OnDestroy, Observer { @ViewChild("sidenav") public sidenav: MatSidenav; @ViewChild("navbar") public navbar: MatToolbar; /** current calculator, inferred from _currentFormId by setActiveCalc() (used for navbar menu) */ public currentCalc: any; /** * liste des calculettes. Forme des objets : * "title": nom de la calculette * "uid": id unique du formulaire */ private _calculators: any[] = []; /** * id du formulaire courant * on utilise pas directement FormulaireService.currentFormId pour éviter l'erreur * ExpressionChangedAfterItHasBeenCheckedError */ private _currentFormId: string; private _innerWidth: number; @ViewChild(LoadCalcDialogAnchorDirective) private appLoadCalcDialogAnchor: LoadCalcDialogAnchorDirective; @ViewChild(SaveCalcDialogAnchorDirective) private appSaveCalcDialogAnchor: SaveCalcDialogAnchorDirective; /** * composant actuellement affiché par l'élément <router-outlet> */ private _routerCurrentComponent: Component; constructor( private intlService: InternationalisationService, private paramService: ParamService, private appSetupService: ApplicationSetupService, private appRef: ApplicationRef, private errorService: ErrorService, private router: Router, private formulaireService: FormulaireService, private httpService: HttpService, private confirmEmptySessionDialog: MatDialog ) { ServiceFactory.instance.applicationSetupService = appSetupService; ServiceFactory.instance.paramService = paramService; ServiceFactory.instance.internationalisationService = intlService; ServiceFactory.instance.formulaireService = formulaireService; ServiceFactory.instance.httpService = httpService; this.router.events.subscribe((event: Event) => { // close side navigation when clicking a calculator tab if (event instanceof NavigationEnd) { this.sidenav.close(); } // [de]activate calc tabs depending on loaded route if (event instanceof ActivationEnd) { const path = event.snapshot.url[0].path; if (path === "calculator") { const calcUid = event.snapshot.params.uid; this.setActiveCalc(calcUid); } else { this.setActiveCalc(null); } } }); } ngOnInit() { this.intlService.addObserver(this); this.intlService.setLocale("fr"); this.formulaireService.addObserver(this); this.subscribeErrorService(); this._innerWidth = window.innerWidth; } ngOnDestroy() { this.unsubscribeErrorService(); this.formulaireService.removeObserver(this); } @HostListener("window:resize", ["$event"]) onResize(event) { // keep track of window size for navbar tabs arrangement this._innerWidth = window.innerWidth; } public get uitextSidenavNewCalc() { return this.intlService.localizeText("INFO_MENU_NOUVELLE_CALC"); } public get uitextSidenavParams() { return this.intlService.localizeText("INFO_SETUP_TITLE"); } public get uitextSidenavLoadSession() { return this.intlService.localizeText("INFO_MENU_LOAD_SESSION_TITLE"); } public get uitextSidenavSaveSession() { return this.intlService.localizeText("INFO_MENU_SAVE_SESSION_TITLE"); } public get uitextSidenavEmptySession() { return this.intlService.localizeText("INFO_MENU_EMPTY_SESSION_TITLE"); } public get uitextSidenavHelp() { return this.intlService.localizeText("INFO_MENU_HELP_TITLE"); } public get uitextSelectCalc() { return this.intlService.localizeText("INFO_MENU_SELECT_CALC"); } public get calculators() { return this._calculators; } public get currentFormId() { return this._currentFormId; } public setActiveCalc(uid: string) { this._calculators.forEach((calc) => { calc.active = (calc.uid === uid); }); // mark current calc for navbar menu const index = this.getCalculatorIndexFromId(uid); this.currentCalc = this._calculators[index]; } /** * Returns true if sum of open calculator tabs witdh is lower than navbar * available space (ie. if navbar is not overflowing), false otherwise */ public get tabsFitInNavbar() { // manual breakpoints // @WARNING keep in sync with .calculator-buttons sizes in app.component.scss let tabsLimit = 0; if (this._innerWidth > 480) { tabsLimit = 3; } if (this._innerWidth > 640) { tabsLimit = 4; } if (this._innerWidth > 800) { tabsLimit = 6; } /*if (this._innerWidth > 1200) { tabsLimit = 8; }*/ const fits = this._calculators.length <= tabsLimit; return fits; } /** * abonnement au service d'erreurs */ private subscribeErrorService() { this.errorService.addObserver(this); } private unsubscribeErrorService() { this.errorService.removeObserver(this); } private updateLocale() { const tag = this.intlService.currentLanguage.tag; document["locale"] = tag; // location.reload(true); // this.cdRef.markForCheck(); // this.cdRef.detectChanges(); this.appRef.tick(); } // interface Observer update(sender: any, data: any): void { if (sender instanceof ErrorService) { // on ouvre un dialogue avec le message d'erreur reçu // if (this._displayErrorDialog) { // let dialogRef = this.dialog.open(AlertDialog); // let ad: AlertDialog = dialogRef.componentInstance; // ad.text = String(data); // } // else console.log(data); } else if (sender instanceof FormulaireService) { switch (data["action"]) { case "createForm": const f: FormulaireDefinition = data["form"]; this._calculators.push( { "title": f.calculatorName, "type": this.formulaireService.getLocalisedTitleFromCalculatorType(f.calculatorType), "uid": f.uid } ); this.setActiveCalc(f.uid); // abonnement en tant qu'observateur du nouveau formulaire f.addObserver(this); break; case "invalidFormId": this.toList(); break; case "currentFormChanged": this._currentFormId = data["formId"]; break; case "saveForm": this.saveForm(data["form"]); break; case "closeForm": const form: FormulaireDefinition = data["form"]; this.closeCalculator(form); break; } } else if (sender instanceof InternationalisationService) { this.updateLocale(); } else if (sender instanceof FormulaireDefinition) { switch (data["action"]) { case "nameChanged": this.updateCalculatorTitle(sender, data["name"]); break; } } } private getCalculatorIndexFromId(formId: string) { const index = this._calculators.reduce((resultIndex, calc, currIndex) => { if (resultIndex === -1 && calc["uid"] === formId) { resultIndex = currIndex; } return resultIndex; }, -1); return index; } private updateCalculatorTitle(f: FormulaireDefinition, title: string) { const formIndex = this.getCalculatorIndexFromId(f.uid); this._calculators[formIndex]["title"] = title; } private saveSession(calcList: any[], filename) { const elems = []; for (const c of calcList) { if (c.selected) { const form: FormulaireDefinition = this.formulaireService.getFormulaireFromId(c.uid); elems.push(form.JSONserialise()); } } const session = { "session": { "elements": elems } }; this.formulaireService.saveSession(session, filename); } private closeCalculator(form: FormulaireDefinition) { const formId: string = form.uid; // désabonnement en tant qu'observateur form.removeObserver(this); // recherche de la calculette correspondante à formId const closedIndex = this.getCalculatorIndexFromId(formId); /* * détermination de la nouvelle calculette à afficher : * - celle après celle supprimée * - ou celle avant celle supprimée si on supprime la dernière */ let newId = null; const l = this._calculators.length; if (l > 1) { if (closedIndex === l - 1) { newId = this._calculators[closedIndex - 1]["uid"]; } else { newId = this._calculators[closedIndex + 1]["uid"]; } } // suppression this._calculators = this._calculators.filter(calc => { return formId !== calc["uid"]; }); // MAJ affichage if (newId === null) { this.toList(); this._currentFormId = null; } else { this.toCalc(newId); } } private toList() { this.router.navigate(["/list"]); } private toCalc(id: string) { this.router.navigate(["/calculator", id]); this.setActiveCalc(id); } /** * récupération du composant affiché par le routeur */ public onRouterOutletActivated(a) { this._routerCurrentComponent = a; } /** * restarts a fresh session by closing all calculators */ public emptySession() { const dialogRef = this.confirmEmptySessionDialog.open( DialogConfirmEmptySessionComponent, { disableClose: true } ); dialogRef.afterClosed().subscribe(result => { if (result) { for (const c of this._calculators) { const form = this.formulaireService.getFormulaireFromId(c.uid); this.closeCalculator(form); } } }); } public loadSession() { // création du dialogue de sélection des formulaires à sauver const compRef: ComponentRef<LoadCalculatorComponent> = this.appLoadCalcDialogAnchor.createDialog(); const prom: Promise<any[]> = compRef.instance.run(); prom.then(list => { this.formulaireService.loadSession(compRef.instance.selectedFile, compRef.instance.calculators); compRef.destroy(); }).catch(err => { }); } public getDateRevision(): string[] { const dr: string[] = [jalhydDateRev, nghydDateRev]; return dr; } /** * sauvegarde du/des formulaires * @param form formulaire à sélectionner par défaut dans la liste */ public saveForm(form?: FormulaireDefinition) { // création du dialogue de sélection des formulaires à sauver const compRef: ComponentRef<SaveCalculatorComponent> = this.appSaveCalcDialogAnchor.createDialog(); // création de la liste des formulaires const list = []; for (const c of this._calculators) { const uid = c["uid"]; list.push({ "selected": form ? (uid === form.uid) : true, "title": c["title"], "uid": uid }); } // passage de la liste, récupération d'une Promise pour traiter le résultat const prom: Promise<any[]> = compRef.instance.run(list); prom.then(innerList => { let name = compRef.instance.filename; // ajout extension ".json" const re = /.+\.json/; const match = re.exec(name.toLowerCase()); if (match === null) { name = name + ".json"; } this.saveSession(innerList, name); }).catch(err => { }); } /** * détection de la fermeture de la page/navigateur et demande de confirmation */ @HostListener("window:beforeunload", ["$event"]) confirmExit($event) { if (environment.production) { // affecter une valeur différente de null provoque l'affichage d'un dialogue de confirmation, mais le texte n'est pas affiché $event.returnValue = "Your data will be lost !"; } } }