@sumaris-net/ngx-components 1.5.0 → 1.5.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1146,6 +1146,32 @@
1146
1146
  });
1147
1147
  });
1148
1148
  }
1149
+ function waitForTrue(predicate, opts) {
1150
+ return __awaiter(this, void 0, void 0, function () {
1151
+ var firstTrueObservable, $timeout;
1152
+ return __generator(this, function (_a) {
1153
+ firstTrueObservable = predicate.pipe(operators.first(function (v) { return v === true; }), operators.map(function () { }) // convert to void
1154
+ );
1155
+ // Timeout (+ dueTime)
1156
+ if (opts && opts.timeout > 0) {
1157
+ $timeout = rxjs.timer(opts.timeout);
1158
+ return [2 /*return*/, rxjs.merge(rxjs.timer(opts.dueTime || 0)
1159
+ .pipe(operators.switchMap(function () { return firstTrueObservable; }), operators.takeUntil($timeout)), $timeout
1160
+ .pipe(operators.map(function () {
1161
+ throw new Error("Timeout ready() - after " + opts.timeout + "ms");
1162
+ })))
1163
+ .toPromise()];
1164
+ }
1165
+ // dueTime (without timeout)
1166
+ if (opts && opts.dueTime > 0) {
1167
+ return [2 /*return*/, rxjs.timer(opts.dueTime)
1168
+ .pipe(operators.switchMap(function () { return firstTrueObservable; }))
1169
+ .toPromise()];
1170
+ }
1171
+ return [2 /*return*/, firstTrueObservable.toPromise()];
1172
+ });
1173
+ });
1174
+ }
1149
1175
 
