@sumaris-net/ngx-components 0.28.2 → 1.0.0

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.
Files changed (42) hide show
  1. package/bundles/sumaris-net.ngx-components.umd.js +369 -337
  2. package/bundles/sumaris-net.ngx-components.umd.js.map +1 -1
  3. package/bundles/sumaris-net.ngx-components.umd.min.js +2 -2
  4. package/bundles/sumaris-net.ngx-components.umd.min.js.map +1 -1
  5. package/doc/changelog.md +4 -0
  6. package/esm2015/src/app/core/account/account.js +4 -5
  7. package/esm2015/src/app/core/auth/form/form-auth.js +3 -4
  8. package/esm2015/src/app/core/form/form.class.js +24 -12
  9. package/esm2015/src/app/core/form/list.form.js +2 -3
  10. package/esm2015/src/app/core/form/properties.form.js +2 -3
  11. package/esm2015/src/app/core/graphql/graphql.service.js +5 -8
  12. package/esm2015/src/app/core/services/account.service.js +177 -188
  13. package/esm2015/src/app/core/services/base-entity-service.class.js +9 -7
  14. package/esm2015/src/app/core/services/base-graphql-service.class.js +7 -2
  15. package/esm2015/src/app/core/services/config.service.js +9 -54
  16. package/esm2015/src/app/core/services/model/account.model.js +1 -1
  17. package/esm2015/src/app/core/services/model/settings.model.js +29 -2
  18. package/esm2015/src/app/core/services/network.service.js +5 -7
  19. package/esm2015/src/app/core/services/storage/entities-storage.service.js +3 -6
  20. package/esm2015/src/app/core/settings/settings.page.js +3 -4
  21. package/esm2015/src/app/shared/services/startable-service.class.js +13 -2
  22. package/esm2015/src/app/shared/services/translate-context.service.js +38 -13
  23. package/fesm2015/sumaris-net.ngx-components.js +308 -296
  24. package/fesm2015/sumaris-net.ngx-components.js.map +1 -1
  25. package/package.json +1 -1
  26. package/src/app/core/account/account.d.ts +0 -1
  27. package/src/app/core/auth/form/form-auth.d.ts +0 -1
  28. package/src/app/core/form/form.class.d.ts +10 -2
  29. package/src/app/core/form/list.form.d.ts +0 -1
  30. package/src/app/core/form/properties.form.d.ts +0 -1
  31. package/src/app/core/graphql/graphql.service.d.ts +0 -1
  32. package/src/app/core/services/account.service.d.ts +8 -21
  33. package/src/app/core/services/base-entity-service.class.d.ts +1 -0
  34. package/src/app/core/services/base-graphql-service.class.d.ts +3 -2
  35. package/src/app/core/services/config.service.d.ts +2 -9
  36. package/src/app/core/services/model/settings.model.d.ts +2 -0
  37. package/src/app/core/services/network.service.d.ts +2 -2
  38. package/src/app/core/services/storage/entities-storage.service.d.ts +0 -1
  39. package/src/app/core/settings/settings.page.d.ts +0 -1
  40. package/src/app/shared/services/startable-service.class.d.ts +3 -1
  41. package/src/app/shared/services/translate-context.service.d.ts +10 -0
  42. package/sumaris-net.ngx-components.metadata.json +1 -1
@@ -36,7 +36,7 @@ import { MatAutocomplete, MatAutocompleteTrigger, MatAutocompleteModule, MAT_AUT
36
36
  import { __awaiter, __decorate } from 'tslib';
37
37
  import * as i1$6 from '@angular/forms';
38
38
  import { NG_VALUE_ACCESSOR, FormGroupDirective, AbstractControl, FormGroup, FormArray, FormControl, ReactiveFormsModule, Validators, FormBuilder } from '@angular/forms';
39
- import { timer, merge, fromEvent, Subscription, BehaviorSubject, Subject, isObservable, of, noop as noop$8, Observable, defer, from, combineLatest, EMPTY } from 'rxjs';
39
+ import { timer, merge, fromEvent, Subscription, BehaviorSubject, Subject, isObservable, noop as noop$8, Observable, defer, of, from, combineLatest, EMPTY } from 'rxjs';
40
40
  import { filter, first, map, takeUntil, startWith, debounceTime, takeWhile, switchMap, tap, distinctUntilChanged, mergeMap, catchError, throttleTime, skip } from 'rxjs/operators';
41
41
  import * as i1$1 from '@ngx-translate/core';
42
42
  import { TranslateService, TranslateModule } from '@ngx-translate/core';
@@ -2431,23 +2431,41 @@ class TranslateContextService {
2431
2431
  }
2432
2432
  get(key, context, interpolateParams) {
2433
2433
  // No context: do a normal translate
2434
- if (!isNil(context))
2434
+ if (!key || !isNil(context))
2435
2435
  return this.translate.get(key, interpolateParams);
2436
2436
  // Compute a contextual i18n key, using the context as suffix
2437
- const contextKey = this.contextualKey(key, context);
2437
+ const contextualKeys = this.contextualKeys(key, context);
2438
2438
  // Return the contextual translation, or default of not exists
2439
- return this.translate.get(contextKey, interpolateParams)
2440
- .pipe(mergeMap(translation => (translation !== contextKey) ? of(translation) : this.translate.get(key)));
2439
+ return this.translate.get(contextualKeys, interpolateParams)
2440
+ .pipe(map(translations => this.findFirstValid(contextualKeys, translations)));
2441
2441
  }
2442
2442
  instant(key, context, interpolateParams) {
2443
2443
  // No context: do a normal translate
2444
- if (isNilOrBlank(context))
2444
+ if (!key || isNilOrBlank(context))
2445
2445
  return this.translate.instant(key, interpolateParams);
2446
2446
  // Compute a contextual i18n key, using the context as suffix
2447
- const contextualKey = this.contextualKey(key, context);
2448
- // Return the contextual translation, or default of not exists
2449
- const translation = this.translate.instant(contextualKey, interpolateParams);
2450
- return (translation !== contextualKey) ? translation : this.translate.instant(key, interpolateParams);
2447
+ const contextualKeys = this.contextualKeys(key, context);
2448
+ const translations = this.translate.instant(contextualKeys, interpolateParams);
2449
+ return this.findFirstValid(contextualKeys, translations);
2450
+ }
2451
+ /**
2452
+ * Compute contextual i18n keys, using the context as suffix.<br>
2453
+ * Suffix can be simple name (like 'aaa' - will return ['<key>.aaa']) or complexe (like 'aaa.bbb' - will return ['<key>.aaa.bbb', '<key>.aaa']
2454
+ *
2455
+ * @param key
2456
+ * @param context
2457
+ * @private
2458
+ */
2459
+ contextualKeys(key, context) {
2460
+ return context.split('.')
2461
+ .filter(isNotNilOrBlank)
2462
+ .reduce((res, item) => {
2463
+ const inheritedContext = res.length > 0 ? (res[res.length - 1] + '.') : '';
2464
+ return res.concat(inheritedContext + item);
2465
+ }, [])
2466
+ .reverse() // Start with children, ends with parent key
2467
+ .map(contextItem => this.contextualKey(key, contextItem))
2468
+ .concat(key);
2451
2469
  }
2452
2470
  /**
2453
2471
  * Compute a contextual i18n key, using the context as suffix
@@ -2467,6 +2485,14 @@ class TranslateContextService {
2467
2485
  ? `${context}${key}`
2468
2486
  : parts.slice(0, parts.length - 1).concat(context + parts[parts.length - 1]).join('.');
2469
2487
  }
2488
+ findFirstValid(keys, translations) {
2489
+ // For eac contextual key: try to find translation, then use the first valid
2490
+ for (let key of keys) {
2491
+ if (translations[key] !== key)
2492
+ return translations[key];
2493
+ }
2494
+ return translations[keys[keys.length - 1]]; // Return the last keys translation
2495
+ }
2470
2496
  }
2471
2497
  TranslateContextService.ɵprov = i0.ɵɵdefineInjectable({ factory: function TranslateContextService_Factory() { return new TranslateContextService(i0.ɵɵinject(i1$1.TranslateService)); }, token: TranslateContextService, providedIn: "root" });
2472
2498
  TranslateContextService.decorators = [
@@ -7731,6 +7757,9 @@ class StartableService {
7731
7757
  this._startByReadyFunction = true; // should start when calling ready() ?
7732
7758
  this._data = null;
7733
7759
  this._started = false;
7760
+ this._startPromise = null;
7761
+ this._startPrerequisite = null;
7762
+ this._subscription = null;
7734
7763
  this._startPrerequisite = prerequisiteService
7735
7764
  ? () => prerequisiteService.ready()
7736
7765
  : () => Promise.resolve();
@@ -7760,6 +7789,10 @@ class StartableService {
7760
7789
  stop() {
7761
7790
  return __awaiter(this, void 0, void 0, function* () {
7762
7791
  try {
7792
+ if (this._subscription) {
7793
+ this._subscription.unsubscribe();
7794
+ this._subscription = null;
7795
+ }
7763
7796
  yield this.ngOnStop();
7764
7797
  this._started = false;
7765
7798
  this._startPromise = undefined;
@@ -7794,6 +7827,10 @@ class StartableService {
7794
7827
  return waitFor(() => this._started)
7795
7828
  .then(() => this._data);
7796
7829
  }
7830
+ registerSubscription(sub) {
7831
+ this._subscription = this._subscription || new Subscription();
7832
+ return this._subscription.add(sub);
7833
+ }
7797
7834
  ngOnStop() {
7798
7835
  return __awaiter(this, void 0, void 0, function* () {
7799
7836
  // Can be override by subclasses
@@ -9997,7 +10034,6 @@ class NetworkService extends StartableService {
9997
10034
  this.onPeerChanges = this.onStart.pipe(map(peer => peer && peer.url), filter(isNotNilOrBlank), distinctUntilChanged());
9998
10035
  this.onNetworkStatusChanges = new BehaviorSubject(null);
9999
10036
  this.onResetNetworkCache = new EventEmitter(true);
10000
- this._subscription = new Subscription();
10001
10037
  this._listeners = {};
10002
10038
  this._mobile = this.platform.is('mobile');
10003
10039
  if (this._mobile) {
@@ -10158,7 +10194,8 @@ class NetworkService extends StartableService {
10158
10194
  /**
10159
10195
  * Check if the peer is alive
10160
10196
  *
10161
- * @param email
10197
+ * @param peer
10198
+ * @param opts
10162
10199
  */
10163
10200
  checkPeerAlive(peer, opts) {
10164
10201
  return __awaiter(this, void 0, void 0, function* () {
@@ -10298,8 +10335,8 @@ class NetworkService extends StartableService {
10298
10335
  this.startRefreshTimer();
10299
10336
  // Listen for device network changes
10300
10337
  if (this.network) {
10301
- this._subscription.add(this.network.onDisconnect().subscribe(() => this.onDeviceConnectionChanged('none')));
10302
- this._subscription.add(this.network.onConnect().subscribe(() => this.onDeviceConnectionChanged(this.network.type)));
10338
+ this.registerSubscription(this.network.onDisconnect().subscribe(() => this.onDeviceConnectionChanged('none')));
10339
+ this.registerSubscription(this.network.onConnect().subscribe(() => this.onDeviceConnectionChanged(this.network.type)));
10303
10340
  }
10304
10341
  });
10305
10342
  }
@@ -10310,8 +10347,6 @@ class NetworkService extends StartableService {
10310
10347
  if (this._timerRefreshCondition() === false) {
10311
10348
  this.stopRefreshTimer();
10312
10349
  }
10313
- this._subscription.unsubscribe();
10314
- this._subscription = new Subscription();
10315
10350
  });
10316
10351
  }
