diff --git a/.gitignore b/.gitignore index 5bd2d8af3276d30225062d5cddfe237c0578176d..d484f6a04277b6d27baea18c96717919acad33b7 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ /src/assets/docs /release /build +/docs/pdf_build # dependencies /node_modules diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index ed146e000f40c2e3b3874a3c1241896787708e44..92811cedb1fad95a05865e51acd895e050f929ce 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -79,19 +79,34 @@ test: script: - npm run e2e -build: +.build: stage: build - only: - - pushes - - tags - - schedules - - web artifacts: expire_in: 10 min paths: - dist/ script: - - npm run build + # -baseref option is used by npm to set the npm_config_basehref environment variable + # used in package.json + - npm run build-href -basehref=$BASEHREF + +build-dev: + extends: .build + only: + - tags + - pushes + - schedules + - web + variables: + BASEHREF: "/cassiopee/$CI_COMMIT_REF_NAME/" + +build-prod: + extends: .build + only: + - tags + - devel + variables: + BASEHREF: "/" clean-stale-branches: stage: clean-stale-branches @@ -109,7 +124,7 @@ deploy-dev: - tags - web dependencies: - - build + - build-dev script: # Copie de la branche / du tag - ./scripts/deploy-version.sh $CI_COMMIT_REF_NAME $DEV_LOGIN $DEV_HOST $DEV_PATH @@ -120,7 +135,7 @@ deploy-ovh-dev: only: - devel dependencies: - - build + - build-prod script: # Copie de la branche / du tag - ./scripts/deploy-version.sh prod-devel $PROD_LOGIN $PROD_HOST $PROD_DEV_PATH @@ -131,7 +146,7 @@ deploy-prod: variables: - $CI_COMMIT_REF_NAME == "stable" dependencies: - - build + - build-prod script: - ./scripts/deploy-version.sh prod $PROD_LOGIN $PROD_HOST $PROD_PATH diff --git a/README.md b/README.md index 5750c79b6ba0413eaaf5bd25fba416d4790817f8..0fc8a9725a79ebaa03c2684ef07a93917538361a 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ Requirements for developping Cassiopee can be achieved by manually install the r * npm * python3 * pandoc ^2 (optional, for PDF documentation only) - * texlive (optional, for PDF documentation only) + * texlive, texlive-bibtex-extra, texlive-latex-extra, latexmk (optional, for PDF documentation only) Building the HTML documentation requires MkDocs and some extensions: diff --git a/package.json b/package.json index 47e5d03490ed4a741f99a9d11dc7693f057d9ff7..b7cf7d4e8781930a18da8e1cc310cc48ecc3db55 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,8 @@ "preprocess": "mkdir -p build; node scripts/preprocessors.js; npm run service-worker-version; bash scripts/fix-chartjs-plugin-zoom-2.0.0.sh", "start": "npm run preprocess && npm run mkdocs && npm run ng serve -- --host 0.0.0.0 --poll 5000", "build-no-pdf": "npm run preprocess && npm run mkdocs && npm run ng build -- --configuration production", - "build": "npm run preprocess && npm run mkdocs && npm run ng build -- --configuration production && npm run mkdocs2pdf", + "build": "npm run build-href -basehref=/", + "build-href": "npm run preprocess && npm run mkdocs && npm run ng build -- --configuration production --base-href=$npm_config_basehref && npm run mkdocs2pdf", "update-dist-index-mimetypes": "node scripts/update-dist-index-mimetypes.js", "electron": "npm run update-dist-index-mimetypes && \"node_modules/.bin/electron\" .", "release-linux-nocompile": "npm run update-dist-index-mimetypes && \"node_modules/.bin/electron-builder\"", diff --git a/scripts/deploy-version.sh b/scripts/deploy-version.sh index 4221e281bf9a84801c54b96417c6fb24955543e7..4d5bd0687b2b11ebc1bce1b92ee80aa979e0f399 100755 --- a/scripts/deploy-version.sh +++ b/scripts/deploy-version.sh @@ -35,10 +35,6 @@ echo "$(basename $0): deploying version $VERSION in $LOGIN@$HOST:$DIR" if [[ $VERSION == "prod" || $VERSION == "prod-devel" ]]; then display_local_href - # Modification du dossier base href -> / - echo "updating index.html base href to /" - sed -i '/<base/s/href="[^"]*"/href="\/"/' $LOCAL_DIR/index.html - # Copie de la branche production rsync -a --delete --exclude=cassiopee-releases -e "ssh -o StrictHostKeyChecking=no" $LOCAL_DIR/ ${LOGIN}@${HOST}:${DIR}/ @@ -46,10 +42,6 @@ if [[ $VERSION == "prod" || $VERSION == "prod-devel" ]]; then else display_local_href - # Modification du dossier base href -> /cassiopee/version/ - echo "updating index.html base href to /cassiopee/$VERSION/" - sed -i "/<base/s/href=\"[^\"]*\"/href=\"\/cassiopee\/$VERSION\/\"/" $LOCAL_DIR/index.html - # Copie de la branche / du tag rsync -a --delete --exclude=cassiopee-releases -e "ssh -o StrictHostKeyChecking=no" $LOCAL_DIR/ "$LOGIN@$HOST:$DIR/$VERSION" diff --git a/scripts/mkdocs2pdf.py b/scripts/mkdocs2pdf.py index 6e3cf68040aa15e43debd48019a955e3e13d9b2a..37deb6a8f52ef1d99b61ccf8f1b618790e3fa4a2 100644 --- a/scripts/mkdocs2pdf.py +++ b/scripts/mkdocs2pdf.py @@ -20,6 +20,9 @@ import yaml import re import shutil +# verbose output +verbose = False + baseDir = os.getcwd() buildDir = os.path.join(baseDir, 'build') latexSourceDir = os.path.join(baseDir, 'docs/latex') @@ -38,6 +41,21 @@ def runCommand(cmd): if os.waitstatus_to_exitcode(os.system(cmd)) != 0: raise RuntimeError("error executing:",cmd) +# Create a symbolic link +def createLink(src): + # check if destination already exists + dest = os.path.basename(src) + if os.path.exists(dest): + if not os.path.islink(dest): + raise Exception('{} exists but is not a symbolic link'.format(dest)) + else: + runCommand('ln -s {}'.format(src)) + +def createEmptyDir(path): + if os.path.exists(path): + shutil.rmtree(path) + os.makedirs(path) + # Reads an MkDocs configuration file def readConfig(sYAML): f = open(sYAML, 'r') @@ -130,9 +148,14 @@ def convertMdToTex(filePath): def getLatexModel(): # Clone Git repository os.chdir(pdfBuildDir) - runCommand( - 'git clone {} {}'.format(latexModelRepository, latexModelDir) - ) + if os.path.isdir(latexModelDir): + # git directory exists, update it + os.chdir(latexModelDir) + runCommand('git pull') + # platform independent "cd .." + os.chdir(os.path.dirname(os.getcwd())) + else: + runCommand('git clone {} {}'.format(latexModelRepository, latexModelDir)) # back to original working drectory os.chdir(baseDir) @@ -146,35 +169,17 @@ def injectContentIntoModel(mergedDocFilenameTex, lang): # Symlink necessary resources os.chdir(modelDir) relPathToMergedTexDoc = os.path.join('..', mergedDocFilenameTex) - runCommand( - 'ln -s {} .'.format(relPathToMergedTexDoc) - ) + createLink(relPathToMergedTexDoc) latexTemplate = filenamePrefix + lang + '.tex' relPathToLatexTemplate = os.path.join(latexSourceDir, latexTemplate) - runCommand( - 'ln -s {}'.format(relPathToLatexTemplate) - ) - runCommand( - 'ln -s {}'.format(os.path.join(latexSourceDir, 'logo_pole.png')) - ) - runCommand( - 'ln -s {}/schema_rugosite_fond.png'.format(os.path.join(baseDir, 'docs', lang, 'calculators', 'pam')) - ) - runCommand( - 'ln -s {}/bloc_cylindre.png'.format(os.path.join(baseDir, 'docs', lang, 'calculators', 'pam')) - ) - runCommand( - 'ln -s {}/bloc_face_arrondie.png'.format(os.path.join(baseDir, 'docs', lang, 'calculators', 'pam')) - ) - runCommand( - 'ln -s {}/bloc_base_carree.png'.format(os.path.join(baseDir, 'docs', lang, 'calculators', 'pam')) - ) - runCommand( - 'rm rapport_inrae/logos.tex' - ) - runCommand( - 'ln -s {} rapport_inrae/'.format(os.path.join(latexSourceDir, 'logos.tex')) - ) + createLink(relPathToLatexTemplate) + createLink(os.path.join(latexSourceDir, 'logo_pole.png')) + createLink('{}/schema_rugosite_fond.png'.format(os.path.join(baseDir, 'docs', lang, 'calculators', 'pam'))) + createLink('{}/bloc_cylindre.png'.format(os.path.join(baseDir, 'docs', lang, 'calculators', 'pam'))) + createLink('{}/bloc_face_arrondie.png'.format(os.path.join(baseDir, 'docs', lang, 'calculators', 'pam'))) + createLink('{}/bloc_base_carree.png'.format(os.path.join(baseDir, 'docs', lang, 'calculators', 'pam'))) + runCommand('rm rapport_inrae/logos.tex') + createLink('{} rapport_inrae/'.format(os.path.join(latexSourceDir, 'logos.tex'))) # back to original working drectory os.chdir(baseDir) @@ -189,9 +194,11 @@ def buildPDF(lang): cvt = os.path.join(buildDir, 'cassiopee_version.tex') shutil.copy(cvt, modelDir) - os.system( - 'latexmk -f -xelatex -pdf -interaction=nonstopmode {} > /dev/null 2>&1'.format(sourceTexFile) - ) + if verbose: + os.system('latexmk -f -xelatex -pdf -interaction=nonstopmode {} > /dev/null'.format(sourceTexFile)) + else: + os.system('latexmk -f -xelatex -pdf -interaction=nonstopmode {} > /dev/null 2>&1'.format(sourceTexFile)) + # copy generated PDF to release directory shutil.copy(outputPdfFile, outputDir) # back to original working drectory @@ -201,9 +208,9 @@ def buildPDF(lang): def buildDocForLang(lang): # Prepare temporary build directory - os.makedirs(pdfBuildDir, exist_ok=True) + createEmptyDir(pdfBuildDir) # Prepare output directory - os.makedirs(outputDir, exist_ok=True) + createEmptyDir(outputDir) # Read config yamlPath = 'mkdocs/mkdocs-' + lang + '.yml' diff --git a/src/app/app.component.ts b/src/app/app.component.ts index d11194ecab2f9f8c1b5ed14ec25d6a671bc1bf23..0e1044ac651ec31ebd7d38d288ab52741aaea25c 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -22,7 +22,7 @@ import { DialogSaveSessionComponent } from "./components/dialog-save-session/dia import { QuicknavComponent } from "./components/quicknav/quicknav.component"; import { NotificationsService } from "./services/notifications.service"; -import { decodeHtml } from "./util"; +import { decodeHtml } from "./util/util"; import { HotkeysService, Hotkey } from "angular2-hotkeys"; @@ -33,6 +33,8 @@ import { saveAs } from "file-saver"; import * as XLSX from "xlsx"; import * as pako from "pako"; +import { DialogConfirmComponent } from "./components/dialog-confirm/dialog-confirm.component"; +import { UserConfirmationService } from "./services/user-confirmation.service"; import { ServiceWorkerUpdateService } from "./services/service-worker-update.service"; @Component({ @@ -85,13 +87,16 @@ export class AppComponent implements OnInit, OnDestroy, Observer { private confirmCloseCalcDialog: MatDialog, private hotkeysService: HotkeysService, private matomoTracker: MatomoTracker, - private serviceWorkerUpdateService: ServiceWorkerUpdateService + private confirmDialog: MatDialog, + private serviceWorkerUpdateService: ServiceWorkerUpdateService, + private userConfirmationService: UserConfirmationService ) { ServiceFactory.httpService = httpService; ServiceFactory.applicationSetupService = appSetupService; ServiceFactory.i18nService = intlService; ServiceFactory.formulaireService = formulaireService; ServiceFactory.notificationsService = notificationsService; + ServiceFactory.serviceWorkerUpdateService = serviceWorkerUpdateService; if (!isDevMode()) { // évite de mettre en place un bandeau RGPD @@ -218,10 +223,24 @@ export class AppComponent implements OnInit, OnDestroy, Observer { ngOnInit() { this.formulaireService.addObserver(this); this._innerWidth = window.innerWidth; + this.logRevisionInfo(); + + // Initialise communication with UserConfirmationService. + // When receiving a message from it, open a dialog to ask user to confirm. + // Will then reply to UserConfirmationService with a message holding confirmation status. + this.userConfirmationService.subscribe(this); + this.userConfirmationService.addHandler(this, { + next: (data) => this.displayConfirmationDialog(data["title"], data["body"]), + error: () => { }, + complete: () => { }, + }); } ngOnDestroy() { this.formulaireService.removeObserver(this); + + // cancel communication link with UserConfirmationService + this.userConfirmationService.unsubscribe(this); } @HostListener("window:resize", ["$event"]) @@ -670,6 +689,12 @@ export class AppComponent implements OnInit, OnDestroy, Observer { }; } + private logRevisionInfo() { + const ri = this.revisionInfo; + console.log("JaLHyd", ri.jalhyd.date, ri.jalhyd.version); + console.log("ngHyd", ri.nghyd.date, ri.nghyd.version); + } + /** * sauvegarde du/des formulaires * @param form formulaire à sélectionner par défaut dans la liste @@ -809,4 +834,24 @@ export class AppComponent implements OnInit, OnDestroy, Observer { } } } + + /** + * display a confirmation display upon request from UserConfirmationService + */ + private displayConfirmationDialog(title: string, text: string) { + const dialogRef = this.confirmDialog.open( + DialogConfirmComponent, + { + data: { + title: title, + text: text + }, + disableClose: true + } + ); + dialogRef.afterClosed().subscribe(result => { + // reply to UserConfirmationService + this.userConfirmationService.postConfirmation(this, { "confirm": result }); + }); + } } diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 15eac95408a49c29f830397b606d7f95990029fc..5c912764c5e680f134109936abe3681c7d0de46d 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -96,6 +96,7 @@ import { JetTrajectoryChartComponent } from "./components/jet-trajectory-chart/j import { SessionPropertiesComponent } from "./components/session-properties/session-properties.component"; import { VerificateurResultsComponent } from "./components/verificateur-results/verificateur-results.component"; +import { DialogConfirmComponent } from "./components/dialog-confirm/dialog-confirm.component"; import { DialogConfirmEmptySessionComponent } from "./components/dialog-confirm-empty-session/dialog-confirm-empty-session.component"; import { DialogConfirmCloseCalcComponent } from "./components/dialog-confirm-close-calc/dialog-confirm-close-calc.component"; import { DialogEditPabComponent } from "./components/dialog-edit-pab/dialog-edit-pab.component"; @@ -127,6 +128,7 @@ import { SelectSectionDetailsComponent } from "./components/select-section-detai import { ServiceWorkerModule } from '@angular/service-worker'; import { environment } from '../environments/environment'; import { ServiceWorkerUpdateService } from "./services/service-worker-update.service"; +import { UserConfirmationService } from "./services/user-confirmation.service"; const appRoutes: Routes = [ { path: "list/search", component: CalculatorListComponent }, @@ -208,6 +210,7 @@ const appRoutes: Routes = [ CalculatorResultsComponent, DialogConfirmCloseCalcComponent, DialogConfirmEmptySessionComponent, + DialogConfirmComponent, DialogEditPabComponent, DialogEditParamComputedComponent, DialogEditParamValuesComponent, @@ -282,7 +285,8 @@ const appRoutes: Routes = [ provide: ErrorStateMatcher, useClass: ImmediateErrorStateMatcher }, - ServiceWorkerUpdateService + ServiceWorkerUpdateService, + UserConfirmationService ], schemas: [NO_ERRORS_SCHEMA], bootstrap: [AppComponent] diff --git a/src/app/components/dialog-confirm/dialog-confirm.component.html b/src/app/components/dialog-confirm/dialog-confirm.component.html new file mode 100644 index 0000000000000000000000000000000000000000..1de1a1bae3d8e2d2cee34ed52dd36424283dc39f --- /dev/null +++ b/src/app/components/dialog-confirm/dialog-confirm.component.html @@ -0,0 +1,12 @@ +<h1 mat-dialog-title [innerHTML]="uitextTitle"></h1> +<div mat-dialog-content> + <p [innerHTML]="uitextBody"></p> +</div> +<div mat-dialog-actions [attr.align]="'end'"> + <button id="cancel" mat-raised-button color="primary" [mat-dialog-close]="false" cdkFocusInitial> + {{ uitextNo }} + </button> + <button id="confirm" mat-raised-button color="warn" [mat-dialog-close]="true"> + {{ uitextYes }} + </button> +</div> diff --git a/src/app/components/dialog-confirm/dialog-confirm.component.ts b/src/app/components/dialog-confirm/dialog-confirm.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..f8cefaa2526ec20077f992157001690ec99e5c16 --- /dev/null +++ b/src/app/components/dialog-confirm/dialog-confirm.component.ts @@ -0,0 +1,38 @@ +import { MatDialogRef, MAT_DIALOG_DATA } from "@angular/material/dialog"; +import { Inject, Component } from "@angular/core"; +import { I18nService } from "../../services/internationalisation.service"; + +@Component({ + selector: "dialog-confirm", + templateUrl: "dialog-confirm.component.html", +}) +export class DialogConfirmComponent { + + private _title: string; + private _text: string; + + constructor( + public dialogRef: MatDialogRef<DialogConfirmComponent>, + private intlService: I18nService, + @Inject(MAT_DIALOG_DATA) public data: any + ) { + this._title = data.title; + this._text = data.text; + } + + public get uitextYes() { + return this.intlService.localizeText("INFO_OPTION_YES"); + } + + public get uitextNo() { + return this.intlService.localizeText("INFO_OPTION_NO"); + } + + public get uitextTitle() { + return this._title; + } + + public get uitextBody() { + return this._text; + } +} diff --git a/src/app/components/dialog-edit-param-values/dialog-edit-param-values.component.ts b/src/app/components/dialog-edit-param-values/dialog-edit-param-values.component.ts index 16768e9c0715efc77bbf7b50d2c4d6b45d3e64ea..47790963b1c2fd3b83d065621b01b1c2326a026a 100644 --- a/src/app/components/dialog-edit-param-values/dialog-edit-param-values.component.ts +++ b/src/app/components/dialog-edit-param-values/dialog-edit-param-values.component.ts @@ -10,7 +10,7 @@ import { sprintf } from "sprintf-js"; import { ParamValueMode, ExtensionStrategy } from "jalhyd"; -import { fv } from "../../util"; +import { fv } from "../../util/util"; import { ServiceFactory } from "app/services/service-factory"; @Component({ diff --git a/src/app/components/dialog-generate-par-simulation/dialog-generate-par-simulation.component.ts b/src/app/components/dialog-generate-par-simulation/dialog-generate-par-simulation.component.ts index b7aaf61d72def9cd99d15fec5b95d62666de2614..554bc994357a9496566c3ad921f6ec8f4b8176f1 100644 --- a/src/app/components/dialog-generate-par-simulation/dialog-generate-par-simulation.component.ts +++ b/src/app/components/dialog-generate-par-simulation/dialog-generate-par-simulation.component.ts @@ -3,7 +3,7 @@ import { Inject, Component } from "@angular/core"; import { I18nService } from "../../services/internationalisation.service"; import { MultiDimensionResults } from "../../results/multidimension-results"; -import { fv, longestVarParam } from "../../util"; +import { fv, longestVarParam } from "../../util/util"; @Component({ selector: "dialog-generate-par-simulation", diff --git a/src/app/components/field-set/field-set.component.ts b/src/app/components/field-set/field-set.component.ts index 448d40a7dc63bfcd47dbec4e10bdb29ba884e857..62b39960f24fd4f2e18e1d4c95c9e5b8dda162c1 100644 --- a/src/app/components/field-set/field-set.component.ts +++ b/src/app/components/field-set/field-set.component.ts @@ -16,7 +16,7 @@ import { I18nService } from "../../services/internationalisation.service"; import { sprintf } from "sprintf-js"; import { capitalize } from "jalhyd"; -import { DefinedBoolean } from "app/definedvalue/definedboolean"; +import { DefinedBoolean } from "../../util/definedvalue/definedboolean"; @Component({ selector: "field-set", diff --git a/src/app/components/fieldset-container/fieldset-container.component.ts b/src/app/components/fieldset-container/fieldset-container.component.ts index 042df751799ed61418e5b9a869e2277cb627110d..e07c6514d5088587c89b402db5c6cca6088bc7e7 100644 --- a/src/app/components/fieldset-container/fieldset-container.component.ts +++ b/src/app/components/fieldset-container/fieldset-container.component.ts @@ -6,7 +6,7 @@ import { FieldSet } from "../../formulaire/elements/fieldset"; import { FormulaireDefinition } from "../../formulaire/definition/form-definition"; import { I18nService } from "../../services/internationalisation.service"; import { ApplicationSetupService } from "../../services/app-setup.service"; -import { DefinedBoolean } from "app/definedvalue/definedboolean"; +import { DefinedBoolean } from "../../util/definedvalue/definedboolean"; import { ParamValueMode } from "jalhyd"; @Component({ diff --git a/src/app/components/fixedvar-results/results.component.ts b/src/app/components/fixedvar-results/results.component.ts index ae9747dbe0e9f0b4843d4ccb9292105cf295e86f..18b08975073117647fc2b4e3fbfd1e94e5e81a1c 100644 --- a/src/app/components/fixedvar-results/results.component.ts +++ b/src/app/components/fixedvar-results/results.component.ts @@ -2,7 +2,7 @@ import screenfull from "screenfull"; import { NgParameter } from "../../formulaire/elements/ngparam"; import { ServiceFactory } from "../../services/service-factory"; -import { fv } from "../../util"; +import { fv } from "../../util/util"; import { CalculatorResults } from "../../results/calculator-results"; import { Directive, HostListener } from "@angular/core"; diff --git a/src/app/components/fixedvar-results/var-results.component.ts b/src/app/components/fixedvar-results/var-results.component.ts index 7493401d2776c1760fbe9c616232aa62ceefecea..3c9f310ce6c3d740cb8bd9303d6c41f58104848a 100644 --- a/src/app/components/fixedvar-results/var-results.component.ts +++ b/src/app/components/fixedvar-results/var-results.component.ts @@ -8,7 +8,7 @@ import { I18nService } from "../../services/internationalisation.service"; import { ResultsComponentDirective } from "./results.component"; import { DialogLogEntriesDetailsComponent } from "../dialog-log-entries-details/dialog-log-entries-details.component"; import { AppComponent } from "../../app.component"; -import { longestVarParam } from "../../../app/util"; +import { longestVarParam } from "../../../app/util/util"; @Component({ selector: "var-results", diff --git a/src/app/components/generic-calculator/calculator.component.ts b/src/app/components/generic-calculator/calculator.component.ts index 49e81d946b6216bb123c23a0c64dea8c32cbe730..fe805f6581909ca8a8dfdde1779083b2412de6c0 100644 --- a/src/app/components/generic-calculator/calculator.component.ts +++ b/src/app/components/generic-calculator/calculator.component.ts @@ -28,7 +28,7 @@ import { ParallelStructure } from "jalhyd"; -import { generateValuesCombination, getUnformattedIthResult, getUnformattedIthValue } from "../../util"; +import { generateValuesCombination, getUnformattedIthResult, getUnformattedIthValue } from "../../util/util"; import { AppComponent } from "../../app.component"; import { FormulaireService } from "../../services/formulaire.service"; @@ -62,7 +62,7 @@ import { sprintf } from "sprintf-js"; import * as XLSX from "xlsx"; import { ServiceFactory } from "app/services/service-factory"; -import { DefinedBoolean } from "app/definedvalue/definedboolean"; +import { DefinedBoolean } from "../../util/definedvalue/definedboolean"; import { FormulaireCourbeRemous } from "app/formulaire/definition/form-courbe-remous"; import { RemousResults } from "app/results/remous-results"; diff --git a/src/app/components/generic-input/generic-input.component.ts b/src/app/components/generic-input/generic-input.component.ts index d95168143cb67edb5fff3ffe00586e65fe60ad4f..281a11332a7c288641ab2719a9f18a1d11c9ccf2 100644 --- a/src/app/components/generic-input/generic-input.component.ts +++ b/src/app/components/generic-input/generic-input.component.ts @@ -5,7 +5,7 @@ import { FormulaireDefinition } from "../../formulaire/definition/form-definitio import { NgParameter } from "../../formulaire/elements/ngparam"; import { I18nService } from "../../services/internationalisation.service"; import { ApplicationSetupService } from "../../services/app-setup.service"; -import { DefinedBoolean } from "app/definedvalue/definedboolean"; +import { DefinedBoolean } from "../../util/definedvalue/definedboolean"; /** * classe de gestion générique d'un champ de saisie avec titre, validation et message d'erreur diff --git a/src/app/components/jet-trajectory-chart/jet-trajectory-chart.component.ts b/src/app/components/jet-trajectory-chart/jet-trajectory-chart.component.ts index 5bd87e2bb12d5177c109a493bcc92517bef823ce..bf4e084e134588c955fd4da70e1a8de96b82808c 100644 --- a/src/app/components/jet-trajectory-chart/jet-trajectory-chart.component.ts +++ b/src/app/components/jet-trajectory-chart/jet-trajectory-chart.component.ts @@ -5,7 +5,7 @@ import { BaseChartDirective } from "ng2-charts"; import { I18nService } from "../../services/internationalisation.service"; import { ResultsComponentDirective } from "../fixedvar-results/results.component"; import { IYSeries } from "../../results/y-series"; -import { fv } from "../../util"; +import { fv } from "../../util/util"; import { AppComponent } from "../../app.component"; import { Jet, Result } from "jalhyd"; diff --git a/src/app/components/macrorugo-compound-results/macrorugo-compound-results.component.ts b/src/app/components/macrorugo-compound-results/macrorugo-compound-results.component.ts index f388647dafbcf7d653fcffd263b87d3dfff40718..039681e3c0195eb26e976b2d3b6ab31fd3189534 100644 --- a/src/app/components/macrorugo-compound-results/macrorugo-compound-results.component.ts +++ b/src/app/components/macrorugo-compound-results/macrorugo-compound-results.component.ts @@ -2,7 +2,7 @@ import { Component, Input } from "@angular/core"; import { Result, cLog, Message, MessageCode, MessageSeverity, MRCInclination } from "jalhyd"; -import { fv } from "../../../app/util"; +import { fv } from "../../../app/util/util"; import { CalculatorResults } from "../../results/calculator-results"; import { NgParameter } from "../../formulaire/elements/ngparam"; diff --git a/src/app/components/modules-diagram/modules-diagram.component.ts b/src/app/components/modules-diagram/modules-diagram.component.ts index 0b607e51e10e25cb2f5505c3e1bdf0993dfcffe5..0ec435a0739a6691b60d18390aab60a0f4227fdc 100644 --- a/src/app/components/modules-diagram/modules-diagram.component.ts +++ b/src/app/components/modules-diagram/modules-diagram.component.ts @@ -33,7 +33,7 @@ import * as SvgPanZoom from "svg-pan-zoom"; import { MatomoTracker } from "@ngx-matomo/tracker"; -import { fv } from "../../util"; +import { fv } from "../../util/util"; @Component({ selector: "modules-diagram", diff --git a/src/app/components/pab-profile-chart/pab-profile-chart.component.ts b/src/app/components/pab-profile-chart/pab-profile-chart.component.ts index ee664e43ad59b3c77bd6e000921cbb11d8c32ea6..11aca1b4046fb5c4b0119cf4a5c87b4a56f41b20 100644 --- a/src/app/components/pab-profile-chart/pab-profile-chart.component.ts +++ b/src/app/components/pab-profile-chart/pab-profile-chart.component.ts @@ -6,7 +6,7 @@ import { I18nService } from "../../services/internationalisation.service"; import { ResultsComponentDirective } from "../fixedvar-results/results.component"; import { PabResults } from "../../results/pab-results"; import { IYSeries } from "../../results/y-series"; -import { fv, longestVarParam } from "../../util"; +import { fv, longestVarParam } from "../../util/util"; import { AppComponent } from "../../app.component"; import { CloisonAval, Cloisons, LoiDebit } from "jalhyd"; diff --git a/src/app/components/pab-results/pab-results-table.component.ts b/src/app/components/pab-results/pab-results-table.component.ts index b541cdc52e332f6cde19abc74c9c9730db870fd8..b2b36a8368d34de4fa186b699152db63e9602442 100644 --- a/src/app/components/pab-results/pab-results-table.component.ts +++ b/src/app/components/pab-results/pab-results-table.component.ts @@ -6,7 +6,7 @@ import { PabResults } from "../../results/pab-results"; import { I18nService } from "../../services/internationalisation.service"; import { ResultsComponentDirective } from "../fixedvar-results/results.component"; import { AppComponent } from "../../app.component"; -import { fv } from "../../util"; +import { fv } from "../../util/util"; @Component({ selector: "pab-results-table", diff --git a/src/app/components/pab-table/pab-table.component.ts b/src/app/components/pab-table/pab-table.component.ts index ead6072f13b0af083cdf29b590ab1172de57f28a..dc2470aef9334fdf718ddc095c103a460279dece 100644 --- a/src/app/components/pab-table/pab-table.component.ts +++ b/src/app/components/pab-table/pab-table.component.ts @@ -28,7 +28,7 @@ import { PabTable } from "../../formulaire/elements/pab-table"; import { DialogEditPabComponent } from "../dialog-edit-pab/dialog-edit-pab.component"; import { AppComponent } from "../../app.component"; import { NgParameter, ParamRadioConfig } from "../../formulaire/elements/ngparam"; -import { DefinedBoolean } from "app/definedvalue/definedboolean"; +import { DefinedBoolean } from "../../util/definedvalue/definedboolean"; /** * The big editable data grid for calculator type "Pab" (component) diff --git a/src/app/components/pb-results/pb-cloison-results.component.ts b/src/app/components/pb-results/pb-cloison-results.component.ts index 0a59b6a8b1f0581e7ff07e706f24459e6e7df5ce..da3aae4ba32983e163a9096fdf1db300ea78aab3 100644 --- a/src/app/components/pb-results/pb-cloison-results.component.ts +++ b/src/app/components/pb-results/pb-cloison-results.component.ts @@ -2,7 +2,7 @@ import { Component, Input } from "@angular/core"; import { FixedResultsComponent } from "../fixedvar-results/fixed-results.component"; import { NgParameter } from "../../formulaire/elements/ngparam"; -import { getIthValue } from "../../util"; +import { getIthValue } from "../../util/util"; import { PbCloisonResults } from "../../results/pb-cloison-results"; import { Result, ResultElement } from "jalhyd"; diff --git a/src/app/components/pb-results/pb-results-table.component.ts b/src/app/components/pb-results/pb-results-table.component.ts index 79084192dd3b7b5e0477157ea06f3e8a09b1e5c9..9e1315d86f6d1f8608f728389431494d1065d239 100644 --- a/src/app/components/pb-results/pb-results-table.component.ts +++ b/src/app/components/pb-results/pb-results-table.component.ts @@ -5,7 +5,7 @@ import { PreBarrage, PbBassin } from "jalhyd"; import { I18nService } from "../../services/internationalisation.service"; import { ResultsComponentDirective } from "../fixedvar-results/results.component"; import { AppComponent } from "../../app.component"; -import { fv, getIthValue } from "../../util"; +import { fv, getIthValue } from "../../util/util"; import { PrebarrageResults } from "../../results/prebarrage-results"; @Component({ diff --git a/src/app/components/pb-schema/pb-schema.component.ts b/src/app/components/pb-schema/pb-schema.component.ts index 6de70eecd8ddf7c49d2c45a200a9ae6a6cf0a009..ef424b22fd24c3c88efd3680c3d0250f38ba1512 100644 --- a/src/app/components/pb-schema/pb-schema.component.ts +++ b/src/app/components/pb-schema/pb-schema.component.ts @@ -18,9 +18,9 @@ import { GenericCalculatorComponent } from "../generic-calculator/calculator.com import { FormulairePrebarrage } from "../../formulaire/definition/form-prebarrage"; import { AppComponent } from "../../app.component"; -import { fv } from "app/util"; +import { fv } from "app/util/util"; import { ServiceFactory } from "app/services/service-factory"; -import { DefinedBoolean } from "app/definedvalue/definedboolean"; +import { DefinedBoolean } from "../../util/definedvalue/definedboolean"; import { PrebarrageService, PrebarrageServiceEvents } from "app/services/prebarrage.service"; /** diff --git a/src/app/components/remous-results/remous-results.component.ts b/src/app/components/remous-results/remous-results.component.ts index 1f5ea4b720bfcb4bbf0ccbd85f42bde4dc582540..2fd73ae1e13f3ecb6b1b9444b07e0800ca6435f8 100644 --- a/src/app/components/remous-results/remous-results.component.ts +++ b/src/app/components/remous-results/remous-results.component.ts @@ -9,7 +9,7 @@ import { FormulaireService } from "../../services/formulaire.service"; import { ResultsComponentDirective } from "../fixedvar-results/results.component"; import { AppComponent } from "../../app.component"; import { LineData, ChartData } from "./line-and-chart-data"; -import { fv } from "../../util"; +import { fv } from "../../util/util"; import { VarResults } from "../../results/var-results"; import { BaseChartDirective } from "ng2-charts"; diff --git a/src/app/components/results-chart/chart-type.component.ts b/src/app/components/results-chart/chart-type.component.ts index 39839c3a96cb2feea1c220ebbf415e2855263880..123657c5173e1f09900f327f140c4cdd6eb3dc65 100644 --- a/src/app/components/results-chart/chart-type.component.ts +++ b/src/app/components/results-chart/chart-type.component.ts @@ -4,7 +4,7 @@ import { I18nService } from "../../services/internationalisation.service"; import { ChartType } from "../../results/chart-type"; import { SelectFieldChartType } from "app/formulaire/elements/select/select-field-charttype"; import { SelectEntry } from "app/formulaire/elements/select/select-entry"; -import { decodeHtml } from "../../util"; +import { decodeHtml } from "../../util/util"; @Component({ selector: "chart-type", diff --git a/src/app/components/results-chart/results-chart.component.ts b/src/app/components/results-chart/results-chart.component.ts index 0b33967f3c45ae7766943a382b9db32fc3593ed7..be7eb667828b18318c0c5b92b5759e88663d7ddc 100644 --- a/src/app/components/results-chart/results-chart.component.ts +++ b/src/app/components/results-chart/results-chart.component.ts @@ -11,7 +11,7 @@ import { ChartType } from "../../results/chart-type"; import { ResultsComponentDirective } from "../fixedvar-results/results.component"; import { IYSeries } from "../../results/y-series"; import { VarResults } from "../../results/var-results"; -import { fv } from "../../util"; +import { fv } from "../../util/util"; import { AppComponent } from "../../app.component"; import zoomPlugin from 'chartjs-plugin-zoom'; diff --git a/src/app/components/select-field-line/select-field-line.component.ts b/src/app/components/select-field-line/select-field-line.component.ts index c6b2474a0f08e795ac58514bb58486d127e4ecdb..7c4b067efcece001ba65882f713c0e286d660f48 100644 --- a/src/app/components/select-field-line/select-field-line.component.ts +++ b/src/app/components/select-field-line/select-field-line.component.ts @@ -4,7 +4,7 @@ import { SelectField } from "../../formulaire/elements/select/select-field"; import { SelectEntry } from "../../formulaire/elements/select/select-entry"; import { I18nService } from "../../services/internationalisation.service"; import { ApplicationSetupService } from "../../services/app-setup.service"; -import { decodeHtml } from "../../util"; +import { decodeHtml } from "../../util/util"; @Component({ selector: "select-field-line", diff --git a/src/app/components/select-section-details/select-section-details.component.ts b/src/app/components/select-section-details/select-section-details.component.ts index 55c21b523cead45096bfc15f39d2d733043aad92..278ce4bdca3d2ead89a5354807f4266b1f4171a0 100644 --- a/src/app/components/select-section-details/select-section-details.component.ts +++ b/src/app/components/select-section-details/select-section-details.component.ts @@ -3,7 +3,7 @@ import { Router } from '@angular/router'; import { FormulaireDefinition } from 'app/formulaire/definition/form-definition'; import { FormulaireService } from 'app/services/formulaire.service'; import { I18nService } from 'app/services/internationalisation.service'; -import { fv } from 'app/util'; +import { fv } from 'app/util/util'; import { formattedValue } from 'jalhyd'; /** diff --git a/src/app/components/variable-results-selector/variable-results-selector.component.ts b/src/app/components/variable-results-selector/variable-results-selector.component.ts index 0d68b44573e4177fb943019a8516944fdbc94162..50c4a0d968df136c66c0f25f9391c0c2a5688820 100644 --- a/src/app/components/variable-results-selector/variable-results-selector.component.ts +++ b/src/app/components/variable-results-selector/variable-results-selector.component.ts @@ -1,7 +1,7 @@ import { Component, Input, OnChanges } from "@angular/core"; import { I18nService } from "../../services/internationalisation.service"; -import { fv, longestVarParam } from "../../util"; +import { fv, longestVarParam } from "../../util/util"; import { MultiDimensionResults } from "../../results/multidimension-results"; import { VariatedDetails } from "jalhyd"; import { CalculatorResults } from "../../results/calculator-results"; diff --git a/src/app/formulaire/definition/form-pab.ts b/src/app/formulaire/definition/form-pab.ts index 85f423501196014b5198c27d9e5a6a243524e796..d27a620927df68c68c2789250112d8889d7dbd93 100644 --- a/src/app/formulaire/definition/form-pab.ts +++ b/src/app/formulaire/definition/form-pab.ts @@ -3,7 +3,7 @@ import { Pab, Result, VariatedDetails } from "jalhyd"; import { FormulaireDefinition } from "./form-definition"; import { PabResults } from "../../results/pab-results"; import { NgParameter } from "../elements/ngparam"; -import { longestVarParam } from "../../util"; +import { longestVarParam } from "../../util/util"; import { PabTable } from "../elements/pab-table"; /** diff --git a/src/app/formulaire/definition/form-prebarrage.ts b/src/app/formulaire/definition/form-prebarrage.ts index c9a0d9e0db35efb7ff665b637520798032944bf6..7aa507656ca0d4925ea3b0d9abf1f79194bf9b4a 100644 --- a/src/app/formulaire/definition/form-prebarrage.ts +++ b/src/app/formulaire/definition/form-prebarrage.ts @@ -9,7 +9,7 @@ import { FieldsetContainer } from "../elements/fieldset-container"; import { CalculatorResults } from "../../results/calculator-results"; import { PrebarrageResults } from "../../results/prebarrage-results"; import { NgParameter } from "../elements/ngparam"; -import { longestVarParam } from "../../util"; +import { longestVarParam } from "../../util/util"; import { FormulaireNode } from "../elements/formulaire-node"; /** diff --git a/src/app/formulaire/elements/ngparam.ts b/src/app/formulaire/elements/ngparam.ts index e4dc902cd4034702c761634d145c30781db189b2..a579f014146807fe4e091f7a7dd6b1d0050faa18 100644 --- a/src/app/formulaire/elements/ngparam.ts +++ b/src/app/formulaire/elements/ngparam.ts @@ -8,7 +8,7 @@ import { sprintf } from "sprintf-js"; import { InputField } from "./input-field"; import { ServiceFactory } from "../../services/service-factory"; import { FormulaireNode } from "./formulaire-node"; -import { fv } from "../../util"; +import { fv } from "../../util/util"; export enum ParamRadioConfig { /** pas de radio, paramètre modifiable à la main uniquement */ diff --git a/src/app/formulaire/elements/select/select-field-searched-param.ts b/src/app/formulaire/elements/select/select-field-searched-param.ts index 339acbfe5702e623c3cc8041b2860ad7ef0fe3f6..8811ef81f441628e39164f0f46404fa8cf1a1d4c 100644 --- a/src/app/formulaire/elements/select/select-field-searched-param.ts +++ b/src/app/formulaire/elements/select/select-field-searched-param.ts @@ -1,5 +1,5 @@ import { ServiceFactory } from "app/services/service-factory"; -import { decodeHtml } from "app/util"; +import { decodeHtml } from "app/util/util"; import { acSection, Nub, Solveur } from "jalhyd"; import { SelectEntry } from "./select-entry"; import { SelectField } from "./select-field"; diff --git a/src/app/formulaire/elements/select/select-field-solveur-target.ts b/src/app/formulaire/elements/select/select-field-solveur-target.ts index b98546e221fc7bf13c782ca43440e24df9f34320..3d752aa4368a5c45c5b0c4a4d36375ff8cb0d8b2 100644 --- a/src/app/formulaire/elements/select/select-field-solveur-target.ts +++ b/src/app/formulaire/elements/select/select-field-solveur-target.ts @@ -5,7 +5,7 @@ */ import { ServiceFactory } from "app/services/service-factory"; -import { decodeHtml } from "../../../util"; +import { decodeHtml } from "../../../util/util"; import { Session, Solveur } from "jalhyd"; import { SelectEntry } from "./select-entry"; import { SelectField } from "./select-field"; diff --git a/src/app/formulaire/elements/select/select-field-target-pass.ts b/src/app/formulaire/elements/select/select-field-target-pass.ts index 6383c02ffc159a14afa7f6a11dce014769c48af9..8438e0a0fe2c50a5b5b1c8b937543249def5a423 100644 --- a/src/app/formulaire/elements/select/select-field-target-pass.ts +++ b/src/app/formulaire/elements/select/select-field-target-pass.ts @@ -1,5 +1,5 @@ import { ServiceFactory } from "app/services/service-factory"; -import { decodeHtml } from "app/util"; +import { decodeHtml } from "app/util/util"; import { CalculatorType, FishPass, Session, Verificateur } from "jalhyd"; import { FormulaireElement } from "../formulaire-element"; import { FormulaireNode } from "../formulaire-node"; diff --git a/src/app/formulaire/elements/select/select-field.ts b/src/app/formulaire/elements/select/select-field.ts index 2b9b65aa97fc770e1548718791597698decb7a09..b45f59c42d2581d536dc40659295cb86ed57cc62 100644 --- a/src/app/formulaire/elements/select/select-field.ts +++ b/src/app/formulaire/elements/select/select-field.ts @@ -1,6 +1,6 @@ import { Field } from "../field"; import { SelectEntry } from "./select-entry"; -import { arraysAreEqual } from "../../../util"; +import { arraysAreEqual } from "../../../util/util"; import { FormulaireNode } from "../formulaire-node"; import { ServiceFactory } from "app/services/service-factory"; import { FormulaireDefinition } from "../../definition/form-definition"; diff --git a/src/app/results/var-results.ts b/src/app/results/var-results.ts index 37ff757b971936d696f0b040060cc6b54f2688f7..c8c814d89776af9760d03c5e524297446330f92a 100644 --- a/src/app/results/var-results.ts +++ b/src/app/results/var-results.ts @@ -2,7 +2,7 @@ import { CalculatedParamResults } from "./param-calc-results"; import { ServiceFactory } from "../services/service-factory"; import { PlottableData } from "./plottable-data"; import { ChartType } from "./chart-type"; -import { longestVarParam } from "../util"; +import { longestVarParam } from "../util/util"; import { FormulaireDefinition } from "../formulaire/definition/form-definition"; import { sprintf } from "sprintf-js"; diff --git a/src/app/services/internationalisation.service.ts b/src/app/services/internationalisation.service.ts index ea1bec8c3d563f2e11b1a8cfdbf45ae95c67100f..deee494fecb63836be93eeb253b8253ed708742f 100644 --- a/src/app/services/internationalisation.service.ts +++ b/src/app/services/internationalisation.service.ts @@ -5,7 +5,7 @@ import { Message, MessageCode, Observable, Observer, Nub, CalculatorType, PreBar import { StringMap } from "../stringmap"; import { ApplicationSetupService } from "./app-setup.service"; import { HttpService } from "./http.service"; -import { fv, decodeHtml } from "../util"; +import { fv, decodeHtml } from "../util/util"; import { ServiceFactory } from "./service-factory"; import { FormulaireService } from "./formulaire.service"; diff --git a/src/app/services/service-factory.ts b/src/app/services/service-factory.ts index e3ea55cae3c7ce504924688dac8ae1517f3268ba..668d5aea18b12ed1295f44b2885fdec97a1eeb56 100644 --- a/src/app/services/service-factory.ts +++ b/src/app/services/service-factory.ts @@ -4,6 +4,7 @@ import { I18nService } from "./internationalisation.service"; import { HttpService } from "./http.service"; import { NotificationsService } from "./notifications.service"; import { PrebarrageService } from "./prebarrage.service"; +import { ServiceWorkerUpdateService } from "./service-worker-update.service"; /** * A "Singleton" the TS way, that holds pointers to all services, to be accessed @@ -17,11 +18,13 @@ export const ServiceFactory: { httpService: HttpService; notificationsService: NotificationsService; prebarrageService: PrebarrageService; + serviceWorkerUpdateService: ServiceWorkerUpdateService; } = { applicationSetupService: undefined, formulaireService: undefined, i18nService: undefined, httpService: undefined, notificationsService: undefined, - prebarrageService: undefined + prebarrageService: undefined, + serviceWorkerUpdateService: undefined }; diff --git a/src/app/services/service-worker-update.service.ts b/src/app/services/service-worker-update.service.ts index 96b84426ac85dc1c4666d7d6b55ecc7e4aa46f29..163fd4a12e45f941abec56a389479a73c68b291c 100644 --- a/src/app/services/service-worker-update.service.ts +++ b/src/app/services/service-worker-update.service.ts @@ -1,36 +1,74 @@ -import { Injectable } from "@angular/core"; +import { Injectable, NgZone } from "@angular/core"; import { SwUpdate } from '@angular/service-worker'; import { I18nService } from "./internationalisation.service"; import { NotificationsService } from "./notifications.service"; +import { UserConfirmationService } from "./user-confirmation.service"; +import { interval } from "rxjs"; @Injectable() export class ServiceWorkerUpdateService { constructor( private swUpdate: SwUpdate, private notificationService: NotificationsService, - private i18nService: I18nService + private i18nService: I18nService, + private userConfirmationService: UserConfirmationService, + private ngZone: NgZone ) { - swUpdate.versionUpdates.subscribe(evt => { + if (this.swUpdate.isEnabled) { + this.ngZone.runOutsideAngular(() => + interval(1000 * 60 * 60).subscribe(val => { + console.log('ServiceWorkerUpdateService: checking for updates...') + swUpdate.checkForUpdate().then(b => { + console.log("ServiceWorkerUpdateService: " + (b ? "new version found" : "no new version")); + }); + }) + ); + } else { + console.log("ServiceWorkerUpdateService: SwUpdate is disabled"); + } + + this.swUpdate.versionUpdates.subscribe(evt => { switch (evt.type) { case 'VERSION_DETECTED': - let ver = evt.version.appData["version"]; - let msg = i18nService.localizeText("INFO_SERVICE_WORKER_VERSION_DETECTED", { "ver": ver }); - notificationService.notify(msg, 10000); + let ver = (evt as any).version?.appData?.version ?? "<NA>"; + console.log("ServiceWorkerUpdateService: VERSION_DETECTED", ver); + notificationService.notify(i18nService.localizeText("INFO_SERVICE_WORKER_VERSION_DETECTED", { "ver": ver }), 10000); break; case 'VERSION_READY': - const newVer = evt.latestVersion.appData["version"]; - // const currVer = evt.currentVersion.appData["version"]; - msg = i18nService.localizeText("INFO_SERVICE_WORKER_VERSION_READY", { "ver": newVer }); - notificationService.notify(msg, 10000); + const currVer = (evt as any).currentVersion?.appData?.version ?? "<NA>"; + const newVer = (evt as any).latestVersion?.appData?.version ?? "<NA>"; + console.log("ServiceWorkerUpdateService: VERSION_READY", currVer, "->", newVer); + + notificationService.notify(i18nService.localizeText("INFO_SERVICE_WORKER_VERSION_READY", { "ver": newVer }), 10000); + // PLANTE si on stocke le message dans une variable !!!! + // const msg = i18nService.localizeText("INFO_SERVICE_WORKER_VERSION_READY", { "ver": newVer }); + // notificationService.notify(msg, 10000); + // -> ReferenceError: can't access lexical declaration 'xxx' before initialization + // avec xxx qui varie d'une fois à l'autre !!! + + userConfirmationService.askUserConfirmation("Confirmation", + i18nService.localizeText("INFO_SERVICE_WORKER_VERSION_READY", { "ver": newVer })).then(data => { + if (data["confirm"]) { + console.log("ServiceWorkerUpdateService: application update confirmed"); + window.location.reload(); + } + else { + console.log("ServiceWorkerUpdateService: application update canceled"); + } + }); break; case 'VERSION_INSTALLATION_FAILED': - ver = evt.version.appData["version"]; - msg = i18nService.localizeText("ERROR_SERVICE_WORKER_INSTALL_FAILED", { "ver": ver }); - notificationService.notify(msg, 10000); + ver = (evt as any).version?.appData?.version ?? "NA"; + console.log("ServiceWorkerUpdateService: VERSION_INSTALLATION_FAILED", ver); + notificationService.notify(i18nService.localizeText("ERROR_SERVICE_WORKER_INSTALL_FAILED", { "ver": ver }), 10000); break; } }); + swUpdate.unrecoverable.subscribe(event => { + console.log("SwUpdate.unrecoverable reason", event.reason, "type", event.type); + notificationService.notify("SwUpdate: unrecoverable state. Reason=" + event.reason + ", type=" + event.type, 10000); + }); } } diff --git a/src/app/services/user-confirmation.service.ts b/src/app/services/user-confirmation.service.ts new file mode 100644 index 0000000000000000000000000000000000000000..d33beecbbcb3516620ad36170922baeeca54a8b3 --- /dev/null +++ b/src/app/services/user-confirmation.service.ts @@ -0,0 +1,69 @@ +import { Injectable } from "@angular/core"; + +import { BidirectionalSubject } from "app/util/bidir_subject"; +import { Observer } from "rxjs"; + +/** + * This service enables any class (even another service) to display a confirmation dialog on GUI ans get the user answer + */ +@Injectable() +export class UserConfirmationService { + + // used to communicate with UI component in charge of displaying confirmation dialog + // direction 0 : this + // direction 1 : UI + private _userConfirm: BidirectionalSubject<{}>; + + public constructor() { + this._userConfirm = new BidirectionalSubject<{}>(); + + // we choose communication canal 0, UI will use 1 + this._userConfirm.selectPostingChannel(this, 0); + } + + /** + * add subscription from UI + * @param source + */ + public subscribe(source: any) { + this._userConfirm.selectPostingChannel(source, 1); + } + + /** + * remove UI subscription + * @param source + */ + public unsubscribe(source: any) { + this._userConfirm.unselectPostingChannel(source); + } + + /** + * add a handler (provided bu UI) to confirmation request + * @param source normally, UI component + * @param obs processing function + */ + public addHandler(source: any, obs: Observer<boolean>) { + this._userConfirm.addHandler(source, obs) + } + + /** + * forward user confirmation from UI to requesting object + * @param confirm user confirmation status + */ + public postConfirmation(source: any, confirm: {}) { + this._userConfirm.post(source, confirm); + } + + /** + * forward to UI a request from source to ask a user confirmation with a dialog + * @param source object requesting confirmation + * @param title confirmation dialog title + * @param text confirmation dialog body text + * @returns a Promise resolving to a boolean holding user confirmation status + */ + public askUserConfirmation(title: string, text: string): Promise<{}> { + const ret = this._userConfirm.getReceivePromise(this); + this._userConfirm.post(this, { title: title, body: text }); // false or true, we don't care + return ret; + } +} diff --git a/src/app/util/bidir_subject.ts b/src/app/util/bidir_subject.ts new file mode 100644 index 0000000000000000000000000000000000000000..069e004e83e7040dff2b273fad42ba9aef3ddd0c --- /dev/null +++ b/src/app/util/bidir_subject.ts @@ -0,0 +1,131 @@ +import { Observer, Subject, firstValueFrom, lastValueFrom } from "rxjs"; + +/** + * bi-directional subject (see RxJS Subject) + * Allows two objects to exchange messages in both directions. Each object has to choose a posting channel + * (messages will receive from the other one). + * + * source1 ----post-----> | channel 0 | --subscribe--> source2 + * <--subscribe-- | channel 1 | <-----post---- + * + * EventEmitter is not used since it is reserved to properties in Angular component with @Output annotation + */ +export class BidirectionalSubject<T> { + + // communication channels + private _channel0: Subject<T>; + private _channel1: Subject<T>; + + // array of "who chose which posting channel" + private _channel0Posters: any[] = []; + private _channel1Posters: any[] = []; + + constructor() { + this._channel0 = new Subject(); + this._channel1 = new Subject(); + } + + /** + * get posting channel index + * @param source object that chose one of the channels + */ + private getPostingChannelIndex(source: any) { + if (this._channel0Posters.indexOf(source) !== -1) { + return 0; + } + if (this._channel1Posters.indexOf(source) !== -1) { + return 1; + } + return -1; + } + + /** + * choose a posting channel + * @param source object that chooses the channel + * @param chan channel number + */ + public selectPostingChannel(source: any, chan: number) { + switch (chan) { + case 0: + if (this.getPostingChannelIndex(source) !== -1) { + throw new Error("object already has a selected channel"); + } + this._channel0Posters.push(source); + break; + + case 1: + if (this.getPostingChannelIndex(source) !== -1) { + throw new Error("object already has a selected channel"); + } + this._channel1Posters.push(source); + break; + + default: + throw new Error(`invalid channel number ${chan}`); + } + } + + /** + * remove a source from its channel + */ + public unselectPostingChannel(source: any) { + this._channel0Posters = this._channel0Posters.filter(o => o != source); + this._channel1Posters = this._channel1Posters.filter(o => o != source); + } + + /** + * used by a source to post a message to communication channel + */ + public post(source: any, msg: T) { + switch (this.getPostingChannelIndex(source)) { + case 0: + this._channel0.next(msg); + break; + + case 1: + this._channel1.next(msg); + break; + + case -1: + throw new Error("must select a channel first"); + } + } + + /** + * create a Promise representing a received message (when posted by another source) + * @param source object that will use the Promise + */ + public getReceivePromise(source: any): Promise<T> { + switch (this.getPostingChannelIndex(source)) { + case 0: + return firstValueFrom(this._channel1); + + case 1: + return firstValueFrom(this._channel0); + + case -1: + throw new Error("must select a channel first"); + } + } + + /** + * Add a message handler (provided by source) to process received messages + * (alternative to getReceivePromise()) + * @param source object providing handler + * @param handler message processing function + */ + public addHandler(source: any, handler: Observer<T>) { + switch (this.getPostingChannelIndex(source)) { + case 0: + this._channel1.subscribe(handler); + break; + + case 1: + this._channel0.subscribe(handler); + break; + + case -1: + throw new Error("must select a channel first"); + } + } +} diff --git a/src/app/definedvalue/definedboolean.ts b/src/app/util/definedvalue/definedboolean.ts similarity index 100% rename from src/app/definedvalue/definedboolean.ts rename to src/app/util/definedvalue/definedboolean.ts diff --git a/src/app/definedvalue/definedvalue.ts b/src/app/util/definedvalue/definedvalue.ts similarity index 100% rename from src/app/definedvalue/definedvalue.ts rename to src/app/util/definedvalue/definedvalue.ts diff --git a/src/app/util.ts b/src/app/util/util.ts similarity index 97% rename from src/app/util.ts rename to src/app/util/util.ts index 763a425cbebe0a5b95596c0ae707666bfea8df12..06db46788e2d96870913a5efd1faf8c49db487dd 100644 --- a/src/app/util.ts +++ b/src/app/util/util.ts @@ -1,5 +1,5 @@ -import { NgParameter } from "./formulaire/elements/ngparam"; -import { ServiceFactory } from "./services/service-factory"; +import { NgParameter } from "../formulaire/elements/ngparam"; +import { ServiceFactory } from "../services/service-factory"; import { formattedValue, Nub, VariatedDetails, ParamDefinition, ParamValueMode, Result } from "jalhyd"; @@ -13,7 +13,7 @@ export function logObject(obj: {}, m?: string) { } export function isNumber(s: string): boolean { - return Number(s) !== NaN; + return !Number.isNaN(Number(s)); } /** diff --git a/src/main.ts b/src/main.ts index 066830176864449a04f0ba173614afdd06bde81e..28da08b5bff46969e78ef8737919a9ee7b67fc3e 100644 --- a/src/main.ts +++ b/src/main.ts @@ -11,5 +11,9 @@ if (environment.production) { enableProdMode(); } -platformBrowserDynamic().bootstrapModule(AppModule) - .catch(err => console.log(err)); +platformBrowserDynamic().bootstrapModule(AppModule).then(() => { + if ('serviceWorker' in navigator && environment.production) { + console.log("Registering ngsw-worker.js..."); + navigator.serviceWorker.register('ngsw-worker.js'); + } +}).catch(err => console.log(err));