1150
1176
  function createPromiseEventEmitter() {
1151
1177
  return new i0.EventEmitter(true);
@@ -1266,7 +1292,6 @@
1266
1292
  this.debounceTime = null;
1267
1293
  this.i18nPrefix = 'REFERENTIAL.';
1268
1294
  this.noResultMessage = 'COMMON.NO_RESULT';
1269
- this.matAutocompletePosition = 'auto';
1270
1295
  this.multiple = false;
1271
1296
  this.itemSize = '48px';
1272
1297
  this.fetchMoreThreshold = '15%';
@@ -1397,7 +1422,7 @@
1397
1422
  this.displayWith = this.displayWith || this.config.displayWith;
1398
1423
  this.mobile = toBoolean(this.mobile, this.config.mobile);
1399
1424
  this.suggestLengthThreshold = toNumber(this.suggestLengthThreshold, this.config.suggestLengthThreshold || 0);
1400
- this.showAllOnFocus = toBoolean(this.showAllOnFocus, toBoolean(this.config.showAllOnFocus, this.suggestLengthThreshold === 0));
1425
+ this.showAllOnFocus = toBoolean(this.showAllOnFocus, toBoolean(this.config.showAllOnFocus, this.suggestLengthThreshold <= 0));
1401
1426
  this.showPanelOnFocus = toBoolean(this.showPanelOnFocus, toBoolean(this.config.showPanelOnFocus));
1402
1427
  this.classList = this.classList || this.config.class;
1403
1428
  this.debounceTime = toNumber(this.debounceTime, this.config.debounceTime);
@@ -1433,6 +1458,7 @@
1433
1458
  this.debounceTime = toNumber(this.debounceTime, 250);
1434
1459
  this.highlightAccent = toBoolean(this.highlightAccent, false);
1435
1460
  this.suggestLengthThreshold = this.suggestLengthThreshold || 0;
1461
+ this.matAutocompletePosition = this.matAutocompletePosition || (this.mobile && this.searchable ? 'above' : 'auto'); // On mobile, set position to above (because of bottom keyboard)
1436
1462
  // No suggestFn: filter on the given items
1437
1463
  if (!this.suggestFn) {
1438
1464
  var suggestFromArrayFn_1 = function (value, filter) { return __awaiter(_this, void 0, void 0, function () {
@@ -1516,8 +1542,16 @@
1516
1542
  .pipe(operators.filter(function (event) { return _this.enabled && _this.searchable && (!event || !event.defaultPrevented); }), operators.map(function (_) { return _this.showAllOnFocus ? '*' : _this.formControl.value; })), rxjs.merge(this.$inputItems, this._filter$)
1517
1543
  .pipe(operators.filter(function (event) { return _this.enabled && _this.searchable; }), operators.map(function (_) { return _this.formControl.value; })))
1518
1544
  .pipe(
1519
- // If value is a string (and is not = '*'), filter if not enough character
1520
- operators.filter(function (value) { return value === '*' || !(typeof value === 'string') || value.length >= _this.suggestLengthThreshold; }),
1545
+ // Start to suggest when :
1546
+ // - '*'
1547
+ // - is nil, and there is no min length threshold
1548
+ // - is not nil and not a string type (e.g. object)
1549
+ // - is not nil and is a string, and has enough character
1550
+ operators.filter(function (value) { return value === '*'
1551
+ || (isNil(value)
1552
+ ? (_this.suggestLengthThreshold <= 0)
1553
+ : (!(typeof value === 'string')
1554
+ || value.length >= _this.suggestLengthThreshold)); }),
1521
1555
  // Distinguish changes
1522
1556
  operators.distinctUntilChanged()))
1523
1557
  .pipe(
@@ -10830,9 +10864,9 @@
10830
10864
  LocalSettingsService.prototype.ngOnStart = function () {
10831
10865
  console.info('[settings] Starting service...');
10832
10866
  // Restoring local settings
10833
- this._data.mobile = isNotNil(this._data.mobile) ? this._data.mobile : this.platform.is('mobile');
10867
+ this._data.mobile = toBoolean(this._data.mobile, this.platform.is('mobile'));
10834
10868
  this._data.touchUi = this._data.mobile || this.platform.is('phablet') || this.platform.is('tablet');
10835
- this._data.usageMode = this.platform.is('android') ? 'FIELD' : 'DESK'; // FIELD by default if Android
10869
+ this._data.usageMode = this.platform.is('android') || this.platform.is('ios') ? 'FIELD' : 'DESK'; // FIELD by default if Android or iOs
10836
10870
  // Restoring local settings
10837
10871
  return this.restoreLocally();
10838
10872
  };
@@ -19639,6 +19673,9 @@
19639
19673
  AppForm.prototype.registerSubscription = function (sub) {
19640
19674
  return this._subscription.add(sub);
19641
19675
  };
19676
+ AppForm.prototype.unregisterSubscription = function (sub) {
19677
+ this._subscription.remove(sub);
19678
+ };
19642
19679
  AppForm.prototype.registerAutocompleteField = function (fieldName, opts) {
19643
19680
  return this.autocompleteHelper.add(fieldName, opts);
19644
19681
  };
@@ -20476,13 +20513,14 @@
20476
20513
  { type: AccountService }
20477
20514
  ]; };
20478
20515
 
20479
- var AccountPage = /** @class */ (function (_super) {
20480
- __extends(AccountPage, _super);
20481
- function AccountPage(injector, router, formBuilder, accountService, validatorService, settingsValidatorService, translate, settings, cd, locales) {
20482
- var _this = _super.call(this, injector, validatorService.getFormGroup(accountService.account)) || this;
20483
- _this.router = router;
20516
+ var AccountPage = /** @class */ (function (_super_1) {
20517
+ __extends(AccountPage, _super_1);
20518
+ function AccountPage(injector, formBuilder, accountService, network, router, validatorService, settingsValidatorService, translate, settings, cd, locales) {
20519
+ var _this = _super_1.call(this, injector, validatorService.getFormGroup(accountService.account)) || this;
20484
20520
  _this.formBuilder = formBuilder;
20485
20521
  _this.accountService = accountService;
20522
+ _this.network = network;
20523
+ _this.router = router;
20486
20524
  _this.validatorService = validatorService;
20487
20525
  _this.settingsValidatorService = settingsValidatorService;
20488
20526
  _this.translate = translate;
@@ -20523,21 +20561,38 @@
20523
20561
  return _this;
20524
20562
  }
20525
20563
  AccountPage.prototype.ngOnDestroy = function () {
20526
- _super.prototype.ngOnDestroy.call(this);
20564
+ var _a;
20565
+ _super_1.prototype.ngOnDestroy.call(this);
20527
20566
  this.stopListenChanges();
20567
+ (_a = this.networkSubscription) === null || _a === void 0 ? void 0 : _a.unsubscribe();
20528
20568
  };
20529
20569
  AccountPage.prototype.onLogin = function (account) {
20570
+ var _this = this;
20571
+ var _a;
20530
20572
  console.debug('[account] Logged account: ', account);
20531
20573
  this.isLogin = true;
20532
20574
  this.setValue(account);
20533
20575
  this.email.confirmed = account && account.email && (account.statusId !== StatusIds.TEMPORARY);
20534
20576
  this.email.notConfirmed = account && account.email && (!account.statusId || account.statusId === StatusIds.TEMPORARY);
20535
- this.enable();
20536
- this.markAsPristine();
20537
- this.startListenChanges();
20538
- this.markAsLoaded();
20577
+ (_a = this.networkSubscription) === null || _a === void 0 ? void 0 : _a.unsubscribe();
20578
+ this.networkSubscription = this.network.onNetworkStatusChanges
20579
+ .pipe(operators.map(function (connectionType) { return connectionType !== 'none'; }))
20580
+ .subscribe(function (online) {
20581
+ if (online) {
20582
+ _this.enable();
20583
+ _this.markAsPristine();
20584
+ _this.startListenChanges();
20585
+ }
20586
+ else {
20587
+ _this.disable();
20588
+ _this.markAsPristine();
20589
+ _this.stopListenChanges();
20590
+ }
20591
+ _this.markAsLoaded();
20592
+ });
20539
20593
  };
20540
20594
  AccountPage.prototype.onLogout = function () {
20595
+ var _a;
20541
20596
  this.isLogin = false;
20542
20597
  this.email.confirmed = false;
20543
20598
  this.email.notConfirmed = false;
@@ -20545,13 +20600,14 @@
20545
20600
  this.email.error = undefined;
20546
20601
  this.form.reset();
20547
20602
  this.disable();
20603
+ (_a = this.networkSubscription) === null || _a === void 0 ? void 0 : _a.unsubscribe();
20548
20604
  this.stopListenChanges();
20549
20605
  // Back to home
20550
20606
  this.router.navigateByUrl('/');
20551
20607
  };
20552
20608
  AccountPage.prototype.refresh = function (event) {
20553
20609
  return __awaiter(this, void 0, void 0, function () {
20554
- return __generator(this, function (_a) {
20610
+ return __generator(this, function (_b) {
20555
20611
  this.disable();
20556
20612
  this.markAsLoading();
20557
20613
  return [2 /*return*/, this.accountService.refresh()];
@@ -20572,8 +20628,8 @@
20572
20628
  AccountPage.prototype.sendConfirmationEmail = function (event) {
20573
20629
  return __awaiter(this, void 0, void 0, function () {
20574
20630
  var json, err_1;
20575
- return __generator(this, function (_a) {
20576
- switch (_a.label) {
20631
+ return __generator(this, function (_b) {
20632
+ switch (_b.label) {
20577
20633
  case 0:
20578
20634
  json = this.form.value;
20579
20635
  json.email = this.form.controls['email'].value;
@@ -20583,17 +20639,17 @@
20583
20639
  }
20584
20640
  this.email.sending = true;
20585
20641
  console.debug('[account] Sending confirmation email...');
20586
- _a.label = 1;
20642
+ _b.label = 1;
20587
20643
  case 1:
20588
- _a.trys.push([1, 3, , 4]);
20644
+ _b.trys.push([1, 3, , 4]);
20589
20645
  return [4 /*yield*/, this.accountService.sendConfirmationEmail(json.email, json.settings && json.settings.locale || this.translate.currentLang)];
20590
20646
  case 2:
20591
- _a.sent();
20647
+ _b.sent();
20592
20648
  console.debug('[account] Confirmation email sent.');
20593
20649
  this.email.sending = false;
20594
20650
  return [3 /*break*/, 4];
20595
20651
  case 3:
20596
- err_1 = _a.sent();
20652
+ err_1 = _b.sent();
20597
20653
  this.email.sending = false;
20598
20654
  this.email.error = err_1 && err_1.message || err_1;
20599
20655
  return [3 /*break*/, 4];
@@ -20605,41 +20661,41 @@
20605
20661
  AccountPage.prototype.save = function (event) {
20606
20662
  return __awaiter(this, void 0, void 0, function () {
20607
20663
  var newAccount, err_2;
20608
- return __generator(this, function (_a) {
20609
- switch (_a.label) {
20664
+ return __generator(this, function (_b) {
20665
+ switch (_b.label) {
20610
20666
  case 0:
20611
20667
  if (this.saving)
20612
- return [2 /*return*/];
20668
+ return [2 /*return*/, false];
20613
20669
  if (!!this.form.valid) return [3 /*break*/, 2];
20614
20670
  return [4 /*yield*/, AppFormUtils.waitWhilePending(this.form)];
20615
20671
  case 1:
20616
- _a.sent();
20672
+ _b.sent();
20617
20673
  if (this.form.invalid) {
20618
20674
  AppFormUtils.logFormErrors(this.form);
20619
20675
  this.form.markAllAsTouched();
20620
- return [2 /*return*/];
20676
+ return [2 /*return*/, false];
20621
20677
  }
20622
- _a.label = 2;
20678
+ _b.label = 2;
20623
20679
  case 2:
20624
20680
  this.submitted = true;
20625
20681
  this.saving = true;
20626
20682
  this.error = undefined;
20627
20683
  newAccount = exports.Account.fromObject(Object.assign(Object.assign({}, (this.accountService.account.asObject())), (this.form.value)));
20628
20684
  console.debug('[account] Saving account...', newAccount);
20629
- _a.label = 3;
20685
+ _b.label = 3;
20630
20686
  case 3:
20631
- _a.trys.push([3, 5, 6, 7]);
20687
+ _b.trys.push([3, 5, 6, 7]);
20632
20688
  this.disable();
20633
20689
  return [4 /*yield*/, this.accountService.save(newAccount)];
20634
20690
  case 4:
20635
- _a.sent();
20691
+ _b.sent();
20636
20692
  this.markAsPristine();
20637
- return [3 /*break*/, 7];
20693
+ return [2 /*return*/, true];
20638
20694
  case 5:
20639
- err_2 = _a.sent();
20695
+ err_2 = _b.sent();
20640
20696
  console.error(err_2);
20641
20697
  this.error = err_2 && err_2.message || err_2;
20642
- return [3 /*break*/, 7];
20698
+ return [2 /*return*/, false];
20643
20699
  case 6:
20644
20700
  this.saving = false;
20645
20701
  this.enable();
@@ -20651,7 +20707,7 @@
20651
20707
  };
20652
20708
  AccountPage.prototype.enable = function () {
20653
20709
  var _this = this;
20654
- _super.prototype.enable.call(this);
20710
+ _super_1.prototype.enable.call(this);
20655
20711
  // Some fields are always disable
20656
20712
  this.form.controls.email.disable();
20657
20713
  this.form.controls.mainProfile.disable();
@@ -20664,6 +20720,17 @@
20664
20720
  });
20665
20721
  this.markForCheck();
20666
20722
  };
20723
+ AccountPage.prototype.cancel = function () {
20724
+ var _super = Object.create(null, {
20725
+ cancel: { get: function () { return _super_1.prototype.cancel; } }
20726
+ });
20727
+ return __awaiter(this, void 0, void 0, function () {
20728
+ return __generator(this, function (_b) {
20729
+ _super.cancel.call(this);
20730
+ return [2 /*return*/];
20731
+ });
20732
+ });
20733
+ };
20667
20734
  AccountPage.prototype.markForCheck = function () {
20668
20735
  this.cd.markForCheck();
20669
20736
  };
@@ -20672,16 +20739,17 @@
20672
20739
  AccountPage.decorators = [
20673
20740
  { type: i0.Component, args: [{
20674
20741
  selector: 'app-page-account',
20675
- template: "<app-toolbar [title]=\"'ACCOUNT.TITLE'|translate\" color=\"primary\"\n [hasValidate]=\"form.dirty && !saving\"\n (onValidate)=\"save($event)\"\n [canGoBack]=\"false\">\n\n <ion-buttons slot=\"end\">\n <!-- loader -->\n <ion-spinner *ngIf=\"loading || saving; else endButtons\"></ion-spinner>\n\n <ng-template #endButtons>\n <!-- refresh button -->\n <ion-button [matTooltip]=\"'COMMON.BTN_REFRESH'|translate\"\n (click)=\"refresh($event)\">\n <mat-icon slot=\"icon-only\">refresh</mat-icon>\n </ion-button>\n </ng-template>\n </ion-buttons>\n</app-toolbar>\n\n<ion-content>\n\n <!-- error -->\n <ion-item *ngIf=\"error\" lines=\"none\">\n <ion-icon color=\"danger\" slot=\"start\" name=\"alert-circle\"></ion-icon>\n <ion-label color=\"danger\" class=\"error\" [innerHTML]=\"error|translate\"></ion-label>\n </ion-item>\n\n <form [formGroup]=\"form\" novalidate (ngSubmit)=\"save($event)\" class=\"form-container\">\n\n <mat-tab-group>\n\n <!-- TAB: user details -->\n <mat-tab label=\"{{'ACCOUNT.USER_DETAILS.TITLE'|translate}}\" class=\"ion-padding\">\n <ng-template mat-tab-label>\n <mat-icon>\n <ion-icon matPrefix slot=\"start\" name=\"person-circle\"></ion-icon>\n </mat-icon>\n <ion-label>{{'ACCOUNT.USER_DETAILS.TITLE'|translate}}</ion-label>\n <ion-icon slot=\"end\" name=\"alert-circle\" color=\"danger\" *ngIf=\"submitted && form.invalid\"></ion-icon>\n </ng-template>\n\n <ion-grid class=\"ion-no-padding\">\n <ion-row class=\"ion-no-padding\">\n\n <!-- left margin -->\n <ion-col size=\"0\" size-lg=\"3\">&nbsp;</ion-col>\n\n <ion-col class=\"ion-padding\">\n <p [innerHTML]=\"'ACCOUNT.USER_DETAILS.DESCRIPTION'|translate\"></p>\n\n <!--h1>Identit\u00E9</h1-->\n\n <!-- Email -->\n <mat-form-field>\n <input matInput [placeholder]=\"'USER.EMAIL'|translate\"\n formControlName=\"email\" autocomplete=\"section-red email\">\n <mat-error *ngIf=\"form.controls.email.hasError('required') && form.controls.email.dirty\" translate>ERROR.FIELD_REQUIRED</mat-error>\n <mat-error *ngIf=\"form.controls.email.hasError('email') && form.controls.email.dirty\">\n <span>{{'ERROR.FIELD_NOT_VALID_EMAIL' | translate }}</span>\n </mat-error>\n </mat-form-field>\n\n <ion-grid *ngIf=\"email.notConfirmed\" class=\"ion-no-padding ion-padding-bottom\">\n <ion-row>\n <ion-col color=\"danger\">\n <ion-icon slot=\"start\" color=\"danger\" name=\"alert-circle\" color=\"danger\"></ion-icon>\n <ion-text [innerHTML]=\"'ACCOUNT.EMAIL_NOT_CONFIRMED_LABEL'|translate\"></ion-text>\n </ion-col>\n </ion-row>\n\n <ion-row style=\"height: 51px;\">\n <ion-col style=\"text-align: right;\">\n <ion-text>\n <small [innerHTML]=\"'ACCOUNT.EMAIL_NOT_RECEIVED_QUESTION'|translate\"></small>\n </ion-text>\n </ion-col>\n <ion-col size=\"auto\">\n <ion-button fill=\"solid\" color=\"secondary\" *ngIf=\"!email.sending\" (click)=\"sendConfirmationEmail($event)\">\n {{'ACCOUNT.BTN_RESEND'|translate}}\n </ion-button>\n <ion-spinner class=\"ion-no-padding\" *ngIf=\"email.sending\"></ion-spinner>\n </ion-col>\n </ion-row>\n\n <ion-row *ngIf=\"email.error && !email.sending\">\n <ion-col>\n <span *ngIf=\"email.error && !email.sending\" [innerHTML]=\"email.error|translate\"></span>\n </ion-col>\n </ion-row>\n </ion-grid>\n\n <!-- Last name -->\n <mat-form-field>\n <input matInput [appAutofocus]=\"true\" [autofocusDelay]=\"500\" [placeholder]=\"'USER.LAST_NAME'|translate\"\n formControlName=\"lastName\"\n autocomplete=\"section-blue family-name\"\n required>\n <mat-error *ngIf=\"form.controls.lastName.hasError('required') && form.controls.lastName.dirty\"\n translate>ERROR.FIELD_REQUIRED</mat-error>\n <mat-error *ngIf=\"form.controls.lastName.hasError('minlength') && form.controls.lastName.dirty\">\n <span>{{'ERROR.FIELD_MIN_LENGTH' | translate: {minLength: 2} }}</span>\n </mat-error>\n </mat-form-field>\n\n <!-- First name -->\n <mat-form-field>\n <input matInput [placeholder]=\"'USER.FIRST_NAME'|translate\" formControlName=\"firstName\" autocomplete=\"section-blue given-name\"\n required>\n <mat-error *ngIf=\"form.controls.firstName.hasError('required') && form.controls.firstName.dirty\"\n translate>ERROR.FIELD_REQUIRED</mat-error>\n <mat-error *ngIf=\"form.controls.firstName.hasError('minlength') && form.controls.firstName.dirty\">\n <span>{{'ERROR.FIELD_MIN_LENGTH' | translate: {minLength: 2} }}</span>\n </mat-error>\n </mat-form-field>\n\n\n <!-- Additional fields -->\n <ng-container *ngFor=\"let definition of additionalFields\">\n <app-form-field [definition]=\"definition\"\n [formControlName]=\"definition.key\"\n [required]=\"definition.extra && definition.extra.account && definition.extra.account.required || false\">\n </app-form-field>\n </ng-container>\n\n <h3 translate>ACCOUNT.USER_DETAILS.TECHNICAL_DIVIDER</h3>\n\n <!-- profile -->\n <mat-form-field>\n <input matInput hidden readonly=\"true\" [placeholder]=\"'ACCOUNT.USER_DETAILS.PROFILE'|translate\"\n formControlName=\"mainProfile\" required>\n <span>{{'USER.PROFILE_ENUM.'+form.controls.mainProfile.value | translate}}</span>\n <mat-error *ngIf=\"form.controls.firstName.hasError('mainProfile') && form.controls.mainProfile.dirty\"\n translate>ERROR.FIELD_REQUIRED</mat-error>\n </mat-form-field>\n\n <!-- pubkey -->\n <mat-form-field>\n <input matInput readonly=\"true\" [placeholder]=\"'ACCOUNT.USER_DETAILS.PUBKEY'|translate\" formControlName=\"pubkey\"\n required>\n <mat-error *ngIf=\"form.controls.firstName.hasError('pubkey') && form.controls.firstName.dirty\"\n translate>ERROR.FIELD_REQUIRED</mat-error>\n <mat-error *ngIf=\"form.controls.firstName.hasError('pubkey') && form.controls.firstName.dirty\">\n <span translate>ERROR.FIELD_NOT_VALID_PUBKEY</span>\n </mat-error>\n </mat-form-field>\n\n </ion-col>\n\n <!-- right margin -->\n <ion-col size=\"0\" size-lg=\"3\">&nbsp;</ion-col>\n </ion-row>\n </ion-grid>\n </mat-tab>\n\n <!-- TAB: settings -->\n <mat-tab label=\"{{'ACCOUNT.SETTINGS.TITLE'|translate}}\" formGroupName=\"settings\">\n <ng-template mat-tab-label>\n <mat-icon>\n <ion-icon matPrefix slot=\"start\" name=\"settings\"></ion-icon>\n </mat-icon>\n <ion-label translate>ACCOUNT.SETTINGS.TITLE</ion-label>\n </ng-template>\n\n <ion-grid class=\"ion-no-padding\">\n <ion-row class=\"ion-no-padding\">\n\n <!-- left margin -->\n <ion-col size=\"0\" size-lg=\"3\">&nbsp;\n </ion-col>\n\n <ion-col class=\"ion-padding\">\n\n <p [innerHTML]=\"'ACCOUNT.SETTINGS.DESCRIPTION'|translate\"></p>\n\n <mat-form-field>\n <mat-select [placeholder]=\"'ACCOUNT.SETTINGS.LOCALE'|translate\" formControlName=\"locale\" required>\n <mat-option *ngFor=\"let item of locales\" [value]=\"item.key\">\n {{item.value}}\n </mat-option>\n </mat-select>\n <mat-error *ngIf=\"settingsForm.controls.locale.hasError('required') && settingsForm.controls.locale.dirty\"\n translate>ERROR.FIELD_REQUIRED</mat-error>\n </mat-form-field>\n\n <!-- lat/long format-->\n <mat-form-field>\n <mat-select [placeholder]=\"'ACCOUNT.SETTINGS.LAT_LONG_FORMAT'|translate\" formControlName=\"latLongFormat\"\n required>\n <mat-option *ngFor=\"let item of latLongFormats\" [value]=\"item\">\n {{'COMMON.LAT_LONG.'+item+'_PLACEHOLDER'|translate}}\n </mat-option>\n </mat-select>\n <mat-error *ngIf=\"settingsForm.controls.latLongFormat.hasError('required') && settingsForm.controls.latLongFormat.dirty\"\n translate>ERROR.FIELD_REQUIRED</mat-error>\n </mat-form-field>\n\n <form formGroupName=\"content\">\n\n\n </form>\n </ion-col>\n\n <!-- right margin -->\n <ion-col size=\"0\" size-lg=\"3\">&nbsp;\n </ion-col>\n </ion-row>\n </ion-grid>\n </mat-tab>\n </mat-tab-group>\n\n\n </form>\n</ion-content>\n\n<ion-footer hidden-xs hidden-sm hidden-mobile>\n <app-form-buttons-bar (onCancel)=\"cancel()\" (onSave)=\"save($event)\" [disabled]=\"!form.dirty || saving\"></app-form-buttons-bar>\n</ion-footer>\n",
20742
+ template: "<app-toolbar [title]=\"'ACCOUNT.TITLE'|translate\" color=\"primary\"\n [hasValidate]=\"form.dirty && !saving\"\n (onValidate)=\"save($event)\"\n [canGoBack]=\"false\">\n\n <ion-buttons slot=\"end\">\n <!-- loader -->\n <ion-spinner *ngIf=\"loading || saving; else endButtons\"></ion-spinner>\n\n <ng-template #endButtons>\n <!-- refresh button (if online) -->\n <ion-button [matTooltip]=\"'COMMON.BTN_REFRESH'|translate\"\n *ngIf=\"network.online\"\n (click)=\"refresh($event)\">\n <mat-icon slot=\"icon-only\">refresh</mat-icon>\n </ion-button>\n </ng-template>\n </ion-buttons>\n</app-toolbar>\n\n<ion-content>\n\n <!-- error -->\n <ion-item *ngIf=\"error\" lines=\"none\">\n <ion-icon color=\"danger\" slot=\"start\" name=\"alert-circle\"></ion-icon>\n <ion-label color=\"danger\" class=\"error\" [innerHTML]=\"error|translate\"></ion-label>\n </ion-item>\n\n <form [formGroup]=\"form\" novalidate (ngSubmit)=\"save($event)\" class=\"form-container\">\n\n <mat-tab-group>\n\n <!-- TAB: user details -->\n <mat-tab label=\"{{'ACCOUNT.USER_DETAILS.TITLE'|translate}}\" class=\"ion-padding\">\n <ng-template mat-tab-label>\n <mat-icon>\n <ion-icon matPrefix slot=\"start\" name=\"person-circle\"></ion-icon>\n </mat-icon>\n <ion-label>{{'ACCOUNT.USER_DETAILS.TITLE'|translate}}</ion-label>\n <ion-icon slot=\"end\" name=\"alert-circle\" color=\"danger\" *ngIf=\"submitted && form.invalid\"></ion-icon>\n </ng-template>\n\n <ion-grid class=\"ion-no-padding\">\n <ion-row class=\"ion-no-padding\">\n\n <!-- left margin -->\n <ion-col size=\"0\" size-lg=\"3\">&nbsp;</ion-col>\n\n <ion-col class=\"ion-padding\">\n <p [innerHTML]=\"'ACCOUNT.USER_DETAILS.DESCRIPTION'|translate\"></p>\n\n <!--h1>Identit\u00E9</h1-->\n\n <!-- Email -->\n <mat-form-field>\n <input matInput [placeholder]=\"'USER.EMAIL'|translate\"\n formControlName=\"email\" autocomplete=\"section-red email\">\n <mat-error *ngIf=\"form.controls.email.hasError('required') && form.controls.email.dirty\" translate>ERROR.FIELD_REQUIRED</mat-error>\n <mat-error *ngIf=\"form.controls.email.hasError('email') && form.controls.email.dirty\">\n <span>{{'ERROR.FIELD_NOT_VALID_EMAIL' | translate }}</span>\n </mat-error>\n </mat-form-field>\n\n <ion-grid *ngIf=\"email.notConfirmed\" class=\"ion-no-padding ion-padding-bottom\">\n <ion-row>\n <ion-col color=\"danger\">\n <ion-icon slot=\"start\" color=\"danger\" name=\"alert-circle\" color=\"danger\"></ion-icon>\n <ion-text [innerHTML]=\"'ACCOUNT.EMAIL_NOT_CONFIRMED_LABEL'|translate\"></ion-text>\n </ion-col>\n </ion-row>\n\n <ion-row style=\"height: 51px;\">\n <ion-col style=\"text-align: right;\">\n <ion-text>\n <small [innerHTML]=\"'ACCOUNT.EMAIL_NOT_RECEIVED_QUESTION'|translate\"></small>\n </ion-text>\n </ion-col>\n <ion-col size=\"auto\">\n <ion-button fill=\"solid\" color=\"secondary\" *ngIf=\"!email.sending\" (click)=\"sendConfirmationEmail($event)\">\n {{'ACCOUNT.BTN_RESEND'|translate}}\n </ion-button>\n <ion-spinner class=\"ion-no-padding\" *ngIf=\"email.sending\"></ion-spinner>\n </ion-col>\n </ion-row>\n\n <ion-row *ngIf=\"email.error && !email.sending\">\n <ion-col>\n <span *ngIf=\"email.error && !email.sending\" [innerHTML]=\"email.error|translate\"></span>\n </ion-col>\n </ion-row>\n </ion-grid>\n\n <!-- Last name -->\n <mat-form-field>\n <input matInput [appAutofocus]=\"true\" [autofocusDelay]=\"500\" [placeholder]=\"'USER.LAST_NAME'|translate\"\n formControlName=\"lastName\"\n autocomplete=\"section-blue family-name\"\n required>\n <mat-error *ngIf=\"form.controls.lastName.hasError('required') && form.controls.lastName.dirty\"\n translate>ERROR.FIELD_REQUIRED</mat-error>\n <mat-error *ngIf=\"form.controls.lastName.hasError('minlength') && form.controls.lastName.dirty\">\n <span>{{'ERROR.FIELD_MIN_LENGTH' | translate: {minLength: 2} }}</span>\n </mat-error>\n </mat-form-field>\n\n <!-- First name -->\n <mat-form-field>\n <input matInput [placeholder]=\"'USER.FIRST_NAME'|translate\" formControlName=\"firstName\" autocomplete=\"section-blue given-name\"\n required>\n <mat-error *ngIf=\"form.controls.firstName.hasError('required') && form.controls.firstName.dirty\"\n translate>ERROR.FIELD_REQUIRED</mat-error>\n <mat-error *ngIf=\"form.controls.firstName.hasError('minlength') && form.controls.firstName.dirty\">\n <span>{{'ERROR.FIELD_MIN_LENGTH' | translate: {minLength: 2} }}</span>\n </mat-error>\n </mat-form-field>\n\n\n <!-- Additional fields -->\n <ng-container *ngFor=\"let definition of additionalFields\">\n <app-form-field [definition]=\"definition\"\n [formControlName]=\"definition.key\"\n [required]=\"definition.extra && definition.extra.account && definition.extra.account.required || false\">\n </app-form-field>\n </ng-container>\n\n <h3 translate>ACCOUNT.USER_DETAILS.TECHNICAL_DIVIDER</h3>\n\n <!-- profile -->\n <mat-form-field>\n <input matInput hidden readonly=\"true\" [placeholder]=\"'ACCOUNT.USER_DETAILS.PROFILE'|translate\"\n formControlName=\"mainProfile\" required>\n <span>{{'USER.PROFILE_ENUM.'+form.controls.mainProfile.value | translate}}</span>\n <mat-error *ngIf=\"form.controls.firstName.hasError('mainProfile') && form.controls.mainProfile.dirty\"\n translate>ERROR.FIELD_REQUIRED</mat-error>\n </mat-form-field>\n\n <!-- pubkey -->\n <mat-form-field>\n <input matInput readonly=\"true\" [placeholder]=\"'ACCOUNT.USER_DETAILS.PUBKEY'|translate\" formControlName=\"pubkey\"\n required>\n <mat-error *ngIf=\"form.controls.firstName.hasError('pubkey') && form.controls.firstName.dirty\"\n translate>ERROR.FIELD_REQUIRED</mat-error>\n <mat-error *ngIf=\"form.controls.firstName.hasError('pubkey') && form.controls.firstName.dirty\">\n <span translate>ERROR.FIELD_NOT_VALID_PUBKEY</span>\n </mat-error>\n </mat-form-field>\n\n </ion-col>\n\n <!-- right margin -->\n <ion-col size=\"0\" size-lg=\"3\">&nbsp;</ion-col>\n </ion-row>\n </ion-grid>\n </mat-tab>\n\n <!-- TAB: settings -->\n <mat-tab label=\"{{'ACCOUNT.SETTINGS.TITLE'|translate}}\" formGroupName=\"settings\">\n <ng-template mat-tab-label>\n <mat-icon>\n <ion-icon matPrefix slot=\"start\" name=\"settings\"></ion-icon>\n </mat-icon>\n <ion-label translate>ACCOUNT.SETTINGS.TITLE</ion-label>\n </ng-template>\n\n <ion-grid class=\"ion-no-padding\">\n <ion-row class=\"ion-no-padding\">\n\n <!-- left margin -->\n <ion-col size=\"0\" size-lg=\"3\">&nbsp;\n </ion-col>\n\n <ion-col class=\"ion-padding\">\n\n <p [innerHTML]=\"'ACCOUNT.SETTINGS.DESCRIPTION'|translate\"></p>\n\n <mat-form-field>\n <mat-select [placeholder]=\"'ACCOUNT.SETTINGS.LOCALE'|translate\" formControlName=\"locale\" required>\n <mat-option *ngFor=\"let item of locales\" [value]=\"item.key\">\n {{item.value}}\n </mat-option>\n </mat-select>\n <mat-error *ngIf=\"settingsForm.controls.locale.hasError('required') && settingsForm.controls.locale.dirty\"\n translate>ERROR.FIELD_REQUIRED</mat-error>\n </mat-form-field>\n\n <!-- lat/long format-->\n <mat-form-field>\n <mat-select [placeholder]=\"'ACCOUNT.SETTINGS.LAT_LONG_FORMAT'|translate\" formControlName=\"latLongFormat\"\n required>\n <mat-option *ngFor=\"let item of latLongFormats\" [value]=\"item\">\n {{'COMMON.LAT_LONG.'+item+'_PLACEHOLDER'|translate}}\n </mat-option>\n </mat-select>\n <mat-error *ngIf=\"settingsForm.controls.latLongFormat.hasError('required') && settingsForm.controls.latLongFormat.dirty\"\n translate>ERROR.FIELD_REQUIRED</mat-error>\n </mat-form-field>\n\n <form formGroupName=\"content\">\n\n\n </form>\n </ion-col>\n\n <!-- right margin -->\n <ion-col size=\"0\" size-lg=\"3\">&nbsp;\n </ion-col>\n </ion-row>\n </ion-grid>\n </mat-tab>\n </mat-tab-group>\n\n\n </form>\n</ion-content>\n\n<ion-footer hidden-xs hidden-sm hidden-mobile>\n <app-form-buttons-bar (onCancel)=\"cancel()\" (onSave)=\"save($event)\" [disabled]=\"!form.dirty || saving\"></app-form-buttons-bar>\n</ion-footer>\n",
20676
20743
  changeDetection: i0.ChangeDetectionStrategy.OnPush,
20677
20744
  styles: ["mat-tab-group .mat-tab-body-content{padding:var(--ion-grid-padding);display:grid}form,form mat-form-field{width:100%}"]
20678
20745
  },] }
20679
20746
  ];
20680
20747
  AccountPage.ctorParameters = function () { return [
20681
20748
  { type: i0.Injector },
20682
- { type: i3.Router },
20683
20749
  { type: i1.FormBuilder },
20684
20750
  { type: AccountService },
20751
+ { type: NetworkService },
20752
+ { type: i3.Router },
20685
20753
  { type: AccountValidatorService },
20686
20754
  { type: UserSettingsValidatorService },
20687
20755
  { type: i1$2.TranslateService },
@@ -21223,12 +21291,14 @@
21223
21291
  SettingsPage.prototype.save = function (event, options) {
21224
21292
  return __awaiter(this, void 0, void 0, function () {
21225
21293
  var data, peerChanged, err_1;
21294
+ var _this = this;
21226
21295
  return __generator(this, function (_a) {
21227
21296
  switch (_a.label) {
21228
21297
  case 0:
21229
21298
  if (this.saving)
21230
21299
  return [2 /*return*/]; // Skip
21231
- event === null || event === void 0 ? void 0 : event.preventDefault();
21300
+ if (event)
21301
+ event.preventDefault();
21232
21302
  // Remove all empty controls
21233
21303
  this.propertiesFormHelper.removeAllEmpty();
21234
21304
  if (!!this.form.valid) return [3 /*break*/, 2];
@@ -21245,6 +21315,7 @@
21245
21315
  console.debug('[settings] Saving local settings...');
21246
21316
  this.saving = true;
21247
21317
  this.error = undefined;
21318
+ this.markForCheck();
21248
21319
  return [4 /*yield*/, this.getValue()];
21249
21320
  case 3:
21250
21321
  data = _a.sent();
@@ -21276,8 +21347,11 @@
21276
21347
  this.enable({ emitEvent: false });
21277
21348
  // Apply inheritance
21278
21349
  this.setAccountInheritance(data.accountInheritance, { emitEvent: false });
21279
- this.saving = false;
21280
- this.markForCheck();
21350
+ // Add a delay, because of option menu that is always opened after save!!
21351
+ setTimeout(function () {
21352
+ _this.saving = false;
21353
+ _this.markForCheck();
21354
+ }, 400);
21281
21355
  return [7 /*endfinally*/];
21282
21356
  case 8: return [2 /*return*/];
21283
21357
  }
@@ -21357,25 +21431,11 @@
21357
21431
  };
21358
21432
  SettingsPage.prototype.close = function (event) {
21359
21433
  return __awaiter(this, void 0, void 0, function () {
21360
- var confirmation;
21361
21434
  return __generator(this, function (_a) {
21362
- switch (_a.label) {
21363
- case 0:
21364
- if (this.saving)
21365
- return [2 /*return*/];
21366
- if (!this.dirty) return [3 /*break*/, 3];
21367
- return [4 /*yield*/, Alerts.askSaveBeforeLeave(this.alertCtrl, this.translate)];
21368
- case 1:
21369
- confirmation = _a.sent();
21370
- if (!confirmation) return [3 /*break*/, 3];
21371
- return [4 /*yield*/, this.save(event)];
21372
- case 2:
21373
- _a.sent();
21374
- _a.label = 3;
21375
- case 3:
21376
- // Back to home
21377
- return [2 /*return*/, this.router.navigateByUrl('/', { replaceUrl: true })]; // back to home
21378
- }
21435
+ if (this.saving)
21436
+ return [2 /*return*/];
21437
+ // Back to home
21438
+ return [2 /*return*/, this.router.navigateByUrl('/', { replaceUrl: true })]; // back to home
21379
21439
  });
21380
21440
  });
21381
21441
  };
@@ -21433,7 +21493,7 @@
21433
21493
  SettingsPage.decorators = [
21434
21494
  { type: i0.Component, args: [{
21435
21495
  selector: 'page-settings',
21436
- template: "<app-toolbar [title]=\"'SETTINGS.TITLE'|translate\" color=\"primary\"\n [hasValidate]=\"form.dirty && !saving\"\n [hasClose]=\"!form.dirty && !saving\"\n (onValidate)=\"save($event)\"\n (onClose)=\"close($event)\">\n\n <ion-buttons slot=\"end\">\n <!-- options menu -->\n <ion-button [matMenuTriggerFor]=\"optionsMenu\" [disabled]=\"saving\">\n <mat-icon slot=\"icon-only\">more_vert</mat-icon>\n </ion-button>\n </ion-buttons>\n</app-toolbar>\n\n<!-- Options menu -->\n<mat-menu #optionsMenu=\"matMenu\">\n <!-- Clear cache -->\n <button mat-menu-item (click)=\"clearCache($event)\">\n <ion-label translate>SETTINGS.BTN_CLEAR_CACHE</ion-label>\n </button>\n\n <!-- Reset -->\n <button mat-menu-item\n [disabled]=\"!dirty\"\n (click)=\"cancel($event)\">\n <ion-label>\n <mat-icon><ion-icon slot=\"start\" name=\"refresh\"></ion-icon></mat-icon>&nbsp;\n <span translate>COMMON.BTN_RESET</span>\n </ion-label>\n </button>\n</mat-menu>\n\n<ion-content>\n\n <form [formGroup]=\"form\" novalidate (ngSubmit)=\"save($event)\" class=\"form-container\">\n\n <ion-grid class=\"ion-no-padding\">\n <ion-row class=\"ion-no-padding\">\n\n <!-- left margin -->\n <ion-col size=\"0\" size-lg=\"2\">&nbsp;\n </ion-col>\n\n <ion-col class=\"ion-padding\">\n\n <!-- error -->\n <ion-item *ngIf=\"error\" lines=\"none\">\n <ion-icon color=\"danger\" slot=\"start\" name=\"alert-circle\"></ion-icon>\n <ion-label color=\"danger\" class=\"error\" [innerHTML]=\"error|translate\"></ion-label>\n </ion-item>\n\n <p [innerHTML]=\"'SETTINGS.DESCRIPTION'|translate\"></p>\n\n <!-- account inheritance (desktop) -->\n <mat-form-field [hidden]=\"!isLogin\" *ngIf=\"!mobile; else accountInheritanceMobile\">\n <input matInput hidden formControlName=\"accountInheritance\" type=\"text\">\n\n <!-- check box (if desktop) -->\n <mat-checkbox\n *ngIf=\"!mobile; else accountInheritanceMobile\"\n (change)=\"setAccountInheritance($event.checked)\"\n [checked]=\"accountInheritance\">\n <span translate>SETTINGS.INHERIT_FROM_ACCOUNT</span>\n </mat-checkbox>\n\n <mat-error *ngIf=\"form.controls.accountInheritance.hasError('required')\" translate>ERROR.FIELD_REQUIRED\n </mat-error>\n </mat-form-field>\n\n <!-- account inheritance (if mobile) -->\n <ng-template #accountInheritanceMobile>\n <mat-form-field [hidden]=\"!isLogin\">\n <input matInput hidden formControlName=\"accountInheritance\" type=\"text\">\n\n <!-- slide toggle -->\n <ion-text translate>SETTINGS.INHERIT_FROM_ACCOUNT</ion-text>\n <mat-slide-toggle matSuffix\n (change)=\"setAccountInheritance($event.checked)\"\n [checked]=\"accountInheritance\">\n\n </mat-slide-toggle>\n <mat-error *ngIf=\"form.controls.accountInheritance.hasError('required')\" translate>ERROR.FIELD_REQUIRED\n </mat-error>\n </mat-form-field>\n </ng-template>\n\n <!-- locale -->\n <mat-form-field>\n <mat-select [placeholder]=\"'SETTINGS.LOCALE'|translate\" formControlName=\"locale\" required>\n <mat-option *ngFor=\"let item of locales\" [value]=\"item.key\">\n {{item.value}}\n </mat-option>\n </mat-select>\n <mat-error *ngIf=\"form.controls.locale.hasError('required')\" translate>ERROR.FIELD_REQUIRED</mat-error>\n </mat-form-field>\n\n <!-- Network entry -->\n <h3>\n <ion-text translate>SETTINGS.NETWORK_DIVIDER</ion-text>\n </h3>\n\n <!-- Peer address -->\n <mat-form-field>\n <input matInput type=\"text\" [placeholder]=\"'SETTINGS.PEER_URL'|translate\" formControlName=\"peerUrl\"\n required>\n\n <button mat-icon-button type=\"button\" matSuffix (click)=\"showSelectPeerModal()\" tabindex=\"-1\"\n [title]=\"'SETTINGS.BTN_CHANGE_PEER'|translate\">\n <mat-icon>search</mat-icon>\n </button>\n\n <mat-error *ngIf=\"form.controls.peerUrl.hasError('required')\"\n translate>ERROR.FIELD_REQUIRED\n </mat-error>\n <mat-error *ngIf=\"form.controls.peerUrl.hasError('peerAlive')\"\n translate>SETTINGS.ERROR.PEER_NOT_REACHABLE\n </mat-error>\n <mat-error *ngIf=\"form.controls.peerUrl.hasError('peerNotCompatible')\"\n translate>SETTINGS.ERROR.PEER_NOT_COMPATIBLE\n </mat-error>\n </mat-form-field>\n\n <!-- Offline mode (if mobile) -->\n <mat-form-field *ngIf=\"mobile\">\n <input matInput hidden formControlName=\"accountInheritance\" type=\"text\">\n\n <!-- slide toggle -->\n <ion-text translate>SETTINGS.OFFLINE_MODE</ion-text>\n <mat-slide-toggle matSuffix\n (change)=\"network.setForceOffline($event.checked)\"\n [checked]=\"network.offline\">\n\n </mat-slide-toggle>\n </mat-form-field>\n\n <!-- Data entry -->\n <h3>\n <ion-text translate>SETTINGS.DATA_ENTRY_DIVIDER</ion-text>\n </h3>\n\n <!-- Usage mode -->\n <mat-form-field>\n <mat-select [placeholder]=\"'SETTINGS.USAGE_MODE'|translate\" formControlName=\"usageMode\"\n required>\n <mat-option *ngFor=\"let item of usageModes\" [value]=\"item\">\n {{'SETTINGS.USAGE_MODES.'+item|translate}}\n </mat-option>\n </mat-select>\n <mat-error *ngIf=\"form.controls.usageMode.hasError('required')\"\n translate>ERROR.FIELD_REQUIRED\n </mat-error>\n </mat-form-field>\n\n <!-- lat/long format-->\n <mat-form-field>\n <mat-select [placeholder]=\"'SETTINGS.LAT_LONG_FORMAT'|translate\" formControlName=\"latLongFormat\"\n required>\n <mat-option *ngFor=\"let item of latLongFormats\" [value]=\"item\">\n {{'COMMON.LAT_LONG.'+item+'_PLACEHOLDER'|translate}}\n </mat-option>\n </mat-select>\n <mat-error\n *ngIf=\"form.controls.latLongFormat.hasError('required')\"\n translate>ERROR.FIELD_REQUIRED\n </mat-error>\n </mat-form-field>\n\n <!-- fields options -->\n <ion-grid formArrayName=\"properties\" class=\"ion-no-padding\">\n <ion-row class=\"ion-no-padding\">\n <ion-col class=\"ion-no-padding ion-align-self-end\" size=\"12\">\n <span class=\"toolbar-spacer\"></span>\n\n <!-- Show more options -->\n <ion-button color=\"light\" *ngIf=\"propertiesForm?.length === 0\"\n [title]=\"'SETTINGS.BTN_SHOW_MORE_HELP'|translate\"\n (click)=\"propertiesFormHelper.add()\">\n <ion-label translate>COMMON.BTN_SHOW_MORE</ion-label>\n <mat-icon slot=\"end\">arrow_drop_down</mat-icon>\n </ion-button>\n </ion-col>\n </ion-row>\n\n <!-- Fields options -->\n <ng-container *ngFor=\"let propertyForm of propertiesForm?.controls; let i=index\">\n <ion-row class=\"ion-no-padding\" [formGroupName]=\"i\">\n\n <!-- property key -->\n <ion-col class=\"ion-no-padding\">\n <mat-form-field>\n <mat-select formControlName=\"key\"\n [placeholder]=\" 'SETTINGS.PROPERTY_KEY'|translate\">\n <mat-option *ngFor=\"let item of propertyDefinitions\" [value]=\"item.key\">{{ item.label | translate }}\n </mat-option>\n </mat-select>\n </mat-form-field>\n </ion-col>\n\n <!-- property value -->\n <ion-col class=\"ion-no-padding\" padding-left>\n <app-form-field *ngIf=\"getPropertyDefinition(i); let definition\"\n floatLabel=\"never\"\n [definition]=\"definition\"\n [formControl]=\"propertyForm.controls.value\"\n [placeholder]=\"'SETTINGS.PROPERTY_VALUE' | translate\"\n [required]=\"true\">\n </app-form-field>\n </ion-col>\n <ion-col size=\"2\" class=\"ion-no-padding\">\n <button type=\"button\" mat-icon-button color=\"light\"\n [disabled]=\"loading\"\n [title]=\"'COMMON.BTN_DELETE'|translate\"\n (click)=\"removePropertyAt(i)\">\n <mat-icon>close</mat-icon>\n </button>\n <button *ngIf=\"propertiesFormHelper.isLast(i)\"\n type=\"button\"\n mat-icon-button\n color=\"light\"\n [disabled]=\"loading\"\n [title]=\"'SETTINGS.BTN_ADD_PROPERTY'|translate\"\n (click)=\"propertiesFormHelper.add()\">\n <mat-icon>add</mat-icon>\n </button>\n </ion-col>\n </ion-row>\n </ng-container>\n </ion-grid>\n </ion-col>\n\n <!-- right margin -->\n <ion-col size=\"0\" size-lg=\"2\">&nbsp;\n </ion-col>\n </ion-row>\n </ion-grid>\n\n\n </form>\n</ion-content>\n\n<ion-footer hidden-xs hidden-sm hidden-mobile>\n <app-form-buttons-bar (onCancel)=\"cancel()\" (onSave)=\"save($event)\" [disabled]=\"!form.dirty || saving\"></app-form-buttons-bar>\n</ion-footer>\n",
21496
+ template: "<app-toolbar [title]=\"'SETTINGS.TITLE'|translate\" color=\"primary\"\n [hasValidate]=\"dirty && !saving\"\n [hasClose]=\"!dirty && !saving\"\n (onValidate)=\"save($event)\"\n (onClose)=\"close($event)\">\n\n <ion-buttons slot=\"end\">\n <!-- options menu -->\n <ion-button [matMenuTriggerFor]=\"optionsMenu\"\n *ngIf=\"!saving\"\n [disabled]=\"saving\">\n <mat-icon slot=\"icon-only\">more_vert</mat-icon>\n </ion-button>\n </ion-buttons>\n</app-toolbar>\n\n<!-- Options menu -->\n<mat-menu #optionsMenu=\"matMenu\">\n <!-- Clear cache -->\n <button mat-menu-item (click)=\"clearCache($event)\">\n <ion-label translate>SETTINGS.BTN_CLEAR_CACHE</ion-label>\n </button>\n\n <!-- Reset -->\n <button mat-menu-item\n [disabled]=\"!dirty\"\n (click)=\"cancel($event)\">\n <ion-label>\n <mat-icon><ion-icon slot=\"start\" name=\"refresh\"></ion-icon></mat-icon>&nbsp;\n <span translate>COMMON.BTN_RESET</span>\n </ion-label>\n </button>\n</mat-menu>\n\n<ion-content>\n\n <form [formGroup]=\"form\" novalidate (ngSubmit)=\"save($event)\" class=\"form-container\">\n\n <ion-grid class=\"ion-no-padding\">\n <ion-row class=\"ion-no-padding\">\n\n <!-- left margin -->\n <ion-col size=\"0\" size-lg=\"2\">&nbsp;\n </ion-col>\n\n <ion-col class=\"ion-padding\">\n\n <!-- error -->\n <ion-item *ngIf=\"error\" lines=\"none\">\n <ion-icon color=\"danger\" slot=\"start\" name=\"alert-circle\"></ion-icon>\n <ion-label color=\"danger\" class=\"error\" [innerHTML]=\"error|translate\"></ion-label>\n </ion-item>\n\n <p [innerHTML]=\"'SETTINGS.DESCRIPTION'|translate\"></p>\n\n <!-- account inheritance (desktop) -->\n <mat-form-field [hidden]=\"!isLogin\" *ngIf=\"!mobile; else accountInheritanceMobile\">\n <input matInput hidden formControlName=\"accountInheritance\" type=\"text\">\n\n <!-- check box (if desktop) -->\n <mat-checkbox\n *ngIf=\"!mobile; else accountInheritanceMobile\"\n (change)=\"setAccountInheritance($event.checked)\"\n [checked]=\"accountInheritance\">\n <span translate>SETTINGS.INHERIT_FROM_ACCOUNT</span>\n </mat-checkbox>\n\n <mat-error *ngIf=\"form.controls.accountInheritance.hasError('required')\" translate>ERROR.FIELD_REQUIRED\n </mat-error>\n </mat-form-field>\n\n <!-- account inheritance (if mobile) -->\n <ng-template #accountInheritanceMobile>\n <mat-form-field [hidden]=\"!isLogin\">\n <input matInput hidden formControlName=\"accountInheritance\" type=\"text\">\n\n <!-- slide toggle -->\n <ion-text translate>SETTINGS.INHERIT_FROM_ACCOUNT</ion-text>\n <mat-slide-toggle matSuffix\n (change)=\"setAccountInheritance($event.checked)\"\n [checked]=\"accountInheritance\">\n\n </mat-slide-toggle>\n <mat-error *ngIf=\"form.controls.accountInheritance.hasError('required')\" translate>ERROR.FIELD_REQUIRED\n </mat-error>\n </mat-form-field>\n </ng-template>\n\n <!-- locale -->\n <mat-form-field>\n <mat-select [placeholder]=\"'SETTINGS.LOCALE'|translate\" formControlName=\"locale\" required>\n <mat-option *ngFor=\"let item of locales\" [value]=\"item.key\">\n {{item.value}}\n </mat-option>\n </mat-select>\n <mat-error *ngIf=\"form.controls.locale.hasError('required')\" translate>ERROR.FIELD_REQUIRED</mat-error>\n </mat-form-field>\n\n <!-- Network entry -->\n <h3>\n <ion-text translate>SETTINGS.NETWORK_DIVIDER</ion-text>\n </h3>\n\n <!-- Peer address -->\n <mat-form-field>\n <input matInput type=\"text\" [placeholder]=\"'SETTINGS.PEER_URL'|translate\" formControlName=\"peerUrl\"\n required>\n\n <button mat-icon-button type=\"button\" matSuffix (click)=\"showSelectPeerModal()\" tabindex=\"-1\"\n [title]=\"'SETTINGS.BTN_CHANGE_PEER'|translate\">\n <mat-icon>search</mat-icon>\n </button>\n\n <mat-error *ngIf=\"form.controls.peerUrl.hasError('required')\"\n translate>ERROR.FIELD_REQUIRED\n </mat-error>\n <mat-error *ngIf=\"form.controls.peerUrl.hasError('peerAlive')\"\n translate>SETTINGS.ERROR.PEER_NOT_REACHABLE\n </mat-error>\n <mat-error *ngIf=\"form.controls.peerUrl.hasError('peerNotCompatible')\"\n translate>SETTINGS.ERROR.PEER_NOT_COMPATIBLE\n </mat-error>\n </mat-form-field>\n\n <!-- Offline mode (if mobile) -->\n <mat-form-field *ngIf=\"mobile\">\n <input matInput hidden formControlName=\"accountInheritance\" type=\"text\">\n\n <!-- slide toggle -->\n <ion-text translate>SETTINGS.OFFLINE_MODE</ion-text>\n <mat-slide-toggle matSuffix\n (change)=\"network.setForceOffline($event.checked)\"\n [checked]=\"network.offline\">\n\n </mat-slide-toggle>\n </mat-form-field>\n\n <!-- Data entry -->\n <h3>\n <ion-text translate>SETTINGS.DATA_ENTRY_DIVIDER</ion-text>\n </h3>\n\n <!-- Usage mode -->\n <mat-form-field>\n <mat-select [placeholder]=\"'SETTINGS.USAGE_MODE'|translate\" formControlName=\"usageMode\"\n required>\n <mat-option *ngFor=\"let item of usageModes\" [value]=\"item\">\n {{'SETTINGS.USAGE_MODES.'+item|translate}}\n </mat-option>\n </mat-select>\n <mat-error *ngIf=\"form.controls.usageMode.hasError('required')\"\n translate>ERROR.FIELD_REQUIRED\n </mat-error>\n </mat-form-field>\n\n <!-- lat/long format-->\n <mat-form-field>\n <mat-select [placeholder]=\"'SETTINGS.LAT_LONG_FORMAT'|translate\" formControlName=\"latLongFormat\"\n required>\n <mat-option *ngFor=\"let item of latLongFormats\" [value]=\"item\">\n {{'COMMON.LAT_LONG.'+item+'_PLACEHOLDER'|translate}}\n </mat-option>\n </mat-select>\n <mat-error\n *ngIf=\"form.controls.latLongFormat.hasError('required')\"\n translate>ERROR.FIELD_REQUIRED\n </mat-error>\n </mat-form-field>\n\n <!-- fields options -->\n <ion-grid formArrayName=\"properties\" class=\"ion-no-padding\">\n <ion-row class=\"ion-no-padding\">\n <ion-col class=\"ion-no-padding ion-align-self-end\" size=\"12\">\n <span class=\"toolbar-spacer\"></span>\n\n <!-- Show more options -->\n <ion-button color=\"light\" *ngIf=\"propertiesForm?.length === 0\"\n [title]=\"'SETTINGS.BTN_SHOW_MORE_HELP'|translate\"\n (click)=\"propertiesFormHelper.add()\">\n <ion-label translate>COMMON.BTN_SHOW_MORE</ion-label>\n <mat-icon slot=\"end\">arrow_drop_down</mat-icon>\n </ion-button>\n </ion-col>\n </ion-row>\n\n <!-- Fields options -->\n <ng-container *ngFor=\"let propertyForm of propertiesForm?.controls; let i=index\">\n <ion-row class=\"ion-no-padding\" [formGroupName]=\"i\">\n\n <!-- property key -->\n <ion-col class=\"ion-no-padding\">\n <mat-form-field>\n <mat-select formControlName=\"key\"\n [placeholder]=\" 'SETTINGS.PROPERTY_KEY'|translate\">\n <mat-option *ngFor=\"let item of propertyDefinitions\" [value]=\"item.key\">{{ item.label | translate }}\n </mat-option>\n </mat-select>\n </mat-form-field>\n </ion-col>\n\n <!-- property value -->\n <ion-col class=\"ion-no-padding\" padding-left>\n <app-form-field *ngIf=\"getPropertyDefinition(i); let definition\"\n floatLabel=\"never\"\n [definition]=\"definition\"\n [formControl]=\"propertyForm.controls.value\"\n [placeholder]=\"'SETTINGS.PROPERTY_VALUE' | translate\"\n [required]=\"true\">\n </app-form-field>\n </ion-col>\n <ion-col size=\"2\" class=\"ion-no-padding\">\n <button type=\"button\" mat-icon-button color=\"light\"\n [disabled]=\"loading\"\n [title]=\"'COMMON.BTN_DELETE'|translate\"\n (click)=\"removePropertyAt(i)\">\n <mat-icon>close</mat-icon>\n </button>\n <button *ngIf=\"propertiesFormHelper.isLast(i)\"\n type=\"button\"\n mat-icon-button\n color=\"light\"\n [disabled]=\"loading\"\n [title]=\"'SETTINGS.BTN_ADD_PROPERTY'|translate\"\n (click)=\"propertiesFormHelper.add()\">\n <mat-icon>add</mat-icon>\n </button>\n </ion-col>\n </ion-row>\n </ng-container>\n </ion-grid>\n </ion-col>\n\n <!-- right margin -->\n <ion-col size=\"0\" size-lg=\"2\">&nbsp;\n </ion-col>\n </ion-row>\n </ion-grid>\n\n\n </form>\n</ion-content>\n\n<ion-footer hidden-xs hidden-sm hidden-mobile>\n <app-form-buttons-bar (onCancel)=\"cancel()\" (onSave)=\"save($event)\" [disabled]=\"!form.dirty || saving\"></app-form-buttons-bar>\n</ion-footer>\n",
21437
21497
  providers: [
21438
21498
  { provide: ngxMaterialTable.ValidatorService, useExisting: LocalSettingsValidatorService },
21439
21499
  ],
@@ -25893,6 +25953,8 @@
25893
25953
  };
25894
25954
  AppEditor.prototype.ngOnDestroy = function () {
25895
25955
  this._subscription.unsubscribe();
25956
+ this._$ready.complete();
25957
+ this._$ready.unsubscribe();
25896
25958
  };
25897
25959
  AppEditor.prototype.addChildForm = function (form) {
25898
25960
  if (!form)
@@ -25990,12 +26052,7 @@
25990
26052
  AppEditor.prototype.ready = function (opts) {
25991
26053
  return __awaiter(this, void 0, void 0, function () {
25992
26054
  return __generator(this, function (_b) {
25993
- switch (_b.label) {
25994
- case 0: return [4 /*yield*/, firstTruePromise(this._$ready)];
25995
- case 1:
25996
- _b.sent();
25997
- return [2 /*return*/];
25998
- }
26055
+ return [2 /*return*/, waitForTrue(this._$ready, opts)];
25999
26056
  });
26000
26057
  });
26001
26058
  };
@@ -28763,6 +28820,7 @@
28763
28820
  exports.uncapitalizeFirstLetter = uncapitalizeFirstLetter;
28764
28821
  exports.updateValueAndValidity = updateValueAndValidity;
28765
28822
  exports.waitFor = waitFor;
28823
+ exports.waitForTrue = waitForTrue;
28766
28824
  exports.waitIdle = waitIdle;
28767
28825
  exports.waitWhilePending = waitWhilePending;
28768
28826
  exports["ɵ0"] = ɵ0$5;