10317
10352
  /**
@@ -11006,7 +11041,6 @@ class EntitiesStorage extends StartableService {
11006
11041
  this.progressBarService = progressBarService;
11007
11042
  this.storage = storage;
11008
11043
  this.environment = environment;
11009
- this._subscription = new Subscription();
11010
11044
  this._$save = new EventEmitter(true);
11011
11045
  this._dirty = false;
11012
11046
  this._saving = false;
@@ -11299,7 +11333,7 @@ class EntitiesStorage extends StartableService {
11299
11333
  // Restore sequences
11300
11334
  yield this.restoreLocally();
11301
11335
  // Start a save timer
11302
- this._subscription.add(merge(this._$save, timer(this._saveTimerPeriod, this._saveTimerPeriod))
11336
+ this.registerSubscription(merge(this._$save, timer(this._saveTimerPeriod, this._saveTimerPeriod))
11303
11337
  .pipe(
11304
11338
  // Avoid to many call (e.g. when $save AND timer are triggered
11305
11339
  throttleTime(this._saveTimerPeriod))
@@ -11310,8 +11344,6 @@ class EntitiesStorage extends StartableService {
11310
11344
  }
11311
11345
  ngOnStop() {
11312
11346
  return __awaiter(this, void 0, void 0, function* () {
11313
- this._subscription.unsubscribe();
11314
- this._subscription = new Subscription();
11315
11347
  if (this.dirty) {
11316
11348
  yield this.storeLocally();
11317
11349
  }
@@ -11493,7 +11525,34 @@ let UserSettings = UserSettings_1 = class UserSettings extends Entity {
11493
11525
  }
11494
11526
  this.nonce = source.nonce;
11495
11527
  }
11528
+ merge(source, includesProperties) {
11529
+ if (!source)
11530
+ return; // Skip
11531
+ // Reset content
11532
+ this.content = {};
11533
+ let hasChanges = false;
11534
+ // For each property of source object
11535
+ Object.keys(source)
11536
+ .forEach(key => {
11537
+ // If property exists in UserSettings class: store it directly
11538
+ if (UserSettings_1.SETTINGS_PROPERTIES.includes(key)) {
11539
+ if (!equals(this[key], source[key])) {
11540
+ this[key] = source[key];
11541
+ hasChanges = true;
11542
+ }
11543
+ }
11544
+ // Else, store property into the 'content' map
11545
+ else if (includesProperties && includesProperties.includes(key)) {
11546
+ if (!equals(this.content[key], source[key])) {
11547
+ this.content[key] = Object.assign({}, source[key]);
11548
+ hasChanges = true;
11549
+ }
11550
+ }
11551
+ });
11552
+ return hasChanges;
11553
+ }
11496
11554
  };
11555
+ UserSettings.SETTINGS_PROPERTIES = ['locale', 'latLongFormat'];
11497
11556
  UserSettings = UserSettings_1 = __decorate([
11498
11557
  EntityClass({ typename: 'UserSettingsVO' })
11499
11558
  ], UserSettings);
@@ -11906,7 +11965,6 @@ class GraphqlService extends StartableService {
11906
11965
  this.cryptoService = cryptoService;
11907
11966
  this.environment = environment;
11908
11967
  this.typePolicies = typePolicies;
11909
- this._subscription = new Subscription();
11910
11968
  this.connectionParams = {};
11911
11969
  this.onNetworkError = new Subject();
11912
11970
  this.customErrors = {};
@@ -12406,7 +12464,7 @@ class GraphqlService extends StartableService {
12406
12464
  });
12407
12465
  // Creating a mutation queue
12408
12466
  const queueLink = new QueueLink();
12409
- this._subscription.add(this._networkStatusChanged$
12467
+ this.registerSubscription(this._networkStatusChanged$
12410
12468
  .subscribe(type => {
12411
12469
  // Network is offline: start buffering into queue
12412
12470
  if (type === 'none') {
@@ -12469,9 +12527,9 @@ class GraphqlService extends StartableService {
12469
12527
  }
12470
12528
  }
12471
12529
  // Listen for network restart
12472
- this._subscription.add(this.network.on('start', () => this.restart()));
12530
+ this.registerSubscription(this.network.on('start', () => this.restart()));
12473
12531
  // Listen for clear cache request, from network
12474
- this._subscription.add(this.network.on('resetCache', () => this.clearCache()));
12532
+ this.registerSubscription(this.network.on('resetCache', () => this.clearCache()));
12475
12533
  console.info('[graphql] Starting graphql [OK]');
12476
12534
  return client;
12477
12535
  });
@@ -12479,8 +12537,6 @@ class GraphqlService extends StartableService {
12479
12537
  ngOnStop() {
12480
12538
  return __awaiter(this, void 0, void 0, function* () {
12481
12539
  console.info('[graphql] Stopping graphql service...');
12482
- this._subscription.unsubscribe();
12483
- this._subscription = new Subscription();
12484
12540
  yield this.resetClient();
12485
12541
  });
12486
12542
  }
@@ -12599,8 +12655,9 @@ const sha256 = require('hash.js/lib/hash/sha/256');
12599
12655
  class BaseGraphqlServiceOptions {
12600
12656
  }
12601
12657
  // eslint-disable-next-line @angular-eslint/directive-class-suffix
12602
- class BaseGraphqlService {
12658
+ class BaseGraphqlService extends StartableService {
12603
12659
  constructor(graphql, options) {
12660
+ super(graphql);
12604
12661
  this.graphql = graphql;
12605
12662
  this._mutableWatchQueries = [];
12606
12663
  // Max updated queries, for this entity.
@@ -12792,6 +12849,9 @@ class BaseGraphqlService {
12792
12849
  return { query, variables };
12793
12850
  });
12794
12851
  }
12852
+ ngOnStart() {
12853
+ return Promise.resolve(null);
12854
+ }
12795
12855
  }
12796
12856
  BaseGraphqlService.decorators = [
12797
12857
  { type: Directive }
@@ -12925,18 +12985,16 @@ class AccountService extends BaseGraphqlService {
12925
12985
  this.onChange = new Subject();
12926
12986
  this.onAuthTokenChange = new Subject();
12927
12987
  this.onAuthBasicChange = new Subject();
12928
- this._data = {
12988
+ this._cache = {
12929
12989
  loaded: false,
12930
12990
  keypair: null,
12931
12991
  authToken: null,
12932
12992
  authBasic: null,
12933
12993
  pubkey: null,
12934
12994
  mainProfile: null,
12935
- account: null,
12936
12995
  person: null,
12937
12996
  department: null
12938
12997
  };
12939
- this._started = false;
12940
12998
  this._$additionalFields = new BehaviorSubject([]);
12941
12999
  this._tokenType$ = new BehaviorSubject(undefined);
12942
13000
  this._debug = !environment.production;
@@ -12946,41 +13004,31 @@ class AccountService extends BaseGraphqlService {
12946
13004
  // Send auth token to the graphql layer, when changed
12947
13005
  this.onAuthTokenChange.subscribe((token) => this.graphql.setAuthToken(token));
12948
13006
  this.onAuthBasicChange.subscribe((basic) => this.graphql.setAuthBasic(basic));
12949
- // Listen graphql start (or restart)
12950
- this.graphql.onStart.subscribe(() => __awaiter(this, void 0, void 0, function* () {
12951
- if (!this._started) {
12952
- this.ready();
12953
- }
12954
- if (this._started && this.isLogin()) {
12955
- console.debug('[account] Restarting, to retry to authenticate...');
12956
- this.restart();
12957
- }
12958
- }));
12959
13007
  // Force network to wait account service, after getting connection to the peer
12960
13008
  this.network.on('beforeTryOnlineFinish', (online) => __awaiter(this, void 0, void 0, function* () {
12961
13009
  // If online, wait a full restart, because it can force offline mode
12962
- if (online && (!this._started || this.isLogin())) {
12963
- console.debug('[account] Force networkService.tryOnline() to wait, that account service is restarted...');
12964
- //await this.stop();
12965
- yield sleep(500);
12966
- return this.ready();
13010
+ if (online && (!this.started || this.isLogin())) {
13011
+ console.debug('[account] Force networkService.tryOnline() to wait, that graphql and account service is restarted...');
13012
+ yield sleep(500); // wait graphql service to be restarted
13013
+ if (!this.started)
13014
+ yield this.ready();
12967
13015
  }
12968
13016
  }));
12969
13017
  }
12970
13018
  get account() {
12971
- return this._data.loaded ? this._data.account : undefined;
13019
+ return this._cache.loaded ? this._data : undefined;
12972
13020
  }
12973
13021
  get person() {
12974
- if (this._data.loaded && !this._data.person) {
12975
- this._data.person = this._data.loaded ? this._data.account.asPerson() : undefined;
13022
+ if (this._cache.loaded && !this._cache.person) {
13023
+ this._cache.person = this._cache.loaded ? this._data.asPerson() : undefined;
12976
13024
  }
12977
- return this._data.person;
13025
+ return this._cache.person;
12978
13026
  }
12979
13027
  get department() {
12980
- if (this._data.loaded && !this._data.department) {
12981
- this._data.department = this._data.loaded ? this._data.account.asPerson().department : undefined;
13028
+ if (this._cache.loaded && !this._cache.department) {
13029
+ this._cache.department = this._cache.loaded ? this._data.asPerson().department : undefined;
12982
13030
  }
12983
- return this._data.department;
13031
+ return this._cache.department;
12984
13032
  }
12985
13033
  get tokenType() {
12986
13034
  return this._tokenType$.value;
@@ -12990,91 +13038,73 @@ class AccountService extends BaseGraphqlService {
12990
13038
  console.info('[account] Using authentication token type: ' + value);
12991
13039
  this._tokenType$.next(value);
12992
13040
  // Reset values
12993
- this._data.authToken = undefined;
13041
+ this._cache.authToken = undefined;
12994
13042
  this.onAuthTokenChange.next(undefined);
12995
- this._data.authBasic = undefined;
13043
+ this._cache.authBasic = undefined;
12996
13044
  this.onAuthBasicChange.next(undefined);
12997
13045
  }
12998
13046
  }
12999
- start() {
13000
- if (this._startPromise)
13001
- return this._startPromise;
13002
- if (this._started)
13003
- return Promise.resolve(this.account);
13004
- // Restoring local settings
13005
- this._startPromise = Promise.all([
13006
- this.settings.ready(),
13007
- // Wait token type to be set
13008
- firstNotNilPromise(this._tokenType$)
13009
- ])
13010
- .then(() => this.restoreLocally())
13011
- .then(() => {
13012
- this._started = true;
13013
- this._startPromise = undefined;
13014
- return this.account;
13015
- });
13016
- return this._startPromise;
13017
- }
13018
- get started() {
13019
- return this._started;
13020
- }
13021
- stop() {
13047
+ ngOnStart() {
13022
13048
  return __awaiter(this, void 0, void 0, function* () {
13023
- if (this._started || this._startPromise) {
13024
- this._started = false;
13025
- this._startPromise = undefined;
13026
- const hadAuthToken = this._data.authToken && true;
13027
- const hadAuthBasic = this._data.authBasic && true;
13028
- const hadAuth = hadAuthToken || hadAuthBasic;
13029
- this.resetData();
13030
- if (hadAuth) {
13031
- this.onLogout.next();
13032
- this.onChange.next(undefined);
13033
- if (hadAuthToken)
13034
- this.onAuthTokenChange.next(undefined);
13035
- if (hadAuthBasic)
13036
- this.onAuthBasicChange.next(undefined);
13049
+ yield Promise.all([
13050
+ this.settings.ready(),
13051
+ // Wait token type to be set
13052
+ firstNotNilPromise(this._tokenType$)
13053
+ ]);
13054
+ // Listen graphql start (or restart)
13055
+ this.registerSubscription(this.graphql.onStart.subscribe(() => {
13056
+ if (this.started && this.isLogin()) {
13057
+ console.debug('[account] Restarting, to retry to authenticate...');
13058
+ this.restart();
13037
13059
  }
13038
- }
13060
+ }));
13061
+ yield this.restoreLocally();
13062
+ yield this.listenSettings();
13063
+ return this._data;
13039
13064
  });
13040
13065
  }
13041
- restart() {
13066
+ ngOnStop() {
13042
13067
  return __awaiter(this, void 0, void 0, function* () {
13043
- yield this.stop();
13044
- return this.start();
13068
+ const hadAuthToken = this._cache.authToken && true;
13069
+ const hadAuthBasic = this._cache.authBasic && true;
13070
+ const hadAuth = hadAuthToken || hadAuthBasic;
13071
+ this.resetData();
13072
+ if (hadAuth) {
13073
+ this.onLogout.next();
13074
+ this.onChange.next(undefined);
13075
+ if (hadAuthToken)
13076
+ this.onAuthTokenChange.next(undefined);
13077
+ if (hadAuthBasic)
13078
+ this.onAuthBasicChange.next(undefined);
13079
+ }
13045
13080
  });
13046
13081
  }
13047
- ready() {
13048
- if (this._started)
13049
- return Promise.resolve(this.account);
13050
- return this.start();
13051
- }
13052
13082
  isLogin() {
13053
- return !!(this._data.pubkey && this._data.loaded);
13083
+ return !!(this._cache.pubkey && this._cache.loaded);
13054
13084
  }
13055
13085
  isAuth() {
13056
- return !!(this._data.pubkey && this._data.keypair && this._data.keypair.secretKey);
13086
+ return !!(this._cache.pubkey && this._cache.keypair && this._cache.keypair.secretKey);
13057
13087
  }
13058
13088
  hasMinProfile(userProfile) {
13059
13089
  // should be login, and status ENABLE or TEMPORARY
13060
- if (!this._data.account || !this._data.account.pubkey ||
13061
- (this._data.account.statusId !== StatusIds.ENABLE && this._data.account.statusId !== StatusIds.TEMPORARY)) {
13090
+ if (!this._data || !this._data.pubkey ||
13091
+ (this._data.statusId !== StatusIds.ENABLE && this._data.statusId !== StatusIds.TEMPORARY)) {
13062
13092
  return false;
13063
13093
  }
13064
- return PersonUtils.hasUpperOrEqualsProfile(this._data.account.profiles, userProfile);
13094
+ return PersonUtils.hasUpperOrEqualsProfile(this._data.profiles, userProfile);
13065
13095
  }
13066
13096
  hasExactProfile(label) {
13067
13097
  // should be login, and status ENABLE or TEMPORARY
13068
- if (!this._data.account || !this._data.account.pubkey ||
13069
- (this._data.account.statusId !== StatusIds.ENABLE && this._data.account.statusId !== StatusIds.TEMPORARY))
13098
+ if (!this._data || !this._data.pubkey ||
13099
+ (this._data.statusId !== StatusIds.ENABLE && this._data.statusId !== StatusIds.TEMPORARY))
13070
13100
  return false;
13071
- return this._data.account.profiles.some(profile => profile === label);
13101
+ return this._data.profiles.some(profile => profile === label);
13072
13102
  }
13073
13103
  hasProfileAndIsEnable(userProfile) {
13074
13104
  // should be login, and status ENABLE
13075
- if (!this._data.account || !this._data.account.pubkey || this._data.account.statusId !== StatusIds.ENABLE)
13105
+ if (!this._data || !this._data.pubkey || this._data.statusId !== StatusIds.ENABLE)
13076
13106
  return false;
13077
- return PersonUtils.hasUpperOrEqualsProfile(this._data.account.profiles, userProfile);
13107
+ return PersonUtils.hasUpperOrEqualsProfile(this._data.profiles, userProfile);
13078
13108
  }
13079
13109
  isAdmin() {
13080
13110
  return this.hasProfileAndIsEnable('ADMIN');
@@ -13094,11 +13124,11 @@ class AccountService extends BaseGraphqlService {
13094
13124
  }
13095
13125
  isOnlyGuest() {
13096
13126
  // Should be login, and status ENABLE or TEMPORARY
13097
- if (!this._data.account || !this._data.account.pubkey ||
13098
- (this._data.account.statusId !== StatusIds.ENABLE && this._data.account.statusId !== StatusIds.TEMPORARY))
13127
+ if (!this._data || !this._data.pubkey ||
13128
+ (this._data.statusId !== StatusIds.ENABLE && this._data.statusId !== StatusIds.TEMPORARY))
13099
13129
  return false;
13100
13130
  // Profile less then user
13101
- return !PersonUtils.hasUpperOrEqualsProfile(this._data.account.profiles, 'USER');
13131
+ return !PersonUtils.hasUpperOrEqualsProfile(this._data.profiles, 'USER');
13102
13132
  }
13103
13133
  canUserWriteDataForDepartment(recorderDepartment) {
13104
13134
  if (ReferentialUtils.isEmpty(recorderDepartment)) {
@@ -13107,17 +13137,17 @@ class AccountService extends BaseGraphqlService {
13107
13137
  return this.isAdmin();
13108
13138
  }
13109
13139
  // Should be login, and status ENABLE
13110
- if (!this._data.account || !this._data.account.pubkey || this._data.account.statusId !== StatusIds.ENABLE)
13140
+ if (!this._data || !this._data.pubkey || this._data.statusId !== StatusIds.ENABLE)
13111
13141
  return false;
13112
- if (!this._data.account.department || !this._data.account.department.id) {
13142
+ if (!this._data.department || !this._data.department.id) {
13113
13143
  console.warn('User account has no department ! Unable to check write right against recorderDepartment');
13114
13144
  return false;
13115
13145
  }
13116
13146
  // Same recorder department: OK, user can write
13117
- if (this._data.account.department.id === recorderDepartment.id)
13147
+ if (this._data.department.id === recorderDepartment.id)
13118
13148
  return true;
13119
13149
  // Else, check if supervisor (or more)
13120
- return PersonUtils.hasUpperOrEqualsProfile(this._data.account.profiles, 'SUPERVISOR');
13150
+ return PersonUtils.hasUpperOrEqualsProfile(this._data.profiles, 'SUPERVISOR');
13121
13151
  }
13122
13152
  register(data) {
13123
13153
  return __awaiter(this, void 0, void 0, function* () {
@@ -13128,7 +13158,7 @@ class AccountService extends BaseGraphqlService {
13128
13158
  throw new Error('Missing required username or password');
13129
13159
  if (this._debug)
13130
13160
  console.debug('[account] Register new user account...', data.account);
13131
- this._data.loaded = false;
13161
+ this._cache.loaded = false;
13132
13162
  const now = Date.now();
13133
13163
  try {
13134
13164
  const keypair = yield this.cryptoService.scryptKeypair(data.username, data.password);
@@ -13138,22 +13168,22 @@ class AccountService extends BaseGraphqlService {
13138
13168
  data.account.settings.locale = this.settings.locale;
13139
13169
  data.account.settings.latLongFormat = this.settings.latLongFormat;
13140
13170
  data.account.department.id = data.account.department.id || this.environment.defaultDepartmentId;
13141
- this._data.keypair = keypair;
13171
+ this._cache.keypair = keypair;
13142
13172
  const account = yield this.saveRemotely(data.account, keypair);
13143
13173
  // Default values
13144
13174
  account.avatar = account.avatar || (this.environment.baseUrl + DEFAULT_AVATAR_IMAGE);
13145
- this._data.mainProfile = PersonUtils.getMainProfile(account.profiles);
13146
- this._data.account = account;
13147
- this._data.pubkey = account.pubkey;
13175
+ this._cache.mainProfile = PersonUtils.getMainProfile(account.profiles);
13176
+ this._data = account;
13177
+ this._cache.pubkey = account.pubkey;
13148
13178
  // Try to auth on pod
13149
13179
  yield this.authenticate(data);
13150
- this._data.loaded = true;
13180
+ this._cache.loaded = true;
13151
13181
  yield this.saveLocally();
13152
13182
  console.debug(`[account] Account successfully registered in ${Date.now() - now}ms`);
13153
13183
  // Emit events
13154
- this.onLogin.next(this._data.account);
13155
- this.onChange.next(this._data.account);
13156
- return this._data.account;
13184
+ this.onLogin.next(this._data);
13185
+ this.onChange.next(this._data);
13186
+ return this._data;
13157
13187
  }
13158
13188
  catch (error) {
13159
13189
  console.error(error && error.message || error);
@@ -13169,20 +13199,20 @@ class AccountService extends BaseGraphqlService {
13169
13199
  // Basic auth
13170
13200
  if (tokenType === 'basic' || tokenType === 'basic-and-token') {
13171
13201
  // Generate the authBasic, if used
13172
- if (!this._data.authBasic) {
13202
+ if (!this._cache.authBasic) {
13173
13203
  // Skip if token already provided
13174
- if (!(this._data.authToken && tokenType === 'basic-and-token')) {
13204
+ if (!(this._cache.authToken && tokenType === 'basic-and-token')) {
13175
13205
  if (!data || !data.username || !data.password)
13176
13206
  throw new Error('Missing username and password');
13177
- this._data.authBasic = this.cryptoService.encodeBase64(`${data.username}:${data.password}`);
13207
+ this._cache.authBasic = this.cryptoService.encodeBase64(`${data.username}:${data.password}`);
13178
13208
  }
13179
13209
  }
13180
- this.onAuthBasicChange.next(this._data.authBasic);
13210
+ this.onAuthBasicChange.next(this._cache.authBasic);
13181
13211
  }
13182
13212
  // Generate the authToken, if used
13183
13213
  if (tokenType === 'token' || tokenType === 'basic-and-token') {
13184
13214
  try {
13185
- this._data.authToken = yield this.authenticateAndGetToken(this._data.authToken);
13215
+ this._cache.authToken = yield this.authenticateAndGetToken(this._cache.authToken);
13186
13216
  }
13187
13217
  catch (error) {
13188
13218
  // Never authenticate, or not ready for offline mode => exit
@@ -13193,7 +13223,7 @@ class AccountService extends BaseGraphqlService {
13193
13223
  }
13194
13224
  // Forget authBasic, to switch to authToken
13195
13225
  if (tokenType === 'basic-and-token') {
13196
- this._data.authBasic = undefined;
13226
+ this._cache.authBasic = undefined;
13197
13227
  this.onAuthBasicChange.next(undefined);
13198
13228
  }
13199
13229
  });
@@ -13213,18 +13243,18 @@ class AccountService extends BaseGraphqlService {
13213
13243
  throw { code: ErrorCodes$2.UNKNOWN_ERROR, message: 'ERROR.SCRYPT_ERROR' };
13214
13244
  }
13215
13245
  // Store pubkey+keypair
13216
- this._data.pubkey = Base58.encode(keypair.publicKey);
13217
- this._data.keypair = keypair;
13246
+ this._cache.pubkey = Base58.encode(keypair.publicKey);
13247
+ this._cache.keypair = keypair;
13218
13248
  // Try to load previous token
13219
13249
  let previousToken = yield this.storage.get(TOKEN_STORAGE_KEY);
13220
- previousToken = previousToken && previousToken.startsWith(this._data.pubkey) && previousToken || null;
13250
+ previousToken = previousToken && previousToken.startsWith(this._cache.pubkey) && previousToken || null;
13221
13251
  // Offline mode
13222
13252
  const offline = this.settings.hasOfflineFeature() && (this.network.offline || data.offline === true);
13223
13253
  if (offline) {
13224
- this._data.authToken = previousToken;
13254
+ this._cache.authToken = previousToken;
13225
13255
  // Make sure network if set as offline
13226
13256
  this.network.setForceOffline(true, { showToast: false });
13227
- console.info(`[account] Login [OK] {pubkey: ${this._data.pubkey.substr(0, 8)}}, {offline: true}`);
13257
+ console.info(`[account] Login [OK] {pubkey: ${this._cache.pubkey.substr(0, 8)}}, {offline: true}`);
13228
13258
  }
13229
13259
  // Online mode: try to auth on pod
13230
13260
  else {
@@ -13274,14 +13304,14 @@ class AccountService extends BaseGraphqlService {
13274
13304
  throw error;
13275
13305
  }
13276
13306
  // Emit event to observers
13277
- this.onLogin.next(this._data.account);
13278
- this.onChange.next(this._data.account);
13279
- return this._data.account;
13307
+ this.onLogin.next(this._data);
13308
+ this.onChange.next(this._data);
13309
+ return this._data;
13280
13310
  });
13281
13311
  }
13282
13312
  refresh() {
13283
13313
  return __awaiter(this, void 0, void 0, function* () {
13284
- if (!this._data.pubkey)
13314
+ if (!this._cache.pubkey)
13285
13315
  throw new Error('User not logged');
13286
13316
  if (this.network.offline)
13287
13317
  throw new Error('Cannot check account in offline mode');
@@ -13289,9 +13319,9 @@ class AccountService extends BaseGraphqlService {
13289
13319
  yield this.saveLocally();
13290
13320
  console.debug('[account] Successfully reload account');
13291
13321
  // Emit login event to subscribers
13292
- this.onLogin.next(this._data.account);
13293
- this.onChange.next(this._data.account);
13294
- return this._data.account;
13322
+ this.onLogin.next(this._data);
13323
+ this.onChange.next(this._data);
13324
+ return this._data;
13295
13325
  });
13296
13326
  }
13297
13327
  /**
@@ -13301,29 +13331,29 @@ class AccountService extends BaseGraphqlService {
13301
13331
  */
13302
13332
  save(account) {
13303
13333
  return __awaiter(this, void 0, void 0, function* () {
13304
- if (!this._data.pubkey)
13334
+ if (!this._cache.pubkey)
13305
13335
  return Promise.reject('User not logged');
13306
- if (this._data.pubkey !== account.pubkey)
13336
+ if (this._cache.pubkey !== account.pubkey)
13307
13337
  return Promise.reject('Not user account');
13308
- account = yield this.saveRemotely(account, this._data.keypair);
13338
+ account = yield this.saveRemotely(account, this._cache.keypair);
13309
13339
  // Set defaults
13310
13340
  account.avatar = account.avatar || (this.environment.baseUrl + DEFAULT_AVATAR_IMAGE);
13311
- this._data.mainProfile = PersonUtils.getMainProfile(account.profiles);
13312
- this._data.account = account;
13313
- this._data.loaded = true;
13341
+ this._cache.mainProfile = PersonUtils.getMainProfile(account.profiles);
13342
+ this._data = account;
13343
+ this._cache.loaded = true;
13314
13344
  // Save locally (in storage)
13315
13345
  yield this.saveLocally();
13316
13346
  // Send event
13317
- this.onLogin.next(this._data.account);
13318
- this.onChange.next(this._data.account);
13319
- return this._data.account;
13347
+ this.onLogin.next(this._data);
13348
+ this.onChange.next(this._data);
13349
+ return this._data;
13320
13350
  });
13321
13351
  }
13322
13352
  logout() {
13323
13353
  return __awaiter(this, void 0, void 0, function* () {
13324
- const hadAuthToken = this._data.authToken && true;
13325
- const hadAuthBasic = this._data.authBasic && true;
13326
- const pubkey = this._data && this._data.pubkey;
13354
+ const hadAuthToken = this._cache.authToken && true;
13355
+ const hadAuthBasic = this._cache.authBasic && true;
13356
+ const pubkey = this._cache && this._cache.pubkey;
13327
13357
  this.resetData();
13328
13358
  if (!this.settings.hasOfflineFeature()) {
13329
13359
  // Remove all data from the local storage
@@ -13376,9 +13406,9 @@ class AccountService extends BaseGraphqlService {
13376
13406
  if (offline) {
13377
13407
  json = yield this.storage.get(ACCOUNT_STORAGE_KEY);
13378
13408
  json = json && (typeof json === 'string') && JSON.parse(json) || json;
13379
- json = json && this._data.pubkey && (json.pubkey === this._data.pubkey) && json || null;
13380
- if (!json && this._data.pubkey) {
13381
- json = yield this.storage.get(ACCOUNT_STORAGE_KEY + '#' + this._data.pubkey);
13409
+ json = json && this._cache.pubkey && (json.pubkey === this._cache.pubkey) && json || null;
13410
+ if (!json && this._cache.pubkey) {
13411
+ json = yield this.storage.get(ACCOUNT_STORAGE_KEY + '#' + this._cache.pubkey);
13382
13412
  json = json && (typeof json === 'string') && JSON.parse(json) || json;
13383
13413
  }
13384
13414
  }
@@ -13473,7 +13503,7 @@ class AccountService extends BaseGraphqlService {
13473
13503
  });
13474
13504
  }
13475
13505
  listenChanges() {
13476
- if (!this._data.pubkey)
13506
+ if (!this._cache.pubkey)
13477
13507
  return Subscription.EMPTY;
13478
13508
  const self = this;
13479
13509
  console.debug('[account] [WS] Listening account changes');
@@ -13491,7 +13521,7 @@ class AccountService extends BaseGraphqlService {
13491
13521
  var _a;
13492
13522
  if (!data)
13493
13523
  return;
13494
- const existingUpdateDate = toDateISOString((_a = self._data.account) === null || _a === void 0 ? void 0 : _a.updateDate);
13524
+ const existingUpdateDate = toDateISOString((_a = self._data) === null || _a === void 0 ? void 0 : _a.updateDate);
13495
13525
  if (existingUpdateDate !== data.updateDate) {
13496
13526
  console.debug(`[account] [WS] Detected update on {${data.updateDate}}`);
13497
13527
  yield self.refresh();
@@ -13518,20 +13548,6 @@ class AccountService extends BaseGraphqlService {
13518
13548
  subscription.add(() => console.debug('[account] [WS] Stop listening account changes'));
13519
13549
  return subscription;
13520
13550
  }
13521
- /**
13522
- * @deprecated
13523
- */
13524
- getPageSettings(pageId, propertyName) {
13525
- return this.settings.getPageSettings(pageId, propertyName);
13526
- }
13527
- /**
13528
- * @deprecated
13529
- */
13530
- savePageSetting(pageId, value, propertyName) {
13531
- return __awaiter(this, void 0, void 0, function* () {
13532
- yield this.settings.savePageSetting(pageId, value, propertyName);
13533
- });
13534
- }
13535
13551
  get additionalFields() {
13536
13552
  return this._$additionalFields.getValue();
13537
13553
  }
@@ -13552,21 +13568,21 @@ class AccountService extends BaseGraphqlService {
13552
13568
  }
13553
13569
  /* -- protected method -- */
13554
13570
  resetData() {
13555
- this._data.loaded = false;
13556
- this._data.keypair = null;
13557
- this._data.authToken = null;
13558
- this._data.authBasic = null;
13559
- this._data.pubkey = null;
13560
- this._data.mainProfile = null;
13561
- this._data.account = new Account();
13562
- this._data.person = null;
13563
- this._data.department = null;
13571
+ this._cache.loaded = false;
13572
+ this._cache.keypair = null;
13573
+ this._cache.authToken = null;
13574
+ this._cache.authBasic = null;
13575
+ this._cache.pubkey = null;
13576
+ this._cache.mainProfile = null;
13577
+ this._cache.person = null;
13578
+ this._cache.department = null;
13579
+ this._data = new Account();
13564
13580
  }
13565
13581
  loadData(opts) {
13566
13582
  return __awaiter(this, void 0, void 0, function* () {
13567
- if (!this._data.pubkey)
13583
+ if (!this._cache.pubkey)
13568
13584
  throw new Error('User not logged');
13569
- this._data.loaded = false;
13585
+ this._cache.loaded = false;
13570
13586
  try {
13571
13587
  const account = (yield this.load(opts)) || new Account();
13572
13588
  // Set defaults
@@ -13574,17 +13590,25 @@ class AccountService extends BaseGraphqlService {
13574
13590
  account.settings = account.settings || new UserSettings();
13575
13591
  account.settings.locale = account.settings.locale || this.settings.locale;
13576
13592
  account.settings.latLongFormat = account.settings.latLongFormat || this.settings.latLongFormat || 'DDMM';
13593
+ account.settings.content = account.settings.content || {};
13577
13594
  // Read main profile
13578
- this._data.mainProfile = PersonUtils.getMainProfile(account.profiles);
13595
+ this._cache.mainProfile = PersonUtils.getMainProfile(account.profiles);
13579
13596
  // Update, instead of replace it
13580
- if (this._data.account) {
13581
- this._data.account.fromObject(account);
13597
+ if (this._data) {
13598
+ this._data.fromObject(account);
13582
13599
  }
13583
13600
  else {
13584
- this._data.account = account;
13601
+ this._data = account;
13602
+ }
13603
+ this._cache.loaded = true;
13604
+ // Apply settings properties, found in remote settings
13605
+ const accountSettingsContentKeys = Object.keys(account.settings.content);
13606
+ if (isNotEmptyArray(accountSettingsContentKeys)) {
13607
+ const settings = Object.assign(Object.assign(Object.assign({}, this.settings.settings), account.settings.content), account.settings);
13608
+ delete settings.content; // Remove content attribute
13609
+ yield this.settings.apply(settings);
13585
13610
  }
13586
- this._data.loaded = true;
13587
- return this._data.account;
13611
+ return this._data;
13588
13612
  }
13589
13613
  catch (error) {
13590
13614
  this.resetData();
@@ -13598,6 +13622,14 @@ class AccountService extends BaseGraphqlService {
13598
13622
  }
13599
13623
  });
13600
13624
  }
13625
+ listenSettings() {
13626
+ return __awaiter(this, void 0, void 0, function* () {
13627
+ // When settings changed: save it remotely
13628
+ this.registerSubscription(this.settings.onChange
13629
+ .pipe(debounceTime(1000), filter(() => this.isLogin() && this.network.online))
13630
+ .subscribe(settings => this.saveLocalSettingsRemotely(settings)));
13631
+ });
13632
+ }
13601
13633
  restoreLocally() {
13602
13634
  return __awaiter(this, void 0, void 0, function* () {
13603
13635
  // Restore from storage
@@ -13618,9 +13650,9 @@ class AccountService extends BaseGraphqlService {
13618
13650
  return;
13619
13651
  if (this._debug)
13620
13652
  console.debug(`[account] Account restoration...`);
13621
- this._data.authToken = token;
13622
- this._data.pubkey = pubkey;
13623
- this._data.keypair = seckey && {
13653
+ this._cache.authToken = token;
13654
+ this._cache.pubkey = pubkey;
13655
+ this._cache.keypair = seckey && {
13624
13656
  publicKey: Base58.decode(pubkey),
13625
13657
  secretKey: Base58.decode(seckey)
13626
13658
  } || null;
@@ -13628,7 +13660,7 @@ class AccountService extends BaseGraphqlService {
13628
13660
  if (this.network.online) {
13629
13661
  try {
13630
13662
  yield this.authenticate();
13631
- if (!this._data.authToken && !this._data.authBasic)
13663
+ if (!this._cache.authToken && !this._cache.authBasic)
13632
13664
  throw new Error('Authentication failed');
13633
13665
  }
13634
13666
  catch (error) {
@@ -13659,14 +13691,14 @@ class AccountService extends BaseGraphqlService {
13659
13691
  // Transform to entity
13660
13692
  const account = Account.fromObject(jsonAccount);
13661
13693
  // Update data
13662
- this._data.account = account;
13663
- this._data.mainProfile = PersonUtils.getMainProfile(account.profiles);
13664
- this._data.loaded = true;
13694
+ this._data = account;
13695
+ this._cache.mainProfile = PersonUtils.getMainProfile(account.profiles);
13696
+ this._cache.loaded = true;
13665
13697
  // Emit event
13666
- this.onLogin.next(this._data.account);
13667
- this.onChange.next(this._data.account);
13698
+ this.onLogin.next(this._data);
13699
+ this.onChange.next(this._data);
13668
13700
  if (this._debug)
13669
- console.debug(`[account] Account restoration [OK] {pubkey: ${pubkey.substr(0, 8)}}, {profile: ${this._data.mainProfile}}`);
13701
+ console.debug(`[account] Account restoration [OK] {pubkey: ${pubkey.substr(0, 8)}}, {profile: ${this._cache.mainProfile}}`);
13670
13702
  return account;
13671
13703
  });
13672
13704
  }
@@ -13675,13 +13707,13 @@ class AccountService extends BaseGraphqlService {
13675
13707
  */
13676
13708
  saveLocally() {
13677
13709
  return __awaiter(this, void 0, void 0, function* () {
13678
- if (!this._data.pubkey)
13710
+ if (!this._cache.pubkey)
13679
13711
  throw new Error('User not logged');
13680
13712
  if (this._debug)
13681
- console.debug(`[account] Saving account {${this._data.pubkey.substring(0, 6)}} in local storage...`);
13713
+ console.debug(`[account] Saving account {${this._cache.pubkey.substring(0, 6)}} in local storage...`);
13682
13714
  // Convert account to json
13683
- const json = this._data.account.asObject({ keepTypename: true });
13684
- const seckey = this._data.keypair && this._data.keypair.secretKey && Base58.encode(this._data.keypair.secretKey) || null;
13715
+ const json = this._data.asObject({ keepTypename: true });
13716
+ const seckey = this._cache.keypair && this._cache.keypair.secretKey && Base58.encode(this._cache.keypair.secretKey) || null;
13685
13717
  // Convert avatar URL to dataUrl (e.g. 'data:image/png:<base64 content>')
13686
13718
  const hasAvatarUrl = json.avatar && !json.avatar.endsWith(DEFAULT_AVATAR_IMAGE) &&
13687
13719
  (json.avatar.startsWith('http://') || (json.avatar.startsWith('https://')));
@@ -13702,9 +13734,9 @@ class AccountService extends BaseGraphqlService {
13702
13734
  }
13703
13735
  try {
13704
13736
  yield Promise.all([
13705
- this.storage.set(PUBKEY_STORAGE_KEY, this._data.pubkey),
13706
- this.storage.set(TOKEN_STORAGE_KEY, this._data.authToken),
13707
- this.storage.set(`${ACCOUNT_STORAGE_KEY}#${this._data.pubkey}`, json),
13737
+ this.storage.set(PUBKEY_STORAGE_KEY, this._cache.pubkey),
13738
+ this.storage.set(TOKEN_STORAGE_KEY, this._cache.authToken),
13739
+ this.storage.set(`${ACCOUNT_STORAGE_KEY}#${this._cache.pubkey}`, json),
13708
13740
  // Secret key (optional)
13709
13741
  seckey && this.storage.set(SECKEY_STORAGE_KEY, seckey) || this.storage.remove(SECKEY_STORAGE_KEY),
13710
13742
  // Remove old storage key
@@ -13718,6 +13750,22 @@ class AccountService extends BaseGraphqlService {
13718
13750
  }
13719
13751
  });
13720
13752
  }
13753
+ saveLocalSettingsRemotely(source) {
13754
+ return __awaiter(this, void 0, void 0, function* () {
13755
+ if (!source || !this.isLogin())
13756
+ return; // Skip
13757
+ const account = this.account;
13758
+ // Merge local settings into account's settings
13759
+ const settings = UserSettings.fromObject(account.settings) || new UserSettings();
13760
+ const hasChanges = settings.merge(source, ['locale', 'latLongFormat', 'properties']);
13761
+ if (hasChanges) {
13762
+ if (this._debug)
13763
+ console.debug('[account] Save local settings remotely', settings);
13764
+ account.settings = settings;
13765
+ yield this.saveRemotely(account);
13766
+ }
13767
+ });
13768
+ }
13721
13769
  /**
13722
13770
  * Create or update an user account
13723
13771
  *
@@ -13762,7 +13810,7 @@ class AccountService extends BaseGraphqlService {
13762
13810
  }
13763
13811
  authenticateAndGetToken(token, counter) {
13764
13812
  return __awaiter(this, void 0, void 0, function* () {
13765
- if (!this._data.pubkey)
13813
+ if (!this._cache.pubkey)
13766
13814
  throw new Error('User not logged');
13767
13815
  if (!counter)
13768
13816
  console.info('[account] Authentication on pod...');
@@ -13788,7 +13836,7 @@ class AccountService extends BaseGraphqlService {
13788
13836
  if (data && data.authenticate) {
13789
13837
  // Store the token
13790
13838
  this.onAuthTokenChange.next(token);
13791
- console.info(`[account] Authentication on pod [OK] {pubkey: '${this._data.pubkey.substr(0, 8)}'}`);
13839
+ console.info(`[account] Authentication on pod [OK] {pubkey: '${this._cache.pubkey.substr(0, 8)}'}`);
13792
13840
  return token; // return the token
13793
13841
  }
13794
13842
  // Continue (will retry with another challenge)
@@ -13814,8 +13862,8 @@ class AccountService extends BaseGraphqlService {
13814
13862
  }
13815
13863
  // TODO: check server pubkey as a valid certificate
13816
13864
  // Do the challenge
13817
- const signature = yield this.cryptoService.sign(data.authChallenge.challenge, this._data.keypair);
13818
- const newToken = `${this._data.pubkey}:${data.authChallenge.challenge}|${signature}`;
13865
+ const signature = yield this.cryptoService.sign(data.authChallenge.challenge, this._cache.keypair);
13866
+ const newToken = `${this._cache.pubkey}:${data.authChallenge.challenge}|${signature}`;
13819
13867
  // iterate with the new token
13820
13868
  return yield this.authenticateAndGetToken(newToken, (counter || 1) + 1 /* increment */);
13821
13869
  });
@@ -14155,73 +14203,28 @@ class ConfigService extends BaseGraphqlService {
14155
14203
  this.toastController = toastController;
14156
14204
  this.translate = translate;
14157
14205
  this.environment = environment;
14158
- this._started = false;
14159
- this._subscription = new Subscription();
14160
14206
  this.$data = new BehaviorSubject(null);
14161
14207
  this._debug = !environment.production;
14162
14208
  if (this._debug)
14163
14209
  console.debug('[config] Creating service');
14164
14210
  this._optionDefs = Object.values(Object.assign(Object.assign({}, CORE_CONFIG_OPTIONS), defaultOptionsMap));
14165
- // Restart if graphql service restart
14166
- this.graphql.onStart.subscribe(() => this.restart());
14167
- // Start
14168
- if (this.graphql.started) {
14169
- this.start();
14170
- }
14171
- }
14172
- get started() {
14173
- return this._started;
14174
14211
  }
14175
14212
  get config() {
14176
14213
  // If first call: start loading
14177
- if (!this._started)
14214
+ if (!this.started)
14178
14215
  this.start();
14179
14216
  return this.$data.pipe(filter(isNotNil));
14180
14217
  }
14181
- start() {
14182
- if (this._startPromise)
14183
- return this._startPromise;
14184
- if (this._started)
14185
- return;
14186
- console.info('[config] Starting configuration...');
14187
- this._startPromise = this.graphql.ready()
14188
- .then(() => this.loadOrRestoreLocally())
14189
- .then((data) => {
14190
- this._started = true;
14191
- this._startPromise = undefined;
14218
+ ngOnStart() {
14219
+ return __awaiter(this, void 0, void 0, function* () {
14220
+ console.info('[config] Starting configuration...');
14221
+ const data = yield this.loadOrRestoreLocally();
14192
14222
  this.$data.next(data);
14223
+ // Restart if graphql service restart
14224
+ this.registerSubscription(this.graphql.onStart.subscribe(() => this.restart()));
14193
14225
  return data;
14194
- })
14195
- .catch((err) => {
14196
- console.error(err && err.message || err, err);
14197
- this._started = false;
14198
- this._startPromise = undefined;
14199
- return null;
14200
- });
14201
- return this._startPromise;
14202
- }
14203
- stop() {
14204
- return __awaiter(this, void 0, void 0, function* () {
14205
- this._subscription.unsubscribe();
14206
- this._subscription = new Subscription();
14207
- this._started = false;
14208
- this._startPromise = undefined;
14209
14226
  });
14210
14227
  }
14211
- restart() {
14212
- return __awaiter(this, void 0, void 0, function* () {
14213
- if (this.started)
14214
- yield this.stop();
14215
- return this.start();
14216
- });
14217
- }
14218
- ready() {
14219
- if (this._started)
14220
- return Promise.resolve(this.$data.value);
14221
- if (this._startPromise)
14222
- return this._startPromise;
14223
- return this.start();
14224
- }
14225
14228
  loadDefault(opts) {
14226
14229
  return __awaiter(this, void 0, void 0, function* () {
14227
14230
  const now = Date.now();
@@ -16657,7 +16660,7 @@ class AppForm {
16657
16660
  this.error = null;
16658
16661
  this._enable = false;
16659
16662
  this._$ready = new BehaviorSubject(false);
16660
- this._loading = true;
16663
+ this._$loading = new BehaviorSubject(true);
16661
16664
  this.i18nFieldPrefix = null;
16662
16665
  this._subscription = new Subscription();
16663
16666
  this.debug = false;
@@ -16673,8 +16676,20 @@ class AppForm {
16673
16676
  });
16674
16677
  this.autocompleteFields = this.autocompleteHelper.fields;
16675
16678
  }
16676
- get _ready() {
16677
- return this._$ready.value == true;
16679
+ /**
16680
+ * @deprecated
16681
+ */
16682
+ set _loading(value) {
16683
+ this._$loading.next(value);
16684
+ }
16685
+ /**
16686
+ * @deprecated
16687
+ */
16688
+ get _loading() {
16689
+ return this._$loading.value;
16690
+ }
16691
+ get loading() {
16692
+ return this._$loading.value;
16678
16693
  }
16679
16694
  get value() {
16680
16695
  return this.form.value;
@@ -16768,11 +16783,11 @@ class AppForm {
16768
16783
  if (this.debug)
16769
16784
  console.debug('[form] Updating form (using entity)', data);
16770
16785
  // Convert object to json, then apply it to form (e.g. convert 'undefined' into 'null')
16771
- AppFormUtils.copyEntity2Form(data, this.form, Object.assign({ emitEvent: false, onlySelf: true }, opts));
16772
- if (this._loading)
16773
- this.markAsLoaded(Object.assign({ emitEvent: false, onlySelf: true }, opts));
16774
- if (!opts || opts.emitEvent !== true)
16786
+ AppFormUtils.copyEntity2Form(data, this.form, opts);
16787
+ if (!opts || opts.emitEvent !== false) {
16788
+ this.markAsLoaded({ emitEvent: false });
16775
16789
  this.markForCheck();
16790
+ }
16776
16791
  }
16777
16792
  reset(data, opts) {
16778
16793
  if (data) {
@@ -16821,15 +16836,15 @@ class AppForm {
16821
16836
  this.markForCheck();
16822
16837
  }
16823
16838
  markAsLoading(opts) {
16824
- if (!this._loading) {
16825
- this._loading = true;
16839
+ if (this._$loading.value !== true) {
16840
+ this._$loading.next(true);
16826
16841
  if (!opts || opts.emitEvent !== false)
16827
16842
  this.markForCheck();
16828
16843
  }
16829
16844
  }
16830
16845
  markAsLoaded(opts) {
16831
- if (this._loading) {
16832
- this._loading = false;
16846
+ if (this._$loading.value !== false) {
16847
+ this._$loading.next(false);
16833
16848
  if (!opts || opts.emitEvent !== false)
16834
16849
  this.markForCheck();
16835
16850
  }
@@ -16919,7 +16934,6 @@ class AuthForm extends AppForm {
16919
16934
  this.network = network;
16920
16935
  this.cd = cd;
16921
16936
  this.environment = environment;
16922
- this.loading = false;
16923
16937
  this.canWorkOffline = false;
16924
16938
  this.showPwd = false;
16925
16939
  this.onCancel = new EventEmitter();
@@ -16967,13 +16981,13 @@ class AuthForm extends AppForm {
16967
16981
  }
16968
16982
  if (this.form.invalid || this.loading)
16969
16983
  return;
16970
- this.loading = true;
16984
+ this.markAsLoading();
16971
16985
  const data = this.form.value;
16972
16986
  this.showPwd = false; // Hide password
16973
16987
  this.error = null; // Reset error
16974
16988
  this.registerSubscription(this.onSubmit
16975
16989
  .pipe(debounceTime(500))
16976
- .subscribe(res => this.loading = false));
16990
+ .subscribe(res => this.markAsLoaded()));
16977
16991
  setTimeout(() => this.onSubmit.emit({
16978
16992
  username: data.username,
16979
16993
  password: data.password,
@@ -17612,7 +17626,6 @@ class AccountPage extends AppForm {
17612
17626
  this.translate = translate;
17613
17627
  this.settings = settings;
17614
17628
  this.cd = cd;
17615
- this.loading = true;
17616
17629
  this.email = {
17617
17630
  confirmed: false,
17618
17631
  notConfirmed: false,
@@ -17642,7 +17655,7 @@ class AccountPage extends AppForm {
17642
17655
  this.onLogin(this.accountService.account);
17643
17656
  }
17644
17657
  else {
17645
- this.loading = false;
17658
+ this.markAsLoaded();
17646
17659
  }
17647
17660
  }));
17648
17661
  }
@@ -17659,7 +17672,7 @@ class AccountPage extends AppForm {
17659
17672
  this.enable();
17660
17673
  this.markAsPristine();
17661
17674
  this.startListenChanges();
17662
- this.loading = false;
17675
+ this.markAsLoaded();
17663
17676
  }
17664
17677
  onLogout() {
17665
17678
  this.isLogin = false;
@@ -17676,7 +17689,7 @@ class AccountPage extends AppForm {
17676
17689
  refresh(event) {
17677
17690
  return __awaiter(this, void 0, void 0, function* () {
17678
17691
  this.disable();
17679
- this.loading = true;
17692
+ this.markAsLoading();
17680
17693
  return this.accountService.refresh();
17681
17694
  });
17682
17695
  }
@@ -18149,7 +18162,6 @@ class SettingsPage extends AppForm {
18149
18162
  this.cd = cd;
18150
18163
  this.network = network;
18151
18164
  this.locales = locales;
18152
- this.loading = true;
18153
18165
  this.saving = false;
18154
18166
  this.usageModes = ['FIELD', 'DESK'];
18155
18167
  this.propertyDefinitionsByKey = {};
@@ -18209,7 +18221,7 @@ class SettingsPage extends AppForm {
18209
18221
  }
18210
18222
  load() {
18211
18223
  return __awaiter(this, void 0, void 0, function* () {
18212
- this.loading = true;
18224
+ this.markAsLoading();
18213
18225
  console.debug('[settings] Loading settings...');
18214
18226
  const data = this.settings.settings;
18215
18227
  // Set defaults
@@ -18240,7 +18252,7 @@ class SettingsPage extends AppForm {
18240
18252
  this.enable({ emitEvent: false });
18241
18253
  // Apply inheritance
18242
18254
  this.setAccountInheritance(data.accountInheritance, { emitEvent: false });
18243
- this.loading = false;
18255
+ this.markAsLoaded();
18244
18256
  this.error = null;
18245
18257
  this.markForCheck();
18246
18258
  }
@@ -18425,7 +18437,6 @@ class AppPropertiesForm extends AppForm {
18425
18437
  this.settings = settings;
18426
18438
  this.cd = cd;
18427
18439
  this.formGroupDir = formGroupDir;
18428
- this.loading = true;
18429
18440
  this.definitionsMapByKey = {};
18430
18441
  this.definitionsByIndex = {};
18431
18442
  //this.debug = !environment.production;
@@ -18494,7 +18505,7 @@ class AppPropertiesForm extends AppForm {
18494
18505
  this.helper.resize(values.length);
18495
18506
  this.helper.formArray.patchValue(values, { emitEvent: false });
18496
18507
  this.markAsPristine();
18497
- this.loading = false;
18508
+ this.markAsLoaded();
18498
18509
  }
18499
18510
  /* -- protected methods -- */
18500
18511
  getPropertyFormGroup(data) {
@@ -18538,7 +18549,6 @@ class AppListForm extends AppForm {
18538
18549
  this.settings = settings;
18539
18550
  this.cd = cd;
18540
18551
  this.formGroupDir = formGroupDir;
18541
- this.loading = true;
18542
18552
  this.selection = new SelectionModel(true, []);
18543
18553
  this.displayWithFn = referentialToString;
18544
18554
  this.onNewItem = new EventEmitter();
@@ -18622,7 +18632,7 @@ class AppListForm extends AppForm {
18622
18632
  }
18623
18633
  this.selection.clear();
18624
18634
  this.markAsPristine();
18625
- this.loading = false;
18635
+ this.markAsLoaded();
18626
18636
  }
18627
18637
  /** Whether the number of selected elements matches the total number of rows. */
18628
18638
  isAllSelected() {
@@ -19722,12 +19732,6 @@ class BaseEntityService extends BaseGraphqlService {
19722
19732
  this.defaultSortBy = options.defaultSortBy || 'id';
19723
19733
  this.defaultSortDirection = options.defaultSortDirection || 'asc';
19724
19734
  this.watchQueriesUpdatePolicy = options.watchQueriesUpdatePolicy || 'update-cache';
19725
- platform.ready().then(() => {
19726
- // No limit for updatable watch queries, if desktop
19727
- if (!platform.mobile) {
19728
- this._mutableWatchQueriesMaxCount = -1;
19729
- }
19730
- });
19731
19735
  const obj = new dataType();
19732
19736
  this._typename = obj.__typename || 'UnknownVO';
19733
19737
  this._logTypeName = removeEnd(this._typename, 'VO');
@@ -20105,6 +20109,14 @@ class BaseEntityService extends BaseGraphqlService {
20105
20109
  return EntityFilterUtils.fromObject(source, this.filterType);
20106
20110
  }
20107
20111
  /* -- protected functions -- */
20112
+ ngOnStart() {
20113
+ return __awaiter(this, void 0, void 0, function* () {
20114
+ // No limit for updatable watch queries, if desktop
20115
+ if (!this.platform.mobile) {
20116
+ this._mutableWatchQueriesMaxCount = -1;
20117
+ }
20118
+ });
20119
+ }
20108
20120
  fillDefaultProperties(source) {
20109
20121
  // Can be override by subclasses
20110
20122
  }