@sumaris-net/ngx-components 0.24.3 → 0.25.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 (36) hide show
  1. package/bundles/sumaris-net.ngx-components.umd.js +860 -835
  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/public_api.js +2 -1
  7. package/esm2015/src/app/admin/users/list/users.js +6 -4
  8. package/esm2015/src/app/core/graphql/graphql.service.js +18 -65
  9. package/esm2015/src/app/core/install/install-upgrade-card.component.js +14 -8
  10. package/esm2015/src/app/core/services/account.service.js +146 -149
  11. package/esm2015/src/app/core/services/config.service.js +21 -14
  12. package/esm2015/src/app/core/services/local-settings.service.js +72 -87
  13. package/esm2015/src/app/core/services/network.service.js +133 -168
  14. package/esm2015/src/app/core/services/platform.service.js +26 -14
  15. package/esm2015/src/app/core/services/storage/entities-storage.service.js +59 -63
  16. package/esm2015/src/app/shared/audio/audio.js +10 -39
  17. package/esm2015/src/app/shared/material/autocomplete/testing/autocomplete.test.js +2 -2
  18. package/esm2015/src/app/shared/services/startable-service.class.js +74 -0
  19. package/esm2015/src/environments/environment.class.js +1 -1
  20. package/esm2015/src/environments/environment.js +5 -1
  21. package/fesm2015/sumaris-net.ngx-components.js +578 -614
  22. package/fesm2015/sumaris-net.ngx-components.js.map +1 -1
  23. package/package.json +1 -1
  24. package/public_api.d.ts +1 -0
  25. package/src/app/admin/users/list/users.d.ts +1 -0
  26. package/src/app/core/graphql/graphql.service.d.ts +5 -13
  27. package/src/app/core/services/account.service.d.ts +11 -10
  28. package/src/app/core/services/config.service.d.ts +6 -5
  29. package/src/app/core/services/local-settings.service.d.ts +4 -9
  30. package/src/app/core/services/network.service.d.ts +27 -38
  31. package/src/app/core/services/platform.service.d.ts +1 -1
  32. package/src/app/core/services/storage/entities-storage.service.d.ts +13 -13
  33. package/src/app/shared/audio/audio.d.ts +4 -8
  34. package/src/app/shared/services/startable-service.class.d.ts +25 -0
  35. package/src/environments/environment.class.d.ts +1 -0
  36. package/sumaris-net.ngx-components.metadata.json +1 -1
@@ -7732,39 +7732,127 @@
7732
7732
  matInput: [{ type: i0.ViewChild, args: ['matInput',] }]
7733
7733
  };
7734
7734
 
7735
- var SYSTEM_SOUNDS = [
7736
- { id: 'beep-confirm', assetPath: 'assets/audio/beep-confirm.mp3', vibration: 250 },
7737
- { id: 'beep-error', assetPath: 'assets/audio/beep-error.mp3', vibration: 1000 },
7738
- { id: 'startup', assetPath: 'assets/audio/unfa-ping.mp3', vibration: [1, 500, 250, 750] },
7739
- ];
7740
- var AudioProvider = /** @class */ (function () {
7741
- function AudioProvider(platform, nativeAudio, vibration, audioManagement) {
7742
- this.platform = platform;
7743
- this.nativeAudio = nativeAudio;
7744
- this.vibration = vibration;
7745
- this.audioManagement = audioManagement;
7746
- this._started = false;
7747
- this._audioMode = ngx$1.AudioManagement.AudioMode.NORMAL;
7748
- this._preloadedSounds = {};
7749
- this._htmlAudioCache = {};
7735
+ var StartableService = /** @class */ (function () {
7736
+ function StartableService(platform) {
7750
7737
  this.onStart = new rxjs.Subject();
7751
- this.start();
7738
+ this._debug = false;
7739
+ this._data = null;
7740
+ this._started = false;
7741
+ this._startPrerequisite = platform
7742
+ ? function () { return platform.ready(); }
7743
+ : function () { return Promise.resolve(); };
7752
7744
  }
7753
- AudioProvider.prototype.ready = function () {
7745
+ StartableService.prototype.start = function () {
7746
+ var _this = this;
7747
+ if (this._startPromise)
7748
+ return this._startPromise;
7749
+ if (this._started)
7750
+ return Promise.resolve(this._data);
7751
+ this._startPromise = this._startPrerequisite()
7752
+ .then(function () { return _this.ngOnStart(); })
7753
+ .then(function (data) {
7754
+ _this._data = data;
7755
+ _this._started = true;
7756
+ _this._startPromise = undefined;
7757
+ _this.onStart.next(_this._data);
7758
+ return _this._data;
7759
+ })
7760
+ .catch(function (err) {
7761
+ console.error('Failed to start a service: ' + (err && err.message || err), err);
7762
+ _this._started = false;
7763
+ _this._startPromise = null;
7764
+ return null;
7765
+ });
7766
+ return this._startPromise;
7767
+ };
7768
+ StartableService.prototype.stop = function () {
7754
7769
  return __awaiter(this, void 0, void 0, function () {
7770
+ var err_1;
7755
7771
  return __generator(this, function (_a) {
7756
7772
  switch (_a.label) {
7757
7773
  case 0:
7758
- if (this._started)
7759
- return [2 /*return*/, Promise.resolve()];
7760
- return [4 /*yield*/, this.start()];
7774
+ _a.trys.push([0, 2, , 3]);
7775
+ return [4 /*yield*/, this.ngOnStop()];
7761
7776
  case 1:
7762
7777
  _a.sent();
7763
- return [2 /*return*/];
7778
+ this._started = false;
7779
+ this._startPromise = undefined;
7780
+ return [3 /*break*/, 3];
7781
+ case 2:
7782
+ err_1 = _a.sent();
7783
+ console.error('Failed to stop a service: ' + (err_1 && err_1.message || err_1), err_1);
7784
+ return [3 /*break*/, 3];
7785
+ case 3: return [2 /*return*/];
7786
+ }
7787
+ });
7788
+ });
7789
+ };
7790
+ StartableService.prototype.restart = function () {
7791
+ return __awaiter(this, void 0, void 0, function () {
7792
+ return __generator(this, function (_a) {
7793
+ switch (_a.label) {
7794
+ case 0:
7795
+ if (!this._startPromise) return [3 /*break*/, 2];
7796
+ return [4 /*yield*/, this._startPromise];
7797
+ case 1:
7798
+ _a.sent(); // Wait end of previous loading
7799
+ _a.label = 2;
7800
+ case 2:
7801
+ if (!this._started) return [3 /*break*/, 4];
7802
+ return [4 /*yield*/, this.stop()];
7803
+ case 3:
7804
+ _a.sent(); // Then stop if started
7805
+ _a.label = 4;
7806
+ case 4: // Then stop if started
7807
+ return [2 /*return*/, this.start()]; // Then start again
7764
7808
  }
7765
7809
  });
7766
7810
  });
7767
7811
  };
7812
+ Object.defineProperty(StartableService.prototype, "started", {
7813
+ get: function () {
7814
+ return this._started;
7815
+ },
7816
+ enumerable: false,
7817
+ configurable: true
7818
+ });
7819
+ StartableService.prototype.ready = function () {
7820
+ if (this._started)
7821
+ return Promise.resolve(this._data);
7822
+ return this.start();
7823
+ };
7824
+ StartableService.prototype.ngOnStop = function () {
7825
+ return __awaiter(this, void 0, void 0, function () {
7826
+ return __generator(this, function (_a) {
7827
+ return [2 /*return*/];
7828
+ });
7829
+ });
7830
+ };
7831
+ return StartableService;
7832
+ }());
7833
+ StartableService.ctorParameters = function () { return [
7834
+ { type: undefined, decorators: [{ type: i0.Optional }] }
7835
+ ]; };
7836
+
7837
+ var SYSTEM_SOUNDS = [
7838
+ { id: 'beep-confirm', assetPath: 'assets/audio/beep-confirm.mp3', vibration: 250 },
7839
+ { id: 'beep-error', assetPath: 'assets/audio/beep-error.mp3', vibration: 1000 },
7840
+ { id: 'startup', assetPath: 'assets/audio/unfa-ping.mp3', vibration: [1, 500, 250, 750] },
7841
+ ];
7842
+ var AudioProvider = /** @class */ (function (_super) {
7843
+ __extends(AudioProvider, _super);
7844
+ function AudioProvider(platform, nativeAudio, vibration, audioManagement) {
7845
+ var _this = _super.call(this, platform) || this;
7846
+ _this.platform = platform;
7847
+ _this.nativeAudio = nativeAudio;
7848
+ _this.vibration = vibration;
7849
+ _this.audioManagement = audioManagement;
7850
+ _this._audioMode = ngx$1.AudioManagement.AudioMode.NORMAL;
7851
+ _this._preloadedSounds = {};
7852
+ _this._htmlAudioCache = {};
7853
+ _this.start();
7854
+ return _this;
7855
+ }
7768
7856
  AudioProvider.prototype.playBeepConfirm = function () {
7769
7857
  return this.play('beep-confirm', {
7770
7858
  // Vibrate only if in vibration mode
@@ -7828,7 +7916,7 @@
7828
7916
  return __generator(this, function (_a) {
7829
7917
  switch (_a.label) {
7830
7918
  case 0:
7831
- if (!!this._started) return [3 /*break*/, 2];
7919
+ if (!!this.started) return [3 /*break*/, 2];
7832
7920
  return [4 /*yield*/, this.ready()];
7833
7921
  case 1:
7834
7922
  _a.sent();
@@ -7912,7 +8000,7 @@
7912
8000
  return __generator(this, function (_a) {
7913
8001
  switch (_a.label) {
7914
8002
  case 0:
7915
- if (!!this._started) return [3 /*break*/, 2];
8003
+ if (!!this.started) return [3 /*break*/, 2];
7916
8004
  return [4 /*yield*/, this.ready()];
7917
8005
  case 1:
7918
8006
  _a.sent();
@@ -7927,15 +8015,10 @@
7927
8015
  });
7928
8016
  };
7929
8017
  /* -- protected methods -- */
7930
- AudioProvider.prototype.start = function () {
7931
- var _this = this;
7932
- if (this._startPromise)
7933
- return this._startPromise;
7934
- if (this._started)
7935
- return Promise.resolve();
7936
- var cordova;
7937
- this._startPromise = this.platform.ready()
7938
- .then(function () { return __awaiter(_this, void 0, void 0, function () {
8018
+ AudioProvider.prototype.ngOnStart = function () {
8019
+ return __awaiter(this, void 0, void 0, function () {
8020
+ var cordova;
8021
+ var _this = this;
7939
8022
  return __generator(this, function (_a) {
7940
8023
  switch (_a.label) {
7941
8024
  case 0:
@@ -7947,33 +8030,21 @@
7947
8030
  case 1:
7948
8031
  _a.sent();
7949
8032
  _a.label = 2;
7950
- case 2: return [2 /*return*/];
8033
+ case 2:
8034
+ // Pre-loading system sounds
8035
+ console.debug('[audio] Preloading audio sounds...');
8036
+ return [4 /*yield*/, Promise.all(SYSTEM_SOUNDS.map(function (s) {
8037
+ // Disable vibration is cordova not enabled
8038
+ if (!cordova)
8039
+ s.vibration = undefined;
8040
+ return _this.preload(s);
8041
+ }))];
8042
+ case 3:
8043
+ _a.sent();
8044
+ return [2 /*return*/];
7951
8045
  }
7952
8046
  });
7953
- }); })
7954
- // Pre-loading system sounds
7955
- .then(function () {
7956
- console.debug('[audio] Preloading audio sounds...');
7957
- return Promise.all(SYSTEM_SOUNDS.map(function (s) {
7958
- // Disable vibration is cordova not enabled
7959
- if (!cordova)
7960
- s.vibration = undefined;
7961
- return _this.preload(s);
7962
- }));
7963
- })
7964
- .then(function () {
7965
- _this._started = true;
7966
- _this._startPromise = null;
7967
- console.info('[audio] Audio provider started');
7968
- // Emit event
7969
- _this.onStart.next();
7970
- })
7971
- .catch(function (err) {
7972
- console.error('[audio] Unable to start audio provider: ' + (err && err.message || err), err);
7973
- _this._started = false;
7974
- _this._startPromise = null;
7975
8047
  });
7976
- return this._startPromise;
7977
8048
  };
7978
8049
  AudioProvider.prototype.readAudioMode = function () {
7979
8050
  return __awaiter(this, void 0, void 0, function () {
@@ -7991,7 +8062,7 @@
7991
8062
  });
7992
8063
  };
7993
8064
  return AudioProvider;
7994
- }());
8065
+ }(StartableService));
7995
8066
  AudioProvider.ɵprov = i0__namespace.ɵɵdefineInjectable({ factory: function AudioProvider_Factory() { return new AudioProvider(i0__namespace.ɵɵinject(i1__namespace$4.Platform), i0__namespace.ɵɵinject(i2__namespace.NativeAudio, 8), i0__namespace.ɵɵinject(i3__namespace.Vibration, 8), i0__namespace.ɵɵinject(i4__namespace.AudioManagement, 8)); }, token: AudioProvider, providedIn: "root" });
7996
8067
  AudioProvider.decorators = [
7997
8068
  { type: i0.Injectable, args: [{ providedIn: 'root' },] }
@@ -8977,6 +9048,10 @@
8977
9048
  {
8978
9049
  host: 'sih.sfa.sc',
8979
9050
  port: 80
9051
+ },
9052
+ {
9053
+ host: 'test.sumaris.net',
9054
+ port: 443
8980
9055
  }
8981
9056
  ],
8982
9057
  defaultAppName: 'SUMARiS',
@@ -9589,110 +9664,87 @@
9589
9664
  };
9590
9665
  var APP_LOCAL_SETTINGS = new i0.InjectionToken('DefaultLocalSettings');
9591
9666
  var APP_LOCAL_SETTINGS_OPTIONS = new i0.InjectionToken('LocalSettingsOptions');
9592
- var LocalSettingsService = /** @class */ (function () {
9667
+ var LocalSettingsService = /** @class */ (function (_super) {
9668
+ __extends(LocalSettingsService, _super);
9593
9669
  function LocalSettingsService(translate, platform, storage, environment, defaultSettings, defaultOptionsMap) {
9594
- this.translate = translate;
9595
- this.platform = platform;
9596
- this.storage = storage;
9597
- this.environment = environment;
9598
- this.defaultSettings = defaultSettings;
9599
- this._started = false;
9600
- this.onChange = new rxjs.Subject();
9601
- this.defaultSettings = Object.assign(Object.assign({}, DEFAULT_SETTINGS), this.defaultSettings);
9602
- this._optionDefs = Object.values(defaultOptionsMap);
9603
- this.resetData();
9604
- this._debug = !environment.production;
9605
- if (this._debug)
9670
+ var _this = _super.call(this, platform) || this;
9671
+ _this.translate = translate;
9672
+ _this.platform = platform;
9673
+ _this.storage = storage;
9674
+ _this.environment = environment;
9675
+ _this.defaultSettings = defaultSettings;
9676
+ _this.onChange = new rxjs.Subject();
9677
+ _this.defaultSettings = Object.assign(Object.assign({}, DEFAULT_SETTINGS), _this.defaultSettings);
9678
+ _this._optionDefs = Object.values(defaultOptionsMap);
9679
+ _this.resetData();
9680
+ _this._debug = !environment.production;
9681
+ if (_this._debug)
9606
9682
  console.debug('[settings] Creating service');
9683
+ return _this;
9607
9684
  }
9608
9685
  Object.defineProperty(LocalSettingsService.prototype, "settings", {
9609
9686
  get: function () {
9610
- return this.data || this.defaultSettings;
9687
+ return this._data || this.defaultSettings;
9611
9688
  },
9612
9689
  enumerable: false,
9613
9690
  configurable: true
9614
9691
  });
9615
9692
  Object.defineProperty(LocalSettingsService.prototype, "locale", {
9616
9693
  get: function () {
9617
- return this.data && this.data.locale || this.translate.currentLang || this.translate.defaultLang;
9694
+ return this._data && this._data.locale || this.translate.currentLang || this.translate.defaultLang;
9618
9695
  },
9619
9696
  enumerable: false,
9620
9697
  configurable: true
9621
9698
  });
9622
9699
  Object.defineProperty(LocalSettingsService.prototype, "latLongFormat", {
9623
9700
  get: function () {
9624
- return this.data && this.data.latLongFormat || 'DDMM';
9701
+ return this._data && this._data.latLongFormat || 'DDMM';
9625
9702
  },
9626
9703
  enumerable: false,
9627
9704
  configurable: true
9628
9705
  });
9629
9706
  Object.defineProperty(LocalSettingsService.prototype, "usageMode", {
9630
9707
  get: function () {
9631
- return (this.data && this.data.usageMode || (this.mobile ? 'FIELD' : 'DESK'));
9708
+ return (this._data && this._data.usageMode || (this.mobile ? 'FIELD' : 'DESK'));
9632
9709
  },
9633
9710
  enumerable: false,
9634
9711
  configurable: true
9635
9712
  });
9636
9713
  Object.defineProperty(LocalSettingsService.prototype, "mobile", {
9637
9714
  get: function () {
9638
- return this.data && toBoolean(this.data.mobile, this.platform.is('mobile'));
9715
+ return this._data && toBoolean(this._data.mobile, this.platform.is('mobile'));
9639
9716
  },
9640
9717
  set: function (value) {
9641
- this.data.mobile = value;
9718
+ this._data.mobile = value;
9642
9719
  },
9643
9720
  enumerable: false,
9644
9721
  configurable: true
9645
9722
  });
9646
9723
  Object.defineProperty(LocalSettingsService.prototype, "touchUi", {
9647
9724
  get: function () {
9648
- return this.data.touchUi;
9725
+ return this._data.touchUi;
9649
9726
  },
9650
9727
  set: function (value) {
9651
- this.data.touchUi = value;
9728
+ this._data.touchUi = value;
9652
9729
  },
9653
9730
  enumerable: false,
9654
9731
  configurable: true
9655
9732
  });
9656
9733
  Object.defineProperty(LocalSettingsService.prototype, "pageHistory", {
9657
9734
  get: function () {
9658
- return (this.data && this.data.pageHistory || []);
9735
+ return (this._data && this._data.pageHistory || []);
9659
9736
  },
9660
9737
  enumerable: false,
9661
9738
  configurable: true
9662
9739
  });
9663
- LocalSettingsService.prototype.start = function () {
9664
- var _this = this;
9665
- if (this._startPromise)
9666
- return this._startPromise;
9667
- if (this._started)
9668
- return Promise.resolve(this.data);
9669
- console.info('[settings] Starting settings...');
9740
+ LocalSettingsService.prototype.ngOnStart = function () {
9741
+ console.info('[settings] Starting service...');
9670
9742
  // Restoring local settings
9671
- this._startPromise = this.platform.ready()
9672
- .then(function () {
9673
- _this.data.mobile = isNotNil(_this.data.mobile) ? _this.data.mobile : _this.platform.is('mobile');
9674
- _this.data.touchUi = _this.data.mobile || _this.platform.is('phablet') || _this.platform.is('tablet');
9675
- _this.data.usageMode = _this.platform.is('android') ? 'FIELD' : 'DESK'; // FIELD by default if Android
9676
- })
9677
- .then(function () { return _this.restoreLocally(); })
9678
- .then(function (data) {
9679
- _this._started = true;
9680
- _this._startPromise = undefined;
9681
- return data;
9682
- });
9683
- return this._startPromise;
9684
- };
9685
- Object.defineProperty(LocalSettingsService.prototype, "started", {
9686
- get: function () {
9687
- return this._started;
9688
- },
9689
- enumerable: false,
9690
- configurable: true
9691
- });
9692
- LocalSettingsService.prototype.ready = function () {
9693
- if (this._started)
9694
- return Promise.resolve(this.data);
9695
- return this.start();
9743
+ this._data.mobile = isNotNil(this._data.mobile) ? this._data.mobile : this.platform.is('mobile');
9744
+ this._data.touchUi = this._data.mobile || this.platform.is('phablet') || this.platform.is('tablet');
9745
+ this._data.usageMode = this.platform.is('android') ? 'FIELD' : 'DESK'; // FIELD by default if Android
9746
+ // Restoring local settings
9747
+ return this.restoreLocally();
9696
9748
  };
9697
9749
  LocalSettingsService.prototype.isUsageMode = function (mode) {
9698
9750
  return this.usageMode === mode;
@@ -9702,10 +9754,12 @@
9702
9754
  };
9703
9755
  LocalSettingsService.prototype.restoreLocally = function () {
9704
9756
  return __awaiter(this, void 0, void 0, function () {
9705
- var settingsStr, restoredData_1;
9757
+ var data, settingsStr, restoredData_1;
9706
9758
  return __generator(this, function (_a) {
9707
9759
  switch (_a.label) {
9708
- case 0: return [4 /*yield*/, this.storage.get(SETTINGS_STORAGE_KEY)];
9760
+ case 0:
9761
+ data = this._data || {};
9762
+ return [4 /*yield*/, this.storage.get(SETTINGS_STORAGE_KEY)];
9709
9763
  case 1:
9710
9764
  settingsStr = _a.sent();
9711
9765
  // Restore local settings (or keep old settings)
@@ -9716,30 +9770,31 @@
9716
9770
  SETTINGS_TRANSIENT_PROPERTIES.forEach(function (transientKey) {
9717
9771
  delete restoredData_1[transientKey];
9718
9772
  });
9719
- this.data = Object.assign(this.data, restoredData_1);
9773
+ // Merge into existing data
9774
+ data = Object.assign(data, restoredData_1);
9720
9775
  }
9721
9776
  // Emit event
9722
- this.onChange.next(this.data);
9723
- return [2 /*return*/, this.data];
9777
+ this.onChange.next(data);
9778
+ return [2 /*return*/, data];
9724
9779
  }
9725
9780
  });
9726
9781
  });
9727
9782
  };
9728
9783
  LocalSettingsService.prototype.setProperty = function (keyOrDef, value) {
9729
- if (!this.data)
9784
+ if (!this._data)
9730
9785
  return;
9731
9786
  if (typeof keyOrDef === 'object') {
9732
9787
  this.setProperty(keyOrDef.key, value);
9733
9788
  return;
9734
9789
  }
9735
- this.data.properties = this.data.properties || {};
9736
- this.data.properties[keyOrDef] = isNil(value) ? undefined : value.toString();
9790
+ this._data.properties = this._data.properties || {};
9791
+ this._data.properties[keyOrDef] = isNil(value) ? undefined : value.toString();
9737
9792
  };
9738
9793
  LocalSettingsService.prototype.getProperty = function (keyOrDef, defaultValue) {
9739
9794
  if (typeof keyOrDef === 'object') {
9740
9795
  return this.getProperty(keyOrDef.key, isNil(defaultValue) ? keyOrDef.defaultValue : defaultValue);
9741
9796
  }
9742
- var value = this.data && this.data.properties && this.data.properties[keyOrDef];
9797
+ var value = this._data && this._data.properties && this._data.properties[keyOrDef];
9743
9798
  return isNotNil(value) ? value : defaultValue;
9744
9799
  };
9745
9800
  LocalSettingsService.prototype.getPropertyAsBoolean = function (definition, defaultValue) {
@@ -9765,19 +9820,25 @@
9765
9820
  return __generator(this, function (_a) {
9766
9821
  switch (_a.label) {
9767
9822
  case 0:
9768
- this.data = Object.assign(Object.assign({}, this.data), settings);
9769
- if (!(opts && opts.persistImmediate)) return [3 /*break*/, 2];
9770
- return [4 /*yield*/, this.persistLocally(true)];
9823
+ if (!!this.started) return [3 /*break*/, 2];
9824
+ return [4 /*yield*/, this.ready()];
9771
9825
  case 1:
9772
9826
  _a.sent();
9773
- return [3 /*break*/, 3];
9827
+ _a.label = 2;
9774
9828
  case 2:
9775
- this.persistLocally(); // No AWAIT
9776
- _a.label = 3;
9829
+ this._data = Object.assign(Object.assign({}, this._data), settings);
9830
+ if (!(opts && opts.persistImmediate)) return [3 /*break*/, 4];
9831
+ return [4 /*yield*/, this.persistLocally(true)];
9777
9832
  case 3:
9833
+ _a.sent();
9834
+ return [3 /*break*/, 5];
9835
+ case 4:
9836
+ this.persistLocally(); // No AWAIT
9837
+ _a.label = 5;
9838
+ case 5:
9778
9839
  // Emit event
9779
9840
  if (!opts || opts.emitEvent !== false) {
9780
- this.onChange.next(this.data);
9841
+ this.onChange.next(this._data);
9781
9842
  }
9782
9843
  return [2 /*return*/];
9783
9844
  }
@@ -9801,27 +9862,27 @@
9801
9862
  });
9802
9863
  };
9803
9864
  LocalSettingsService.prototype.getPageSettings = function (pageId, propertyName) {
9804
- if (!this.data || !this.data.pages)
9865
+ if (!this._data || !this._data.pages)
9805
9866
  return undefined;
9806
9867
  var key = pageId.replace(/[/]/g, '__');
9807
9868
  if (isNotNilOrBlank(propertyName)) {
9808
- return getPropertyByPath(this.data.pages, key + '.' + propertyName);
9869
+ return getPropertyByPath(this._data.pages, key + '.' + propertyName);
9809
9870
  }
9810
- return this.data.pages[key];
9871
+ return this._data.pages[key];
9811
9872
  };
9812
9873
  LocalSettingsService.prototype.savePageSetting = function (pageId, value, propertyName) {
9813
9874
  return __awaiter(this, void 0, void 0, function () {
9814
9875
  var key;
9815
9876
  return __generator(this, function (_a) {
9816
- this.data = this.data || this.defaultSettings;
9817
- this.data.pages = this.data.pages || {};
9877
+ this._data = this._data || this.defaultSettings;
9878
+ this._data.pages = this._data.pages || {};
9818
9879
  key = pageId.replace(/[/]/g, '__');
9819
9880
  if (propertyName) {
9820
- this.data.pages[key] = this.data.pages[key] || {};
9821
- this.data.pages[key][propertyName] = value;
9881
+ this._data.pages[key] = this._data.pages[key] || {};
9882
+ this._data.pages[key][propertyName] = value;
9822
9883
  }
9823
9884
  else {
9824
- this.data.pages[key] = value;
9885
+ this._data.pages[key] = value;
9825
9886
  }
9826
9887
  // Update local settings
9827
9888
  this.persistLocally();
@@ -9830,13 +9891,13 @@
9830
9891
  });
9831
9892
  };
9832
9893
  LocalSettingsService.prototype.getOfflineFeature = function (featureName) {
9833
- if (!this.data || !this.data.offlineFeatures || isEmptyArray(this.data.offlineFeatures))
9894
+ if (!this._data || !this._data.offlineFeatures || isEmptyArray(this._data.offlineFeatures))
9834
9895
  return undefined;
9835
9896
  if (!featureName)
9836
9897
  throw Error('Missing \'featureName\' argument');
9837
9898
  featureName = featureName.toLowerCase();
9838
9899
  var featurePrefix = featureName + '#';
9839
- var feature = this.data.offlineFeatures.find(function (f) {
9900
+ var feature = this._data.offlineFeatures.find(function (f) {
9840
9901
  if (typeof f === 'string')
9841
9902
  return f.toLowerCase().startsWith(featurePrefix);
9842
9903
  if (typeof f === 'object' && f.name)
@@ -9859,24 +9920,24 @@
9859
9920
  LocalSettingsService.prototype.hasOfflineFeature = function (featureName) {
9860
9921
  if (featureName)
9861
9922
  return isNotNil(this.getOfflineFeature(featureName));
9862
- return this.data && isNotEmptyArray(this.data.offlineFeatures);
9923
+ return this._data && isNotEmptyArray(this._data.offlineFeatures);
9863
9924
  };
9864
9925
  LocalSettingsService.prototype.saveOfflineFeature = function (feature) {
9865
- this.data = this.data || this.defaultSettings;
9866
- this.data.offlineFeatures = this.data.offlineFeatures || [];
9926
+ this._data = this._data || this.defaultSettings;
9927
+ this._data.offlineFeatures = this._data.offlineFeatures || [];
9867
9928
  feature.name = feature.name.toLowerCase();
9868
9929
  var featurePrefix = feature.name + '#';
9869
- var existingIndex = this.data.offlineFeatures.findIndex(function (f) {
9930
+ var existingIndex = this._data.offlineFeatures.findIndex(function (f) {
9870
9931
  if (typeof f === 'string')
9871
9932
  return f.toLowerCase().startsWith(featurePrefix);
9872
9933
  if (typeof f === 'object' && f.name)
9873
9934
  return f.name === feature.name;
9874
9935
  });
9875
9936
  if (existingIndex !== -1) {
9876
- this.data.offlineFeatures[existingIndex] = feature;
9937
+ this._data.offlineFeatures[existingIndex] = feature;
9877
9938
  }
9878
9939
  else {
9879
- this.data.offlineFeatures.push(feature);
9940
+ this._data.offlineFeatures.push(feature);
9880
9941
  }
9881
9942
  // Update local settings
9882
9943
  this.persistLocally();
@@ -9895,14 +9956,14 @@
9895
9956
  this.saveOfflineFeature(feature);
9896
9957
  };
9897
9958
  LocalSettingsService.prototype.removeOfflineFeatures = function () {
9898
- if (this.data && this.data.offlineFeatures) {
9899
- this.data.offlineFeatures = [];
9959
+ if (this._data && this._data.offlineFeatures) {
9960
+ this._data.offlineFeatures = [];
9900
9961
  // Update local settings
9901
9962
  this.persistLocally();
9902
9963
  }
9903
9964
  };
9904
9965
  LocalSettingsService.prototype.getFieldDisplayAttributes = function (fieldName, defaultAttributes) {
9905
- var value = this.data && this.data.properties && this.data.properties["sumaris.field." + fieldName + ".attributes"];
9966
+ var value = this._data && this._data.properties && this._data.properties["sumaris.field." + fieldName + ".attributes"];
9906
9967
  // Nothing found in settings: return defaults
9907
9968
  if (!value)
9908
9969
  return defaultAttributes || ['label', 'name'];
@@ -9940,7 +10001,7 @@
9940
10001
  // If not inside recursive call: fill page history defaults
9941
10002
  if (!pageHistory)
9942
10003
  this.fillPageHistoryDefaults(page, opts);
9943
- pageHistory = pageHistory || this.data.pageHistory;
10004
+ pageHistory = pageHistory || this._data.pageHistory;
9944
10005
  index = pageHistory.findIndex(function (p) { return (
9945
10006
  // same path
9946
10007
  p.path === page.path
@@ -9975,10 +10036,10 @@
9975
10036
  _a.sent();
9976
10037
  _a.label = 4;
9977
10038
  case 4:
9978
- if (!(pageHistory === this.data.pageHistory)) return [3 /*break*/, 6];
10039
+ if (!(pageHistory === this._data.pageHistory)) return [3 /*break*/, 6];
9979
10040
  // If max has been reached, remove old pages
9980
- if (this.data.pageHistory.length > this.data.pageHistoryMaxSize) {
9981
- removedPages = pageHistory.splice(this.data.pageHistoryMaxSize, pageHistory.length - this.data.pageHistoryMaxSize);
10041
+ if (this._data.pageHistory.length > this._data.pageHistoryMaxSize) {
10042
+ removedPages = pageHistory.splice(this._data.pageHistoryMaxSize, pageHistory.length - this._data.pageHistoryMaxSize);
9982
10043
  console.debug('[settings] Pages removed from history: ', removedPages);
9983
10044
  }
9984
10045
  // Apply new value
@@ -10000,7 +10061,7 @@
10000
10061
  return __generator(this, function (_a) {
10001
10062
  switch (_a.label) {
10002
10063
  case 0:
10003
- pageHistory = pageHistory || this.data.pageHistory;
10064
+ pageHistory = pageHistory || this._data.pageHistory;
10004
10065
  index = pageHistory.findIndex(function (p) { return p.path === path; });
10005
10066
  found = index !== -1;
10006
10067
  if (found) {
@@ -10015,9 +10076,9 @@
10015
10076
  .filter(isNotEmptyArray)
10016
10077
  .findIndex(function (children) { return _this.removePageHistory(path, opts, children); }) !== -1;
10017
10078
  }
10018
- if (!(found && pageHistory === this.data.pageHistory)) return [3 /*break*/, 2];
10079
+ if (!(found && pageHistory === this._data.pageHistory)) return [3 /*break*/, 2];
10019
10080
  // Apply changes
10020
- return [4 /*yield*/, this.applyProperty('pageHistory', this.data.pageHistory)];
10081
+ return [4 /*yield*/, this.applyProperty('pageHistory', this._data.pageHistory)];
10021
10082
  case 1:
10022
10083
  // Apply changes
10023
10084
  _a.sent();
@@ -10044,27 +10105,27 @@
10044
10105
  };
10045
10106
  /* -- Protected methods -- */
10046
10107
  LocalSettingsService.prototype.resetData = function () {
10047
- this.data = Object.assign(Object.assign({}, this.data), this.defaultSettings);
10048
- this.data.locale = this.translate.currentLang || this.translate.defaultLang;
10049
- this.data.mobile = undefined;
10050
- this.data.usageMode = undefined;
10051
- this.data.pageHistory = [];
10108
+ this._data = Object.assign(Object.assign({}, this._data), this.defaultSettings);
10109
+ this._data.locale = this.translate.currentLang || this.translate.defaultLang;
10110
+ this._data.mobile = undefined;
10111
+ this._data.usageMode = undefined;
10112
+ this._data.pageHistory = [];
10052
10113
  var defaultPeer = this.environment.defaultPeer && exports.Peer.fromObject(this.environment.defaultPeer);
10053
- this.data.peerUrl = defaultPeer && defaultPeer.url || undefined;
10054
- if (this._started)
10055
- this.onChange.next(this.data);
10114
+ this._data.peerUrl = defaultPeer && defaultPeer.url || undefined;
10115
+ if (this.started)
10116
+ this.onChange.next(this._data);
10056
10117
  };
10057
10118
  LocalSettingsService.prototype.persistLocally = function (immediate) {
10058
10119
  var _this = this;
10059
10120
  // Execute immediate
10060
10121
  if (immediate) {
10061
- if (!this.data) {
10122
+ if (!this._data) {
10062
10123
  console.debug('[settings] Removing local settings from storage');
10063
10124
  return this.storage.remove(SETTINGS_STORAGE_KEY);
10064
10125
  }
10065
10126
  else {
10066
- console.debug('[settings] Store local settings', this.data);
10067
- return this.storage.set(SETTINGS_STORAGE_KEY, JSON.stringify(this.data));
10127
+ console.debug('[settings] Store local settings', this._data);
10128
+ return this.storage.set(SETTINGS_STORAGE_KEY, JSON.stringify(this._data));
10068
10129
  }
10069
10130
  }
10070
10131
  // Execute with delay
@@ -10074,7 +10135,7 @@
10074
10135
  this._$persist = new i0.EventEmitter(true);
10075
10136
  this._$persist
10076
10137
  .pipe(operators.debounceTime(2000), // add a delay of 2s
10077
- operators.filter(function () { return _this._started; }))
10138
+ operators.filter(function () { return _this.started; }))
10078
10139
  .subscribe(function () { return _this.persistLocally(true); });
10079
10140
  }
10080
10141
  this._$persist.emit();
@@ -10100,7 +10161,7 @@
10100
10161
  return page;
10101
10162
  };
10102
10163
  return LocalSettingsService;
10103
- }());
10164
+ }(StartableService));
10104
10165
  LocalSettingsService.ɵprov = i0__namespace.ɵɵdefineInjectable({ factory: function LocalSettingsService_Factory() { return new LocalSettingsService(i0__namespace.ɵɵinject(i1__namespace$1.TranslateService), i0__namespace.ɵɵinject(i1__namespace$4.Platform), i0__namespace.ɵɵinject(i3__namespace$1.Storage), i0__namespace.ɵɵinject(ENVIRONMENT), i0__namespace.ɵɵinject(APP_LOCAL_SETTINGS, 8), i0__namespace.ɵɵinject(APP_LOCAL_SETTINGS_OPTIONS, 8)); }, token: LocalSettingsService, providedIn: "root" });
10105
10166
  LocalSettingsService.decorators = [
10106
10167
  { type: i0.Injectable, args: [{
@@ -10271,41 +10332,42 @@
10271
10332
  MOBILE: 1000,
10272
10333
  DESKTOP: 1000
10273
10334
  }*/
10274
- var NetworkService = /** @class */ (function () {
10335
+ var NetworkService = /** @class */ (function (_super) {
10336
+ __extends(NetworkService, _super);
10275
10337
  function NetworkService(_document, platform, modalCtrl, cryptoService, storage, settings, cache, http, environment, network, splashScreen, translate, toastController) {
10276
- var _this = this;
10277
- this._document = _document;
10278
- this.platform = platform;
10279
- this.modalCtrl = modalCtrl;
10280
- this.cryptoService = cryptoService;
10281
- this.storage = storage;
10282
- this.settings = settings;
10283
- this.cache = cache;
10284
- this.http = http;
10285
- this.environment = environment;
10286
- this.network = network;
10287
- this.splashScreen = splashScreen;
10288
- this.translate = translate;
10289
- this.toastController = toastController;
10290
- this._started = false;
10291
- this._subscription = new rxjs.Subscription();
10292
- this._listeners = {};
10293
- this.onStart = new rxjs.Subject();
10294
- this.onPeerChanges = this.onStart.pipe(operators.map(function (peer) { return peer && peer.url; }), operators.filter(isNotNilOrBlank), operators.distinctUntilChanged());
10295
- this.onNetworkStatusChanges = new rxjs.BehaviorSubject(null);
10296
- this.onResetNetworkCache = new i0.EventEmitter(true);
10297
- this._mobile = this.platform.is('mobile');
10298
- if (this._mobile) {
10299
- this._timerRefreshPeriod = NetworkRefreshTimerPeriod.MOBILE;
10300
- this._timerRefreshCondition = function () { return _this.online; }; // Check only when online, and stop when offline
10338
+ var _this = _super.call(this, platform) || this;
10339
+ _this._document = _document;
10340
+ _this.platform = platform;
10341
+ _this.modalCtrl = modalCtrl;
10342
+ _this.cryptoService = cryptoService;
10343
+ _this.storage = storage;
10344
+ _this.settings = settings;
10345
+ _this.cache = cache;
10346
+ _this.http = http;
10347
+ _this.environment = environment;
10348
+ _this.network = network;
10349
+ _this.splashScreen = splashScreen;
10350
+ _this.translate = translate;
10351
+ _this.toastController = toastController;
10352
+ _this.onPeerChanges = _this.onStart.pipe(operators.map(function (peer) { return peer && peer.url; }), operators.filter(isNotNilOrBlank), operators.distinctUntilChanged());
10353
+ _this.onNetworkStatusChanges = new rxjs.BehaviorSubject(null);
10354
+ _this.onResetNetworkCache = new i0.EventEmitter(true);
10355
+ _this._subscription = new rxjs.Subscription();
10356
+ _this._listeners = {};
10357
+ _this._mobile = _this.platform.is('mobile');
10358
+ if (_this._mobile) {
10359
+ _this._timerRefreshPeriod = NetworkRefreshTimerPeriod.MOBILE;
10360
+ _this._timerRefreshCondition = function () { return _this.online; }; // Check only when online, and stop when offline
10301
10361
  }
10302
10362
  else {
10303
- this._timerRefreshPeriod = NetworkRefreshTimerPeriod.DESKTOP;
10304
- this._timerRefreshCondition = function () { return true; }; // Always check
10363
+ _this._timerRefreshPeriod = NetworkRefreshTimerPeriod.DESKTOP;
10364
+ _this._timerRefreshCondition = function () { return true; }; // Always check
10305
10365
  }
10306
- this.resetData();
10366
+ _this.resetData();
10367
+ _this.onStart.subscribe(function () { return _this.ngOnAfterStart(); });
10307
10368
  // For DEV only
10308
- this._debug = !environment.production;
10369
+ _this._debug = !environment.production;
10370
+ return _this;
10309
10371
  }
10310
10372
  Object.defineProperty(NetworkService.prototype, "online", {
10311
10373
  get: function () {
@@ -10326,24 +10388,18 @@
10326
10388
  // If force offline: return 'none'
10327
10389
  return this._forceOffline && 'none'
10328
10390
  // Else, return device connection type (or unknown)
10329
- || (this._started && this._deviceConnectionType || 'unknown');
10391
+ || (this.started && this._deviceConnectionType || 'unknown');
10330
10392
  },
10331
10393
  enumerable: false,
10332
10394
  configurable: true
10333
10395
  });
10334
10396
  Object.defineProperty(NetworkService.prototype, "peer", {
10335
10397
  get: function () {
10336
- return this._peer && this._peer.clone();
10398
+ return this._data && this._data.clone();
10337
10399
  },
10338
10400
  set: function (peer) {
10339
- this.restart(peer);
10340
- },
10341
- enumerable: false,
10342
- configurable: true
10343
- });
10344
- Object.defineProperty(NetworkService.prototype, "started", {
10345
- get: function () {
10346
- return this._started;
10401
+ this._startingPeer = peer;
10402
+ this.restart();
10347
10403
  },
10348
10404
  enumerable: false,
10349
10405
  configurable: true
@@ -10368,109 +10424,11 @@
10368
10424
  return this.addListener(eventType, callback);
10369
10425
  }
10370
10426
  };
10371
- NetworkService.prototype.start = function (peer) {
10372
- return __awaiter(this, void 0, void 0, function () {
10373
- var _this = this;
10374
- return __generator(this, function (_a) {
10375
- if (this._startPromise)
10376
- return [2 /*return*/, this._startPromise];
10377
- if (this._started)
10378
- return [2 /*return*/];
10379
- console.info('[network] Starting network...');
10380
- // Restoring local settings
10381
- this._startPromise = (!peer && this.restoreLocally() || Promise.resolve(peer))
10382
- .then(function (peer) { return __awaiter(_this, void 0, void 0, function () {
10383
- return __generator(this, function (_a) {
10384
- switch (_a.label) {
10385
- case 0:
10386
- // Make sure to hide the splashscreen, before open the modal
10387
- if (!peer && this.splashScreen)
10388
- this.splashScreen.hide();
10389
- _a.label = 1;
10390
- case 1:
10391
- if (!!peer) return [3 /*break*/, 3];
10392
- console.debug('[network] No peer defined. Asking user to choose a peer.');
10393
- return [4 /*yield*/, this.showSelectPeerModal({ allowSelectDownPeer: false })];
10394
- case 2:
10395
- peer = _a.sent();
10396
- return [3 /*break*/, 1];
10397
- case 3:
10398
- this._peer = peer;
10399
- this._started = true;
10400
- this._startPromise = undefined;
10401
- this.onStart.next(peer);
10402
- console.info("[platform] Starting network [OK] {online: " + this.online + "}");
10403
- return [2 /*return*/];
10404
- }
10405
- });
10406
- }); })
10407
- .catch(function (err) {
10408
- console.error(err && err.message || err, err);
10409
- _this._started = false;
10410
- _this._startPromise = undefined;
10411
- })
10412
- // Wait settings starts, then save peer in settings
10413
- .then(function () { return _this.settings.ready(); })
10414
- .then(function () { return _this.settings.apply({ peerUrl: _this._peer.url }); })
10415
- .then(function () { return _this.onDeviceConnectionChanged(_this.network && _this.network.type || 'unknown'); })
10416
- // Start the refresh timer
10417
- .then(function () { return _this.startRefreshTimer(); });
10418
- // Listen for device network changes
10419
- if (this.network) {
10420
- this._subscription.add(this.network.onDisconnect().subscribe(function () { return _this.onDeviceConnectionChanged('none'); }));
10421
- this._subscription.add(this.network.onConnect().subscribe(function () { return _this.onDeviceConnectionChanged(_this.network.type); }));
10422
- }
10423
- return [2 /*return*/, this._startPromise];
10424
- });
10425
- });
10426
- };
10427
- NetworkService.prototype.ready = function () {
10428
- if (this._started)
10429
- return Promise.resolve();
10430
- return this.start();
10431
- };
10432
- NetworkService.prototype.stop = function () {
10433
- return __awaiter(this, void 0, void 0, function () {
10434
- return __generator(this, function (_a) {
10435
- this.resetData();
10436
- this._started = false;
10437
- this._startPromise = undefined;
10438
- // Stop timer if cannot refresh anymore
10439
- if (this._timerRefreshCondition() === false) {
10440
- this.stopRefreshTimer();
10441
- }
10442
- this._subscription.unsubscribe();
10443
- this._subscription = new rxjs.Subscription();
10444
- return [2 /*return*/];
10445
- });
10446
- });
10447
- };
10448
- NetworkService.prototype.restart = function (peer) {
10449
- return __awaiter(this, void 0, void 0, function () {
10450
- var _this = this;
10451
- return __generator(this, function (_a) {
10452
- switch (_a.label) {
10453
- case 0:
10454
- if (!this._started) return [3 /*break*/, 2];
10455
- return [4 /*yield*/, this.stop()
10456
- .then(function () { return _this.start(peer); })];
10457
- case 1:
10458
- _a.sent();
10459
- return [3 /*break*/, 4];
10460
- case 2: return [4 /*yield*/, this.start(peer)];
10461
- case 3:
10462
- _a.sent();
10463
- _a.label = 4;
10464
- case 4: return [2 /*return*/];
10465
- }
10466
- });
10467
- });
10468
- };
10469
10427
  NetworkService.prototype.tryOnline = function (opts) {
10470
10428
  return __awaiter(this, void 0, void 0, function () {
10471
- var now, showLoadingToast, loadingToast, settings, peer, peerInfo, peerAliveAndCompatible, _a, err_1, online;
10472
- return __generator(this, function (_b) {
10473
- switch (_b.label) {
10429
+ var now, showLoadingToast, loadingToast, settings, peer, peerInfo, peerAliveAndCompatible, _b, err_1, online;
10430
+ return __generator(this, function (_c) {
10431
+ switch (_c.label) {
10474
10432
  case 0:
10475
10433
  // If offline mode not forced, and device says there is no connection: skip
10476
10434
  if (!this._forceOffline || this._deviceConnectionType === 'none')
@@ -10481,57 +10439,57 @@
10481
10439
  return [4 /*yield*/, this.showToast({ message: 'NETWORK.INFO.RETRY_TO_CONNECT',
10482
10440
  duration: 10000, onWillPresent: function (t) { return loadingToast = t; } })];
10483
10441
  case 1:
10484
- _b.sent();
10485
- _b.label = 2;
10442
+ _c.sent();
10443
+ _c.label = 2;
10486
10444
  case 2:
10487
- _b.trys.push([2, 10, , 11]);
10445
+ _c.trys.push([2, 10, , 11]);
10488
10446
  console.info('[network] Checking connection to pod...');
10489
10447
  return [4 /*yield*/, this.settings.ready()];
10490
10448
  case 3:
10491
- settings = _b.sent();
10449
+ settings = _c.sent();
10492
10450
  if (!settings.peerUrl)
10493
10451
  return [2 /*return*/, false]; // No peer define. Skip
10494
10452
  peer = exports.Peer.parseUrl(settings.peerUrl);
10495
10453
  return [4 /*yield*/, this.checkPeerAlive(peer)];
10496
10454
  case 4:
10497
- peerInfo = _b.sent();
10498
- _a = peerInfo;
10499
- if (!_a) return [3 /*break*/, 6];
10455
+ peerInfo = _c.sent();
10456
+ _b = peerInfo;
10457
+ if (!_b) return [3 /*break*/, 6];
10500
10458
  return [4 /*yield*/, this.checkPeerCompatible(peerInfo)];
10501
10459
  case 5:
10502
- _a = (_b.sent());
10503
- _b.label = 6;
10460
+ _b = (_c.sent());
10461
+ _c.label = 6;
10504
10462
  case 6:
10505
- peerAliveAndCompatible = _a;
10463
+ peerAliveAndCompatible = _b;
10506
10464
  if (!peerAliveAndCompatible) return [3 /*break*/, 9];
10507
10465
  // Disable the offline mode
10508
10466
  this.setForceOffline(false);
10509
10467
  // Restart
10510
- return [4 /*yield*/, this.restart(peer)];
10468
+ this._startingPeer = peer;
10469
+ return [4 /*yield*/, this.restart()];
10511
10470
  case 7:
10512
- // Restart
10513
- _b.sent();
10471
+ _c.sent();
10514
10472
  // Wait a promise, before recheck
10515
10473
  return [4 /*yield*/, this.emit('beforeTryOnlineFinish', this.online)];
10516
10474
  case 8:
10517
10475
  // Wait a promise, before recheck
10518
- _b.sent();
10519
- _b.label = 9;
10476
+ _c.sent();
10477
+ _c.label = 9;
10520
10478
  case 9: return [3 /*break*/, 11];
10521
10479
  case 10:
10522
- err_1 = _b.sent();
10480
+ err_1 = _c.sent();
10523
10481
  console.error(err_1 && err_1.message || err_1);
10524
10482
  return [3 /*break*/, 11];
10525
10483
  case 11:
10526
10484
  if (!showLoadingToast) return [3 /*break*/, 14];
10527
10485
  return [4 /*yield*/, sleep(2000 - (Date.now() - now))];
10528
10486
  case 12:
10529
- _b.sent();
10487
+ _c.sent();
10530
10488
  if (!loadingToast) return [3 /*break*/, 14];
10531
10489
  return [4 /*yield*/, loadingToast.dismiss()];
10532
10490
  case 13:
10533
- _b.sent();
10534
- _b.label = 14;
10491
+ _c.sent();
10492
+ _c.label = 14;
10535
10493
  case 14:
10536
10494
  online = this.online;
10537
10495
  // Display a toast to user
@@ -10545,7 +10503,7 @@
10545
10503
  // Display toast (without await, because not need to wait toast close event)
10546
10504
  return [2 /*return*/, this.showOfflineToast({ showRetryButton: false })];
10547
10505
  }
10548
- return [2 /*return*/, this._started && online];
10506
+ return [2 /*return*/, this.started && online];
10549
10507
  }
10550
10508
  });
10551
10509
  });
@@ -10553,8 +10511,8 @@
10553
10511
  NetworkService.prototype.showOfflineToast = function (opts) {
10554
10512
  return __awaiter(this, void 0, void 0, function () {
10555
10513
  var toastResult, online;
10556
- return __generator(this, function (_a) {
10557
- switch (_a.label) {
10514
+ return __generator(this, function (_b) {
10515
+ switch (_b.label) {
10558
10516
  case 0:
10559
10517
  if (this.online)
10560
10518
  return [2 /*return*/]; // Skip if online
@@ -10573,7 +10531,7 @@
10573
10531
  ]
10574
10532
  })];
10575
10533
  case 1:
10576
- toastResult = _a.sent();
10534
+ toastResult = _b.sent();
10577
10535
  // User don't click reconnect: return
10578
10536
  if (!toastResult || toastResult.role !== 'refresh')
10579
10537
  return [2 /*return*/, false];
@@ -10586,7 +10544,7 @@
10586
10544
  showLoadingToast: toBoolean(opts && opts.showRetryLoadingToast, true)
10587
10545
  })];
10588
10546
  case 2:
10589
- online = _a.sent();
10547
+ online = _b.sent();
10590
10548
  if (online) {
10591
10549
  // Call success callback (async)
10592
10550
  if (opts && opts.onRetrySuccess) {
@@ -10595,7 +10553,7 @@
10595
10553
  return [2 /*return*/, true];
10596
10554
  }
10597
10555
  opts = Object.assign(Object.assign({}, opts), { showRetryButton: false, showCloseButton: true });
10598
- _a.label = 3;
10556
+ _b.label = 3;
10599
10557
  case 3:
10600
10558
  // Simple toast, without 'await', because not need to wait toast's dismiss
10601
10559
  this.showToast(Object.assign({ message: 'ERROR.NETWORK_REQUIRED', type: 'error' }, opts));
@@ -10604,97 +10562,6 @@
10604
10562
  });
10605
10563
  });
10606
10564
  };
10607
- /**
10608
- * Try to restore peer from the local storage
10609
- */
10610
- NetworkService.prototype.restoreLocally = function () {
10611
- return __awaiter(this, void 0, void 0, function () {
10612
- var settingsStr, settings, location, hostname, detectedPeer;
10613
- return __generator(this, function (_a) {
10614
- switch (_a.label) {
10615
- case 0: return [4 /*yield*/, this.storage.get(SETTINGS_STORAGE_KEY)];
10616
- case 1:
10617
- settingsStr = _a.sent();
10618
- settings = settingsStr && JSON.parse(settingsStr) || undefined;
10619
- if (settings && settings.peerUrl) {
10620
- console.debug("[network] Use peer {" + settings.peerUrl + "} (found in the local storage)");
10621
- return [2 /*return*/, exports.Peer.parseUrl(settings.peerUrl)];
10622
- }
10623
- // Else, use default peer in env, if exists
10624
- if (this.environment.defaultPeer) {
10625
- return [2 /*return*/, exports.Peer.fromObject(this.environment.defaultPeer)];
10626
- }
10627
- location = this._document && this._document.location;
10628
- if (!(location && location.protocol && location.protocol.startsWith('http'))) return [3 /*break*/, 3];
10629
- hostname = this._document.location.host;
10630
- detectedPeer = exports.Peer.parseUrl("" + this._document.location.protocol + hostname + this.environment.baseUrl);
10631
- return [4 /*yield*/, this.checkPeerAlive(detectedPeer)];
10632
- case 2:
10633
- if (_a.sent()) {
10634
- return [2 /*return*/, detectedPeer];
10635
- }
10636
- _a.label = 3;
10637
- case 3: return [2 /*return*/, undefined];
10638
- }
10639
- });
10640
- });
10641
- };
10642
- /**
10643
- * Refresh network state, using a ping to pod
10644
- */
10645
- NetworkService.prototype.refreshPeerState = function (opts) {
10646
- return __awaiter(this, void 0, void 0, function () {
10647
- return __generator(this, function (_a) {
10648
- return [2 /*return*/];
10649
- });
10650
- });
10651
- };
10652
- /**
10653
- * Stop to network state
10654
- *
10655
- * @protected
10656
- */
10657
- NetworkService.prototype.stopRefreshTimer = function () {
10658
- if (this._timerSubscription) {
10659
- this._timerSubscription.unsubscribe();
10660
- this._timerSubscription = undefined;
10661
- }
10662
- };
10663
- /**
10664
- * Refresh the network state
10665
- *
10666
- * @protected
10667
- */
10668
- NetworkService.prototype.startRefreshTimer = function () {
10669
- var _this = this;
10670
- if (this._timerSubscription)
10671
- return; // Already running: skip
10672
- console.info("[network] Starting refresh timer, every " + this._timerRefreshPeriod + "ms...");
10673
- var lastInfo;
10674
- this._timerSubscription = rxjs.timer(this._timerRefreshPeriod, this._timerRefreshPeriod)
10675
- .pipe(
10676
- // Skip some timer event (see constructor)
10677
- operators.filter(this._timerRefreshCondition),
10678
- // Checkin if peer alive
10679
- operators.tap(function () { return console.debug('[network] Checking connection to pod...'); }), operators.mergeMap(function () { return _this.checkPeerAlive(_this.peer); }),
10680
- // Filter to keep only changes
10681
- operators.filter(function (info) { return !!info !== !!lastInfo; }), operators.tap(function (info) { return lastInfo = info; }),
10682
- // Check compatibility
10683
- operators.mergeMap(function (info) { return _this.checkPeerCompatible(info, { showToast: true }); }))
10684
- .subscribe(function (alive) {
10685
- if (alive && _this.offline) {
10686
- _this.setForceOffline(false);
10687
- // Restart the service (to force re auth)
10688
- _this.restart();
10689
- }
10690
- else if (!alive && _this.online) {
10691
- _this.setForceOffline(true);
10692
- // Stop the service
10693
- _this.stop();
10694
- }
10695
- });
10696
- this._timerSubscription.add(function () { return console.debug('[network] Refresh timer stopped'); });
10697
- };
10698
10565
  /**
10699
10566
  * Check if the peer is alive
10700
10567
  *
@@ -10703,24 +10570,24 @@
10703
10570
  NetworkService.prototype.checkPeerAlive = function (peer, opts) {
10704
10571
  return __awaiter(this, void 0, void 0, function () {
10705
10572
  var settings, err_2;
10706
- return __generator(this, function (_a) {
10707
- switch (_a.label) {
10573
+ return __generator(this, function (_b) {
10574
+ switch (_b.label) {
10708
10575
  case 0:
10709
10576
  peer = peer || this.peer;
10710
10577
  if (!!peer) return [3 /*break*/, 2];
10711
10578
  return [4 /*yield*/, this.settings.ready()];
10712
10579
  case 1:
10713
- settings = _a.sent();
10580
+ settings = _b.sent();
10714
10581
  if (!settings.peerUrl)
10715
10582
  return [2 /*return*/, undefined]; // No peer define. Skip
10716
10583
  peer = exports.Peer.parseUrl(settings.peerUrl);
10717
- _a.label = 2;
10584
+ _b.label = 2;
10718
10585
  case 2:
10719
- _a.trys.push([2, 4, , 5]);
10586
+ _b.trys.push([2, 4, , 5]);
10720
10587
  return [4 /*yield*/, this.getNodeInfo(peer)];
10721
- case 3: return [2 /*return*/, _a.sent()];
10588
+ case 3: return [2 /*return*/, _b.sent()];
10722
10589
  case 4:
10723
- err_2 = _a.sent();
10590
+ err_2 = _b.sent();
10724
10591
  console.debug('[network] Cannot get /api/node/info from peer: ' + (err_2 && err_2.message || err_2), err_2);
10725
10592
  return [2 /*return*/, undefined];
10726
10593
  case 5: return [2 /*return*/];
@@ -10731,8 +10598,8 @@
10731
10598
  NetworkService.prototype.checkPeerCompatible = function (peerInfo, opts) {
10732
10599
  return __awaiter(this, void 0, void 0, function () {
10733
10600
  var isCompatible;
10734
- return __generator(this, function (_a) {
10735
- switch (_a.label) {
10601
+ return __generator(this, function (_b) {
10602
+ switch (_b.label) {
10736
10603
  case 0:
10737
10604
  if (!this.environment.peerMinVersion)
10738
10605
  return [2 /*return*/, true]; // Skip compatibility check
@@ -10747,8 +10614,8 @@
10747
10614
  showCloseButton: true
10748
10615
  })];
10749
10616
  case 1:
10750
- _a.sent();
10751
- _a.label = 2;
10617
+ _b.sent();
10618
+ _b.label = 2;
10752
10619
  case 2: return [2 /*return*/, isCompatible];
10753
10620
  }
10754
10621
  });
@@ -10788,8 +10655,8 @@
10788
10655
  return __awaiter(this, void 0, void 0, function () {
10789
10656
  var $onRefresh, peers$, modal, data;
10790
10657
  var _this = this;
10791
- return __generator(this, function (_a) {
10792
- switch (_a.label) {
10658
+ return __generator(this, function (_b) {
10659
+ switch (_b.label) {
10793
10660
  case 0:
10794
10661
  opts = opts || {};
10795
10662
  $onRefresh = new i0.EventEmitter();
@@ -10806,14 +10673,14 @@
10806
10673
  showBackdrop: true
10807
10674
  })];
10808
10675
  case 1:
10809
- modal = _a.sent();
10676
+ modal = _b.sent();
10810
10677
  return [4 /*yield*/, modal.present()];
10811
10678
  case 2:
10812
- _a.sent();
10679
+ _b.sent();
10813
10680
  $onRefresh.emit();
10814
10681
  return [4 /*yield*/, modal.onWillDismiss()];
10815
10682
  case 3:
10816
- data = (_a.sent()).data;
10683
+ data = (_b.sent()).data;
10817
10684
  $onRefresh.complete();
10818
10685
  return [2 /*return*/, data && data || undefined];
10819
10686
  }
@@ -10824,7 +10691,7 @@
10824
10691
  return __awaiter(this, void 0, void 0, function () {
10825
10692
  var now;
10826
10693
  var _this = this;
10827
- return __generator(this, function (_a) {
10694
+ return __generator(this, function (_b) {
10828
10695
  now = this._debug && Date.now();
10829
10696
  console.info('[network] Clearing all caches...');
10830
10697
  return [2 /*return*/, this.cache.clearAll()
@@ -10843,10 +10710,164 @@
10843
10710
  });
10844
10711
  });
10845
10712
  };
10713
+ /* -- protected functions -- */
10714
+ NetworkService.prototype.ngOnStart = function () {
10715
+ return __awaiter(this, void 0, void 0, function () {
10716
+ var peer, _b;
10717
+ return __generator(this, function (_c) {
10718
+ switch (_c.label) {
10719
+ case 0:
10720
+ console.info('[network] Starting network...', this._startingPeer);
10721
+ _b = this._startingPeer;
10722
+ if (_b) return [3 /*break*/, 2];
10723
+ return [4 /*yield*/, this.restoreLocally()];
10724
+ case 1:
10725
+ _b = (_c.sent());
10726
+ _c.label = 2;
10727
+ case 2:
10728
+ peer = _b;
10729
+ // Make sure to hide the splashscreen, before open the modal
10730
+ if (!peer && this.splashScreen)
10731
+ this.splashScreen.hide();
10732
+ _c.label = 3;
10733
+ case 3:
10734
+ if (!!peer) return [3 /*break*/, 5];
10735
+ console.debug('[network] No peer defined. Asking user to choose a peer.');
10736
+ return [4 /*yield*/, this.showSelectPeerModal({ allowSelectDownPeer: false })];
10737
+ case 4:
10738
+ peer = _c.sent();
10739
+ return [3 /*break*/, 3];
10740
+ case 5:
10741
+ console.info("[network] Starting service [OK] {peer: '" + peer.url + "', online: " + this.online + "}");
10742
+ return [2 /*return*/, peer];
10743
+ }
10744
+ });
10745
+ });
10746
+ };
10747
+ NetworkService.prototype.ngOnAfterStart = function () {
10748
+ var _a;
10749
+ return __awaiter(this, void 0, void 0, function () {
10750
+ var _this = this;
10751
+ return __generator(this, function (_b) {
10752
+ switch (_b.label) {
10753
+ case 0:
10754
+ // Wait settings starts, then save peer in settings
10755
+ return [4 /*yield*/, this.settings.apply({ peerUrl: this._data.url })];
10756
+ case 1:
10757
+ // Wait settings starts, then save peer in settings
10758
+ _b.sent();
10759
+ this.onDeviceConnectionChanged(((_a = this.network) === null || _a === void 0 ? void 0 : _a.type) || 'unknown');
10760
+ // Start the refresh timer
10761
+ this.startRefreshTimer();
10762
+ // Listen for device network changes
10763
+ if (this.network) {
10764
+ this._subscription.add(this.network.onDisconnect().subscribe(function () { return _this.onDeviceConnectionChanged('none'); }));
10765
+ this._subscription.add(this.network.onConnect().subscribe(function () { return _this.onDeviceConnectionChanged(_this.network.type); }));
10766
+ }
10767
+ return [2 /*return*/];
10768
+ }
10769
+ });
10770
+ });
10771
+ };
10772
+ NetworkService.prototype.ngOnStop = function () {
10773
+ return __awaiter(this, void 0, void 0, function () {
10774
+ return __generator(this, function (_b) {
10775
+ this.resetData();
10776
+ // Stop timer if cannot refresh anymore
10777
+ if (this._timerRefreshCondition() === false) {
10778
+ this.stopRefreshTimer();
10779
+ }
10780
+ this._subscription.unsubscribe();
10781
+ this._subscription = new rxjs.Subscription();
10782
+ return [2 /*return*/];
10783
+ });
10784
+ });
10785
+ };
10786
+ /**
10787
+ * Try to restore peer from the local storage
10788
+ */
10789
+ NetworkService.prototype.restoreLocally = function () {
10790
+ return __awaiter(this, void 0, void 0, function () {
10791
+ var settingsStr, settings, location, hostname, detectedPeer;
10792
+ return __generator(this, function (_b) {
10793
+ switch (_b.label) {
10794
+ case 0: return [4 /*yield*/, this.storage.get(SETTINGS_STORAGE_KEY)];
10795
+ case 1:
10796
+ settingsStr = _b.sent();
10797
+ settings = settingsStr && JSON.parse(settingsStr) || undefined;
10798
+ if (settings && settings.peerUrl) {
10799
+ console.debug("[network] Use peer {" + settings.peerUrl + "} (found in the local storage)");
10800
+ return [2 /*return*/, exports.Peer.parseUrl(settings.peerUrl)];
10801
+ }
10802
+ // Else, use default peer in env, if exists
10803
+ if (this.environment.defaultPeer) {
10804
+ return [2 /*return*/, exports.Peer.fromObject(this.environment.defaultPeer)];
10805
+ }
10806
+ location = this._document && this._document.location;
10807
+ if (!(location && location.protocol && location.protocol.startsWith('http'))) return [3 /*break*/, 3];
10808
+ hostname = this._document.location.host;
10809
+ detectedPeer = exports.Peer.parseUrl("" + this._document.location.protocol + hostname + this.environment.baseUrl);
10810
+ return [4 /*yield*/, this.checkPeerAlive(detectedPeer)];
10811
+ case 2:
10812
+ if (_b.sent()) {
10813
+ return [2 /*return*/, detectedPeer];
10814
+ }
10815
+ _b.label = 3;
10816
+ case 3: return [2 /*return*/, undefined];
10817
+ }
10818
+ });
10819
+ });
10820
+ };
10821
+ /**
10822
+ * Stop to network state
10823
+ *
10824
+ * @protected
10825
+ */
10826
+ NetworkService.prototype.stopRefreshTimer = function () {
10827
+ if (this._timerSubscription) {
10828
+ this._timerSubscription.unsubscribe();
10829
+ this._timerSubscription = undefined;
10830
+ }
10831
+ };
10832
+ /**
10833
+ * Refresh the network state
10834
+ *
10835
+ * @protected
10836
+ */
10837
+ NetworkService.prototype.startRefreshTimer = function () {
10838
+ var _this = this;
10839
+ if (this._timerSubscription)
10840
+ return; // Already running: skip
10841
+ console.info("[network] Starting refresh timer, every " + this._timerRefreshPeriod + "ms...");
10842
+ var lastInfo;
10843
+ this._timerSubscription = rxjs.timer(this._timerRefreshPeriod, this._timerRefreshPeriod)
10844
+ .pipe(
10845
+ // Skip some timer event (see constructor)
10846
+ operators.filter(this._timerRefreshCondition),
10847
+ // Checkin if peer alive
10848
+ operators.tap(function () { return console.debug('[network] Checking connection to pod...'); }), operators.mergeMap(function () { return _this.checkPeerAlive(_this.peer); }),
10849
+ // Filter to keep only changes
10850
+ operators.filter(function (info) { return !!info !== !!lastInfo; }), operators.tap(function (info) { return lastInfo = info; }),
10851
+ // Check compatibility
10852
+ operators.mergeMap(function (info) { return _this.checkPeerCompatible(info, { showToast: true }); }))
10853
+ .subscribe(function (alive) {
10854
+ if (alive && _this.offline) {
10855
+ _this.setForceOffline(false);
10856
+ // Restart the service (to force re auth)
10857
+ _this.restart();
10858
+ }
10859
+ else if (!alive && _this.online) {
10860
+ _this.setForceOffline(true);
10861
+ // Stop the service
10862
+ _this.stop();
10863
+ }
10864
+ });
10865
+ this._timerSubscription.add(function () { return console.debug('[network] Refresh timer stopped'); });
10866
+ };
10846
10867
  NetworkService.prototype.get = function (path, opts) {
10847
10868
  return __awaiter(this, void 0, void 0, function () {
10848
10869
  var uri, peerUrl;
10849
- return __generator(this, function (_a) {
10870
+ return __generator(this, function (_b) {
10850
10871
  uri = path;
10851
10872
  // If path is not an URI: prepend with peer URL
10852
10873
  if (!uri.startsWith('http://') && !uri.startsWith('https://')) {
@@ -10897,7 +10918,7 @@
10897
10918
  }
10898
10919
  };
10899
10920
  NetworkService.prototype.resetData = function () {
10900
- this._peer = null;
10921
+ this._data = null;
10901
10922
  };
10902
10923
  /**
10903
10924
  * Get default peers, from environment
@@ -10905,7 +10926,7 @@
10905
10926
  NetworkService.prototype.getDefaultPeers = function () {
10906
10927
  return __awaiter(this, void 0, void 0, function () {
10907
10928
  var peers;
10908
- return __generator(this, function (_a) {
10929
+ return __generator(this, function (_b) {
10909
10930
  peers = (this.environment.defaultPeers || []).map(exports.Peer.fromObject);
10910
10931
  return [2 /*return*/, Promise.resolve(peers)];
10911
10932
  });
@@ -10929,7 +10950,7 @@
10929
10950
  NetworkService.prototype.emit = function (name, data) {
10930
10951
  return __awaiter(this, void 0, void 0, function () {
10931
10952
  var hooks;
10932
- return __generator(this, function (_a) {
10953
+ return __generator(this, function (_b) {
10933
10954
  hooks = this._listeners[name];
10934
10955
  if (isNotEmptyArray(hooks)) {
10935
10956
  console.info("[network-service] Trigger " + name + " hook: Executing " + hooks.length + " callbacks...");
@@ -10945,7 +10966,7 @@
10945
10966
  });
10946
10967
  };
10947
10968
  return NetworkService;
10948
- }());
10969
+ }(StartableService));
10949
10970
  NetworkService.ɵprov = i0__namespace.ɵɵdefineInjectable({ factory: function NetworkService_Factory() { return new NetworkService(i0__namespace.ɵɵinject(i1__namespace$3.DOCUMENT), i0__namespace.ɵɵinject(i1__namespace$4.Platform), i0__namespace.ɵɵinject(i1__namespace$4.ModalController), i0__namespace.ɵɵinject(CryptoService), i0__namespace.ɵɵinject(i3__namespace$1.Storage), i0__namespace.ɵɵinject(LocalSettingsService), i0__namespace.ɵɵinject(i6__namespace.CacheService), i0__namespace.ɵɵinject(i2__namespace$2.HttpClient), i0__namespace.ɵɵinject(ENVIRONMENT), i0__namespace.ɵɵinject(i9__namespace.Network, 8), i0__namespace.ɵɵinject(i10__namespace.SplashScreen, 8), i0__namespace.ɵɵinject(i1__namespace$1.TranslateService, 8), i0__namespace.ɵɵinject(i1__namespace$4.ToastController, 8)); }, token: NetworkService, providedIn: "root" });
10950
10971
  NetworkService.decorators = [
10951
10972
  { type: i0.Injectable, args: [{ providedIn: 'root' },] }
@@ -11548,31 +11569,31 @@
11548
11569
  }());
11549
11570
 
11550
11571
  var APP_LOCAL_STORAGE_TYPE_POLICIES = new i0.InjectionToken('localStorageTypePolicies');
11551
- var EntitiesStorage = /** @class */ (function () {
11572
+ ;
11573
+ var EntitiesStorage = /** @class */ (function (_super) {
11574
+ __extends(EntitiesStorage, _super);
11552
11575
  function EntitiesStorage(platform, progressBarService, storage, environment, typePolicies) {
11553
- this.platform = platform;
11554
- this.progressBarService = progressBarService;
11555
- this.storage = storage;
11556
- this.environment = environment;
11557
- this._started = false;
11558
- this._subscription = new rxjs.Subscription();
11559
- this._stores = {};
11560
- this._$save = new i0.EventEmitter(true);
11561
- this._dirty = false;
11562
- this._saving = false;
11563
- this.onStart = new rxjs.Subject();
11564
- this._typePolicies = typePolicies || {};
11576
+ var _this = _super.call(this, platform) || this;
11577
+ _this.platform = platform;
11578
+ _this.progressBarService = progressBarService;
11579
+ _this.storage = storage;
11580
+ _this.environment = environment;
11581
+ _this._subscription = new rxjs.Subscription();
11582
+ _this._$save = new i0.EventEmitter(true);
11583
+ _this._dirty = false;
11584
+ _this._saving = false;
11585
+ _this._typePolicies = typePolicies || {};
11586
+ _this._saveTimerPeriod = environment.storageSavePeriodMs || 10000 /* = 10s */;
11587
+ _this._data = {};
11565
11588
  // For DEV only
11566
- this._debug = !environment.production;
11567
- if (this._debug)
11589
+ _this._debug = !environment.production;
11590
+ if (_this._debug)
11568
11591
  console.debug('[entities-storage] Creating service');
11592
+ return _this;
11569
11593
  }
11570
11594
  Object.defineProperty(EntitiesStorage.prototype, "dirty", {
11571
11595
  get: function () {
11572
- return this._dirty || Object.entries(this._stores).find(function (_a) {
11573
- var _b = __read(_a, 2), _ = _b[0], store = _b[1];
11574
- return store.dirty;
11575
- }) !== undefined;
11596
+ return this._dirty || Object.values(this._data).some(function (store) { return store.dirty; });
11576
11597
  },
11577
11598
  enumerable: false,
11578
11599
  configurable: true
@@ -11580,7 +11601,7 @@
11580
11601
  EntitiesStorage.prototype.watchAll = function (entityName, variables, opts) {
11581
11602
  var _this = this;
11582
11603
  // Make sure store is ready
11583
- if (!this._started) {
11604
+ if (!this.started) {
11584
11605
  return rxjs.defer(function () { return _this.ready(); })
11585
11606
  .pipe(operators.switchMap(function () { return _this.watchAll(entityName, variables, opts); })); // Loop
11586
11607
  }
@@ -11597,7 +11618,7 @@
11597
11618
  return __generator(this, function (_a) {
11598
11619
  switch (_a.label) {
11599
11620
  case 0:
11600
- if (!!this._started) return [3 /*break*/, 2];
11621
+ if (!!this.started) return [3 /*break*/, 2];
11601
11622
  return [4 /*yield*/, this.ready()];
11602
11623
  case 1:
11603
11624
  _a.sent();
@@ -11700,9 +11721,12 @@
11700
11721
  case 0:
11701
11722
  if (!entity)
11702
11723
  return [2 /*return*/]; // skip
11724
+ if (!!this.started) return [3 /*break*/, 2];
11703
11725
  return [4 /*yield*/, this.ready()];
11704
11726
  case 1:
11705
11727
  _a.sent();
11728
+ _a.label = 2;
11729
+ case 2:
11706
11730
  try {
11707
11731
  this.progressBarService.increase();
11708
11732
  this._dirty = true;
@@ -11729,9 +11753,12 @@
11729
11753
  case 0:
11730
11754
  if (isEmptyArray(entities) && (!opts || opts.reset !== true))
11731
11755
  return [2 /*return*/, entities]; // Skip (nothing to save)
11756
+ if (!!this.started) return [3 /*break*/, 2];
11732
11757
  return [4 /*yield*/, this.ready()];
11733
11758
  case 1:
11734
11759
  _a.sent();
11760
+ _a.label = 2;
11761
+ case 2:
11735
11762
  try {
11736
11763
  this.progressBarService.increase();
11737
11764
  this._dirty = true;
@@ -11753,10 +11780,12 @@
11753
11780
  case 0:
11754
11781
  if (!entity)
11755
11782
  return [2 /*return*/, undefined]; // skip
11783
+ if (!!this.started) return [3 /*break*/, 2];
11756
11784
  return [4 /*yield*/, this.ready()];
11757
11785
  case 1:
11758
11786
  _a.sent();
11759
- return [2 /*return*/, this.deleteById(entity.id, Object.assign(Object.assign({}, opts), { entityName: opts && opts.entityName || this.detectEntityName(entity) }))];
11787
+ _a.label = 2;
11788
+ case 2: return [2 /*return*/, this.deleteById(entity.id, Object.assign(Object.assign({}, opts), { entityName: opts && opts.entityName || this.detectEntityName(entity) }))];
11760
11789
  }
11761
11790
  });
11762
11791
  });
@@ -11766,9 +11795,13 @@
11766
11795
  var entityStore, deletedEntity;
11767
11796
  return __generator(this, function (_a) {
11768
11797
  switch (_a.label) {
11769
- case 0: return [4 /*yield*/, this.ready()];
11798
+ case 0:
11799
+ if (!!this.started) return [3 /*break*/, 2];
11800
+ return [4 /*yield*/, this.ready()];
11770
11801
  case 1:
11771
11802
  _a.sent();
11803
+ _a.label = 2;
11804
+ case 2:
11772
11805
  if (!opts || isNilOrBlank(opts.entityName))
11773
11806
  throw new Error('Missing argument \'opts\' or \'entityName\'');
11774
11807
  //if (id >= 0) throw new Error('Invalid id a local entity (not a negative number): ' + id);
@@ -11800,9 +11833,13 @@
11800
11833
  var entityStore, deletedEntities;
11801
11834
  return __generator(this, function (_a) {
11802
11835
  switch (_a.label) {
11803
- case 0: return [4 /*yield*/, this.ready()];
11836
+ case 0:
11837
+ if (!!this.started) return [3 /*break*/, 2];
11838
+ return [4 /*yield*/, this.ready()];
11804
11839
  case 1:
11805
11840
  _a.sent();
11841
+ _a.label = 2;
11842
+ case 2:
11806
11843
  if (!opts || isNilOrBlank(opts.entityName))
11807
11844
  throw new Error('Missing argument \'opts\' or \'opts.entityName\'');
11808
11845
  try {
@@ -11835,10 +11872,12 @@
11835
11872
  case 0:
11836
11873
  if (!entity)
11837
11874
  return [2 /*return*/, undefined]; // skip
11875
+ if (!!this.started) return [3 /*break*/, 2];
11838
11876
  return [4 /*yield*/, this.ready()];
11839
11877
  case 1:
11840
11878
  _a.sent();
11841
- return [2 /*return*/, this.deleteFromTrashById(entity.id, Object.assign(Object.assign({}, opts), { entityName: opts && opts.entityName || this.detectEntityName(entity) }))];
11879
+ _a.label = 2;
11880
+ case 2: return [2 /*return*/, this.deleteFromTrashById(entity.id, Object.assign(Object.assign({}, opts), { entityName: opts && opts.entityName || this.detectEntityName(entity) }))];
11842
11881
  }
11843
11882
  });
11844
11883
  });
@@ -11865,9 +11904,12 @@
11865
11904
  case 0:
11866
11905
  if (!entity)
11867
11906
  return [2 /*return*/, undefined]; // skip
11907
+ if (!!this.started) return [3 /*break*/, 2];
11868
11908
  return [4 /*yield*/, this.ready()];
11869
11909
  case 1:
11870
11910
  _a.sent();
11911
+ _a.label = 2;
11912
+ case 2:
11871
11913
  entityName = opts && opts.entityName || this.detectEntityName(entity);
11872
11914
  entityStore = this.getEntityStore(entityName);
11873
11915
  if (entityStore)
@@ -11887,9 +11929,13 @@
11887
11929
  var entityStore, deletedEntities, trashName;
11888
11930
  return __generator(this, function (_a) {
11889
11931
  switch (_a.label) {
11890
- case 0: return [4 /*yield*/, this.ready()];
11932
+ case 0:
11933
+ if (!!this.started) return [3 /*break*/, 2];
11934
+ return [4 /*yield*/, this.ready()];
11891
11935
  case 1:
11892
11936
  _a.sent();
11937
+ _a.label = 2;
11938
+ case 2:
11893
11939
  if (!opts || isNilOrBlank(opts.entityName))
11894
11940
  throw new Error('Missing argument \'opts.entityName\'');
11895
11941
  entityStore = this.getEntityStore(opts.entityName, { create: false });
@@ -11921,9 +11967,12 @@
11921
11967
  case 0:
11922
11968
  if (!entity)
11923
11969
  return [2 /*return*/, undefined]; // skip
11970
+ if (!!this.started) return [3 /*break*/, 2];
11924
11971
  return [4 /*yield*/, this.ready()];
11925
11972
  case 1:
11926
11973
  _a.sent();
11974
+ _a.label = 2;
11975
+ case 2:
11927
11976
  entityName = opts && opts.entityName || this.detectEntityName(entity);
11928
11977
  trashName = EntitiesStorage.TRASH_PREFIX + entityName;
11929
11978
  this.getEntityStore(trashName).save(entity, opts);
@@ -11938,9 +11987,13 @@
11938
11987
  var trashName, entityStore;
11939
11988
  return __generator(this, function (_a) {
11940
11989
  switch (_a.label) {
11941
- case 0: return [4 /*yield*/, this.ready()];
11990
+ case 0:
11991
+ if (!!this.started) return [3 /*break*/, 2];
11992
+ return [4 /*yield*/, this.ready()];
11942
11993
  case 1:
11943
11994
  _a.sent();
11995
+ _a.label = 2;
11996
+ case 2:
11944
11997
  trashName = EntitiesStorage.TRASH_PREFIX + entityName;
11945
11998
  entityStore = this.getEntityStore(trashName, { create: false });
11946
11999
  if (!entityStore)
@@ -11959,79 +12012,58 @@
11959
12012
  }
11960
12013
  return Promise.resolve();
11961
12014
  };
11962
- EntitiesStorage.prototype.ready = function () {
11963
- if (this._started)
11964
- return Promise.resolve();
11965
- return this.start();
11966
- };
11967
- EntitiesStorage.prototype.start = function () {
11968
- var _this = this;
11969
- if (this._startPromise)
11970
- return this._startPromise;
11971
- if (this._started)
11972
- return Promise.resolve();
11973
- var now = Date.now();
11974
- console.info("[entities-storage] Starting entity storage...");
11975
- // Restore sequences
11976
- this._startPromise = this.restoreLocally()
11977
- .then(function () {
11978
- // Start a save timer
11979
- _this._subscription.add(rxjs.merge(_this._$save, rxjs.timer(2000, 10000))
11980
- .pipe(operators.throttleTime(10000))
11981
- .subscribe(function () { return _this.storeLocally(); }));
11982
- _this._started = true;
11983
- _this._startPromise = undefined;
11984
- console.info("[entities-storage] Starting [OK] in " + (Date.now() - now) + "ms");
11985
- // Emit event
11986
- _this.onStart.next();
11987
- });
11988
- return this._startPromise;
11989
- };
11990
- EntitiesStorage.prototype.stop = function () {
12015
+ EntitiesStorage.prototype.ngOnStart = function () {
11991
12016
  return __awaiter(this, void 0, void 0, function () {
12017
+ var now;
12018
+ var _this = this;
11992
12019
  return __generator(this, function (_a) {
11993
12020
  switch (_a.label) {
11994
12021
  case 0:
11995
- this._started = false;
11996
- this._subscription.unsubscribe();
11997
- this._subscription = new rxjs.Subscription();
11998
- if (!this.dirty) return [3 /*break*/, 2];
11999
- return [4 /*yield*/, this.storeLocally()];
12022
+ now = Date.now();
12023
+ console.info("[entities-storage] Starting entity storage...");
12024
+ // Restore sequences
12025
+ return [4 /*yield*/, this.restoreLocally()];
12000
12026
  case 1:
12027
+ // Restore sequences
12001
12028
  _a.sent();
12002
- _a.label = 2;
12003
- case 2: return [2 /*return*/];
12029
+ // Start a save timer
12030
+ this._subscription.add(rxjs.merge(this._$save, rxjs.timer(this._saveTimerPeriod, this._saveTimerPeriod))
12031
+ .pipe(
12032
+ // Avoid to many call (e.g. when $save AND timer are triggered
12033
+ operators.throttleTime(this._saveTimerPeriod))
12034
+ .subscribe(function () { return _this.storeLocally(); }));
12035
+ console.info("[entities-storage] Starting [OK] in " + (Date.now() - now) + "ms");
12036
+ return [2 /*return*/, this._data];
12004
12037
  }
12005
12038
  });
12006
12039
  });
12007
12040
  };
12008
- EntitiesStorage.prototype.restart = function () {
12041
+ EntitiesStorage.prototype.ngOnStop = function () {
12009
12042
  return __awaiter(this, void 0, void 0, function () {
12010
12043
  return __generator(this, function (_a) {
12011
12044
  switch (_a.label) {
12012
12045
  case 0:
12013
- if (!this._started) return [3 /*break*/, 2];
12014
- return [4 /*yield*/, this.stop()];
12046
+ this._subscription.unsubscribe();
12047
+ this._subscription = new rxjs.Subscription();
12048
+ if (!this.dirty) return [3 /*break*/, 2];
12049
+ return [4 /*yield*/, this.storeLocally()];
12015
12050
  case 1:
12016
12051
  _a.sent();
12017
12052
  _a.label = 2;
12018
- case 2: return [4 /*yield*/, this.start()];
12019
- case 3:
12020
- _a.sent();
12021
- return [2 /*return*/];
12053
+ case 2: return [2 /*return*/];
12022
12054
  }
12023
12055
  });
12024
12056
  });
12025
12057
  };
12026
12058
  /* -- protected methods -- */
12027
12059
  EntitiesStorage.prototype.getEntityStore = function (name, opts) {
12028
- var store = this._stores[name];
12060
+ var store = this._data[name];
12029
12061
  if (!store && (!opts || opts.create !== false)) {
12030
12062
  if (this._debug)
12031
12063
  console.debug("[entities-storage] Creating store " + name);
12032
12064
  var typePolicy = this._typePolicies[name];
12033
12065
  store = new EntityStore(name, this.storage, typePolicy);
12034
- this._stores[name] = store;
12066
+ this._data[name] = store;
12035
12067
  }
12036
12068
  return store;
12037
12069
  };
@@ -12088,11 +12120,11 @@
12088
12120
  this._saving = true;
12089
12121
  this._dirty = false;
12090
12122
  this.progressBarService.increase();
12091
- entityNames = this._stores && Object.keys(this._stores) || [];
12123
+ entityNames = this._data && Object.keys(this._data) || [];
12092
12124
  now = Date.now();
12093
12125
  if (this._debug)
12094
12126
  console.debug('[entities-storage] Persisting...');
12095
- return [2 /*return*/, rxjs.concat.apply(void 0, __spread(entityNames.map(function (entityName) { return rxjs.defer(function () {
12127
+ return [2 /*return*/, chainPromises(entityNames.map(function (entityName) { return function () {
12096
12128
  currentEntityName = entityName;
12097
12129
  var entityStore = _this.getEntityStore(entityName, { create: false });
12098
12130
  if (!entityStore) {
@@ -12106,17 +12138,20 @@
12106
12138
  entityNames.splice(entityNames.findIndex(function (e) { return e === entityName; }), 1);
12107
12139
  }
12108
12140
  });
12109
- }); }), [rxjs.defer(function () {
12110
- currentEntityName = undefined;
12111
- return isEmptyArray(entityNames) ?
12112
- _this.storage.remove(ENTITIES_STORAGE_KEY_PREFIX) :
12113
- _this.storage.set(ENTITIES_STORAGE_KEY_PREFIX, entityNames);
12114
- }), rxjs.defer(function () {
12115
- if (_this._debug)
12116
- console.debug("[entities-storage] Persisting [OK] " + entityNames.length + " stores saved in " + (Date.now() - now) + "ms...");
12117
- _this._saving = false;
12118
- _this.progressBarService.decrease();
12119
- })])).pipe(operators.catchError(function (err) {
12141
+ }; }))
12142
+ .then(function () {
12143
+ currentEntityName = undefined;
12144
+ return isEmptyArray(entityNames) ?
12145
+ _this.storage.remove(ENTITIES_STORAGE_KEY_PREFIX) :
12146
+ _this.storage.set(ENTITIES_STORAGE_KEY_PREFIX, entityNames);
12147
+ })
12148
+ .then(function () {
12149
+ if (_this._debug)
12150
+ console.debug("[entities-storage] Persisting [OK] " + entityNames.length + " stores saved in " + (Date.now() - now) + "ms...");
12151
+ _this._saving = false;
12152
+ _this.progressBarService.decrease();
12153
+ })
12154
+ .catch(function (err) {
12120
12155
  _this._saving = false;
12121
12156
  _this.progressBarService.decrease();
12122
12157
  if (currentEntityName) {
@@ -12125,13 +12160,13 @@
12125
12160
  else {
12126
12161
  console.error("[entities-storage] Error while persisting: " + (err && err.message || err), err);
12127
12162
  }
12128
- return err;
12129
- })).toPromise()];
12163
+ throw err;
12164
+ })];
12130
12165
  });
12131
12166
  });
12132
12167
  };
12133
12168
  return EntitiesStorage;
12134
- }());
12169
+ }(StartableService));
12135
12170
  EntitiesStorage.TRASH_PREFIX = 'Trash#';
12136
12171
  EntitiesStorage.REMOTE_PREFIX = 'Remote#';
12137
12172
  EntitiesStorage.ɵprov = i0__namespace.ɵɵdefineInjectable({ factory: function EntitiesStorage_Factory() { return new EntitiesStorage(i0__namespace.ɵɵinject(i1__namespace$4.Platform), i0__namespace.ɵɵinject(ProgressBarService), i0__namespace.ɵɵinject(i3__namespace$1.Storage), i0__namespace.ɵɵinject(ENVIRONMENT), i0__namespace.ɵɵinject(APP_LOCAL_STORAGE_TYPE_POLICIES, 8)); }, token: EntitiesStorage, providedIn: "root" });
@@ -12142,7 +12177,7 @@
12142
12177
  { type: i1$5.Platform },
12143
12178
  { type: ProgressBarService },
12144
12179
  { type: i3$2.Storage },
12145
- { type: undefined, decorators: [{ type: i0.Inject, args: [ENVIRONMENT,] }] },
12180
+ { type: Environment, decorators: [{ type: i0.Inject, args: [ENVIRONMENT,] }] },
12146
12181
  { type: undefined, decorators: [{ type: i0.Optional }, { type: i0.Inject, args: [APP_LOCAL_STORAGE_TYPE_POLICIES,] }] }
12147
12182
  ]; };
12148
12183
 
@@ -12667,29 +12702,28 @@
12667
12702
  }
12668
12703
 
12669
12704
  var APP_GRAPHQL_TYPE_POLICIES = new i0.InjectionToken('graphqlTypePolicies');
12670
- var GraphqlService = /** @class */ (function () {
12705
+ var GraphqlService = /** @class */ (function (_super) {
12706
+ __extends(GraphqlService, _super);
12671
12707
  function GraphqlService(platform, apollo, httpLink, network, storage, cryptoService, environment, typePolicies) {
12672
- var _this = this;
12673
- this.platform = platform;
12674
- this.apollo = apollo;
12675
- this.httpLink = httpLink;
12676
- this.network = network;
12677
- this.storage = storage;
12678
- this.cryptoService = cryptoService;
12679
- this.environment = environment;
12680
- this.typePolicies = typePolicies;
12681
- this._started = false;
12682
- this._subscription = new rxjs.Subscription();
12683
- this.connectionParams = {};
12684
- this.onNetworkError = new rxjs.Subject();
12685
- this.customErrors = {};
12686
- this.onStart = new rxjs.Subject();
12687
- this._debug = !environment.production;
12688
- this._defaultFetchPolicy = environment.apolloFetchPolicy;
12708
+ var _this = _super.call(this, network) || this;
12709
+ _this.platform = platform;
12710
+ _this.apollo = apollo;
12711
+ _this.httpLink = httpLink;
12712
+ _this.network = network;
12713
+ _this.storage = storage;
12714
+ _this.cryptoService = cryptoService;
12715
+ _this.environment = environment;
12716
+ _this.typePolicies = typePolicies;
12717
+ _this._subscription = new rxjs.Subscription();
12718
+ _this.connectionParams = {};
12719
+ _this.onNetworkError = new rxjs.Subject();
12720
+ _this.customErrors = {};
12721
+ _this._debug = !environment.production;
12722
+ _this._defaultFetchPolicy = environment.apolloFetchPolicy;
12689
12723
  // Restart if network restart
12690
- this.network.on('start', function () { return _this.restart(); });
12724
+ _this.network.on('start', function () { return _this.restart(); });
12691
12725
  // Clear cache
12692
- this.network.on('resetCache', function () { return __awaiter(_this, void 0, void 0, function () {
12726
+ _this.network.on('resetCache', function () { return __awaiter(_this, void 0, void 0, function () {
12693
12727
  return __generator(this, function (_a) {
12694
12728
  switch (_a.label) {
12695
12729
  case 0: return [4 /*yield*/, this.ready()];
@@ -12703,23 +12737,17 @@
12703
12737
  });
12704
12738
  }); });
12705
12739
  // Listen network status
12706
- this._networkStatusChanged$ = network.onNetworkStatusChanges
12740
+ _this._networkStatusChanged$ = network.onNetworkStatusChanges
12707
12741
  .pipe(operators.filter(isNotNil), operators.distinctUntilChanged());
12708
12742
  // When getting network error: try to ping peer, and toggle to offline
12709
- this.onNetworkError
12743
+ _this.onNetworkError
12710
12744
  .pipe(operators.throttleTime(300), operators.filter(function () { return _this.network.online; }), operators.mergeMap(function () { return _this.network.checkPeerAlive(); }), operators.filter(function (alive) { return !alive; }))
12711
12745
  .subscribe(function () { return _this.network.setForceOffline(true, { showToast: true }); });
12746
+ return _this;
12712
12747
  }
12713
- Object.defineProperty(GraphqlService.prototype, "started", {
12714
- get: function () {
12715
- return this._started;
12716
- },
12717
- enumerable: false,
12718
- configurable: true
12719
- });
12720
12748
  Object.defineProperty(GraphqlService.prototype, "client", {
12721
12749
  get: function () {
12722
- return this.apollo.client;
12750
+ return this._data;
12723
12751
  },
12724
12752
  enumerable: false,
12725
12753
  configurable: true
@@ -12738,34 +12766,6 @@
12738
12766
  enumerable: false,
12739
12767
  configurable: true
12740
12768
  });
12741
- GraphqlService.prototype.ready = function () {
12742
- if (this._started)
12743
- return Promise.resolve();
12744
- return this.start();
12745
- };
12746
- GraphqlService.prototype.start = function () {
12747
- var _this = this;
12748
- if (this._startPromise)
12749
- return this._startPromise;
12750
- if (this._started)
12751
- return Promise.resolve();
12752
- console.info('[graphql] Starting graphql...');
12753
- // Waiting for network service
12754
- this._startPromise = this.network.ready()
12755
- .then(function () { return _this.initApollo(); })
12756
- .then(function () {
12757
- _this._started = true;
12758
- _this._startPromise = undefined;
12759
- // Emit event
12760
- _this.onStart.next();
12761
- console.info('[graphql] Starting graphql [OK]');
12762
- })
12763
- .catch(function (err) {
12764
- console.error(err && err.message || err, err);
12765
- _this._startPromise = undefined;
12766
- });
12767
- return this._startPromise;
12768
- };
12769
12769
  GraphqlService.prototype.setAuthToken = function (token) {
12770
12770
  if (token) {
12771
12771
  console.debug('[graphql] Apply token authentication to headers');
@@ -12798,15 +12798,18 @@
12798
12798
  */
12799
12799
  GraphqlService.prototype.addResolver = function (resolvers) {
12800
12800
  return __awaiter(this, void 0, void 0, function () {
12801
- var client;
12802
- var _this = this;
12803
12801
  return __generator(this, function (_a) {
12804
- if (!this._started) {
12805
- this.onStart.toPromise().then(function () { return _this.addResolver(resolvers); }); // Loop
12802
+ switch (_a.label) {
12803
+ case 0:
12804
+ if (!!this.started) return [3 /*break*/, 2];
12805
+ return [4 /*yield*/, this.ready()];
12806
+ case 1:
12807
+ _a.sent();
12808
+ _a.label = 2;
12809
+ case 2:
12810
+ this.apollo.client.addResolvers(resolvers);
12811
+ return [2 /*return*/];
12806
12812
  }
12807
- client = this.apollo.getClient();
12808
- client.addResolvers(resolvers);
12809
- return [2 /*return*/];
12810
12813
  });
12811
12814
  });
12812
12815
  };
@@ -12816,21 +12819,26 @@
12816
12819
  return __generator(this, function (_a) {
12817
12820
  switch (_a.label) {
12818
12821
  case 0:
12819
- _a.trys.push([0, 3, , 4]);
12820
- return [4 /*yield*/, this.getApollo()];
12821
- case 1: return [4 /*yield*/, (_a.sent()).query({
12822
- query: opts.query,
12823
- variables: opts.variables,
12824
- fetchPolicy: opts.fetchPolicy || this._defaultFetchPolicy || undefined
12825
- }).toPromise()];
12822
+ if (!!this.started) return [3 /*break*/, 2];
12823
+ return [4 /*yield*/, this.ready()];
12824
+ case 1:
12825
+ _a.sent();
12826
+ _a.label = 2;
12826
12827
  case 2:
12827
- res = _a.sent();
12828
- return [3 /*break*/, 4];
12828
+ _a.trys.push([2, 4, , 5]);
12829
+ return [4 /*yield*/, this.client.query({
12830
+ query: opts.query,
12831
+ variables: opts.variables,
12832
+ fetchPolicy: opts.fetchPolicy || this._defaultFetchPolicy || undefined
12833
+ })];
12829
12834
  case 3:
12835
+ res = _a.sent();
12836
+ return [3 /*break*/, 5];
12837
+ case 4:
12830
12838
  err_1 = _a.sent();
12831
12839
  res = this.toApolloError(err_1, opts.error);
12832
- return [3 /*break*/, 4];
12833
- case 4:
12840
+ return [3 /*break*/, 5];
12841
+ case 5:
12834
12842
  if (res.errors) {
12835
12843
  throw res.errors[0];
12836
12844
  }
@@ -13207,13 +13215,14 @@
13207
13215
  this.customErrors = Object.assign(Object.assign({}, this.customErrors), error);
13208
13216
  };
13209
13217
  /* -- protected methods -- */
13210
- GraphqlService.prototype.initApollo = function () {
13218
+ GraphqlService.prototype.ngOnStart = function () {
13211
13219
  return __awaiter(this, void 0, void 0, function () {
13212
13220
  var mobile, enableTrackMutationQueries, peer, uri, wsUri, storage, client, wsLink, retryLink, authLink, httpLink, cache, mutationLinks, serializingLink, trackerLink, queueLink_1, err_2;
13213
13221
  var _this = this;
13214
13222
  return __generator(this, function (_a) {
13215
13223
  switch (_a.label) {
13216
13224
  case 0:
13225
+ console.info('[graphql] Starting graphql...');
13217
13226
  mobile = this.platform.is('mobile') || this.platform.is('mobileweb');
13218
13227
  enableTrackMutationQueries = !mobile;
13219
13228
  peer = this.network.peer;
@@ -13284,9 +13293,8 @@
13284
13293
  queueLink_1 = new QueueLink__default["default"]();
13285
13294
  this._subscription.add(this._networkStatusChanged$
13286
13295
  .subscribe(function (type) {
13287
- var offline = type === 'none';
13288
13296
  // Network is offline: start buffering into queue
13289
- if (offline) {
13297
+ if (type === 'none') {
13290
13298
  console.info('[graphql] offline mode: enable mutations buffer');
13291
13299
  queueLink_1.close();
13292
13300
  }
@@ -13349,12 +13357,14 @@
13349
13357
  err_2 = _a.sent();
13350
13358
  console.error('[graphql] Failed to restore tracked queries from storage: ' + (err_2 && err_2.message || err_2), err_2);
13351
13359
  return [3 /*break*/, 7];
13352
- case 7: return [2 /*return*/];
13360
+ case 7:
13361
+ console.info('[graphql] Starting graphql [OK]');
13362
+ return [2 /*return*/, client];
13353
13363
  }
13354
13364
  });
13355
13365
  });
13356
13366
  };
13357
- GraphqlService.prototype.stop = function () {
13367
+ GraphqlService.prototype.ngOnStop = function () {
13358
13368
  return __awaiter(this, void 0, void 0, function () {
13359
13369
  return __generator(this, function (_a) {
13360
13370
  switch (_a.label) {
@@ -13365,24 +13375,11 @@
13365
13375
  return [4 /*yield*/, this.resetClient()];
13366
13376
  case 1:
13367
13377
  _a.sent();
13368
- this._started = false;
13369
- this._startPromise = undefined;
13370
13378
  return [2 /*return*/];
13371
13379
  }
13372
13380
  });
13373
13381
  });
13374
13382
  };
13375
- GraphqlService.prototype.restart = function () {
13376
- return __awaiter(this, void 0, void 0, function () {
13377
- var _this = this;
13378
- return __generator(this, function (_a) {
13379
- if (this.started) {
13380
- return [2 /*return*/, this.stop().then(function () { return _this.start(); })];
13381
- }
13382
- return [2 /*return*/, this.start()];
13383
- });
13384
- });
13385
- };
13386
13383
  GraphqlService.prototype.resetClient = function (client) {
13387
13384
  return __awaiter(this, void 0, void 0, function () {
13388
13385
  return __generator(this, function (_a) {
@@ -13484,24 +13481,8 @@
13484
13481
  }
13485
13482
  return undefined;
13486
13483
  };
13487
- GraphqlService.prototype.getApollo = function () {
13488
- return __awaiter(this, void 0, void 0, function () {
13489
- return __generator(this, function (_a) {
13490
- switch (_a.label) {
13491
- case 0:
13492
- if (!!this._started) return [3 /*break*/, 2];
13493
- console.debug('[graphql] Waiting apollo client... ');
13494
- return [4 /*yield*/, this.onStart.toPromise()];
13495
- case 1:
13496
- _a.sent();
13497
- _a.label = 2;
13498
- case 2: return [2 /*return*/, this.apollo];
13499
- }
13500
- });
13501
- });
13502
- };
13503
13484
  return GraphqlService;
13504
- }());
13485
+ }(StartableService));
13505
13486
  GraphqlService.ɵprov = i0__namespace.ɵɵdefineInjectable({ factory: function GraphqlService_Factory() { return new GraphqlService(i0__namespace.ɵɵinject(i1__namespace$4.Platform), i0__namespace.ɵɵinject(i2__namespace$3.Apollo), i0__namespace.ɵɵinject(i3__namespace$2.HttpLink), i0__namespace.ɵɵinject(NetworkService), i0__namespace.ɵɵinject(i3__namespace$1.Storage), i0__namespace.ɵɵinject(CryptoService), i0__namespace.ɵɵinject(ENVIRONMENT), i0__namespace.ɵɵinject(APP_GRAPHQL_TYPE_POLICIES, 8)); }, token: GraphqlService, providedIn: "root" });
13506
13487
  GraphqlService.decorators = [
13507
13488
  { type: i0.Injectable, args: [{
@@ -13784,7 +13765,12 @@
13784
13765
  _this.storage = storage;
13785
13766
  _this.file = file;
13786
13767
  _this.environment = environment;
13787
- _this.data = {
13768
+ _this.onLogin = new rxjs.Subject();
13769
+ _this.onLogout = new rxjs.Subject();
13770
+ _this.onChange = new rxjs.Subject();
13771
+ _this.onAuthTokenChange = new rxjs.Subject();
13772
+ _this.onAuthBasicChange = new rxjs.Subject();
13773
+ _this._data = {
13788
13774
  loaded: false,
13789
13775
  keypair: null,
13790
13776
  authToken: null,
@@ -13798,11 +13784,6 @@
13798
13784
  _this._started = false;
13799
13785
  _this._$additionalFields = new rxjs.BehaviorSubject([]);
13800
13786
  _this._tokenType$ = new rxjs.BehaviorSubject(undefined);
13801
- _this.onLogin = new rxjs.Subject();
13802
- _this.onLogout = new rxjs.Subject();
13803
- _this.onChange = new rxjs.Subject();
13804
- _this.onAuthTokenChange = new rxjs.Subject();
13805
- _this.onAuthBasicChange = new rxjs.Subject();
13806
13787
  _this._debug = !environment.production;
13807
13788
  if (_this._debug)
13808
13789
  console.debug('[account-service] Creating service');
@@ -13810,7 +13791,7 @@
13810
13791
  // Send auth token to the graphql layer, when changed
13811
13792
  _this.onAuthTokenChange.subscribe(function (token) { return _this.graphql.setAuthToken(token); });
13812
13793
  _this.onAuthBasicChange.subscribe(function (basic) { return _this.graphql.setAuthBasic(basic); });
13813
- // Listen network restart
13794
+ // Listen graphql start (or restart)
13814
13795
  _this.graphql.onStart.subscribe(function () { return __awaiter(_this, void 0, void 0, function () {
13815
13796
  return __generator(this, function (_b) {
13816
13797
  if (!this._started) {
@@ -13844,27 +13825,27 @@
13844
13825
  }
13845
13826
  Object.defineProperty(AccountService.prototype, "account", {
13846
13827
  get: function () {
13847
- return this.data.loaded ? this.data.account : undefined;
13828
+ return this._data.loaded ? this._data.account : undefined;
13848
13829
  },
13849
13830
  enumerable: false,
13850
13831
  configurable: true
13851
13832
  });
13852
13833
  Object.defineProperty(AccountService.prototype, "person", {
13853
13834
  get: function () {
13854
- if (this.data.loaded && !this.data.person) {
13855
- this.data.person = this.data.loaded ? this.data.account.asPerson() : undefined;
13835
+ if (this._data.loaded && !this._data.person) {
13836
+ this._data.person = this._data.loaded ? this._data.account.asPerson() : undefined;
13856
13837
  }
13857
- return this.data.person;
13838
+ return this._data.person;
13858
13839
  },
13859
13840
  enumerable: false,
13860
13841
  configurable: true
13861
13842
  });
13862
13843
  Object.defineProperty(AccountService.prototype, "department", {
13863
13844
  get: function () {
13864
- if (this.data.loaded && !this.data.department) {
13865
- this.data.department = this.data.loaded ? this.data.account.asPerson().department : undefined;
13845
+ if (this._data.loaded && !this._data.department) {
13846
+ this._data.department = this._data.loaded ? this._data.account.asPerson().department : undefined;
13866
13847
  }
13867
- return this.data.department;
13848
+ return this._data.department;
13868
13849
  },
13869
13850
  enumerable: false,
13870
13851
  configurable: true
@@ -13878,32 +13859,21 @@
13878
13859
  console.info('[account] Using authentication token type: ' + value);
13879
13860
  this._tokenType$.next(value);
13880
13861
  // Reset values
13881
- this.data.authToken = undefined;
13862
+ this._data.authToken = undefined;
13882
13863
  this.onAuthTokenChange.next(undefined);
13883
- this.data.authBasic = undefined;
13864
+ this._data.authBasic = undefined;
13884
13865
  this.onAuthBasicChange.next(undefined);
13885
13866
  }
13886
13867
  },
13887
13868
  enumerable: false,
13888
13869
  configurable: true
13889
13870
  });
13890
- AccountService.prototype.resetData = function () {
13891
- this.data.loaded = false;
13892
- this.data.keypair = null;
13893
- this.data.authToken = null;
13894
- this.data.authBasic = null;
13895
- this.data.pubkey = null;
13896
- this.data.mainProfile = null;
13897
- this.data.account = new exports.Account();
13898
- this.data.person = null;
13899
- this.data.department = null;
13900
- };
13901
13871
  AccountService.prototype.start = function () {
13902
13872
  var _this = this;
13903
13873
  if (this._startPromise)
13904
13874
  return this._startPromise;
13905
13875
  if (this._started)
13906
- return Promise.resolve();
13876
+ return Promise.resolve(this.account);
13907
13877
  // Restoring local settings
13908
13878
  this._startPromise = Promise.all([
13909
13879
  this.settings.ready(),
@@ -13914,6 +13884,7 @@
13914
13884
  .then(function () {
13915
13885
  _this._started = true;
13916
13886
  _this._startPromise = undefined;
13887
+ return _this.account;
13917
13888
  });
13918
13889
  return this._startPromise;
13919
13890
  };
@@ -13931,8 +13902,8 @@
13931
13902
  if (this._started || this._startPromise) {
13932
13903
  this._started = false;
13933
13904
  this._startPromise = undefined;
13934
- hadAuthToken = this.data.authToken && true;
13935
- hadAuthBasic = this.data.authBasic && true;
13905
+ hadAuthToken = this._data.authToken && true;
13906
+ hadAuthBasic = this._data.authBasic && true;
13936
13907
  hadAuth = hadAuthToken || hadAuthBasic;
13937
13908
  this.resetData();
13938
13909
  if (hadAuth) {
@@ -13962,35 +13933,35 @@
13962
13933
  };
13963
13934
  AccountService.prototype.ready = function () {
13964
13935
  if (this._started)
13965
- return Promise.resolve();
13936
+ return Promise.resolve(this.account);
13966
13937
  return this.start();
13967
13938
  };
13968
13939
  AccountService.prototype.isLogin = function () {
13969
- return !!(this.data.pubkey && this.data.loaded);
13940
+ return !!(this._data.pubkey && this._data.loaded);
13970
13941
  };
13971
13942
  AccountService.prototype.isAuth = function () {
13972
- return !!(this.data.pubkey && this.data.keypair && this.data.keypair.secretKey);
13943
+ return !!(this._data.pubkey && this._data.keypair && this._data.keypair.secretKey);
13973
13944
  };
13974
13945
  AccountService.prototype.hasMinProfile = function (userProfile) {
13975
13946
  // should be login, and status ENABLE or TEMPORARY
13976
- if (!this.data.account || !this.data.account.pubkey ||
13977
- (this.data.account.statusId !== StatusIds.ENABLE && this.data.account.statusId !== StatusIds.TEMPORARY)) {
13947
+ if (!this._data.account || !this._data.account.pubkey ||
13948
+ (this._data.account.statusId !== StatusIds.ENABLE && this._data.account.statusId !== StatusIds.TEMPORARY)) {
13978
13949
  return false;
13979
13950
  }
13980
- return PersonUtils.hasUpperOrEqualsProfile(this.data.account.profiles, userProfile);
13951
+ return PersonUtils.hasUpperOrEqualsProfile(this._data.account.profiles, userProfile);
13981
13952
  };
13982
13953
  AccountService.prototype.hasExactProfile = function (label) {
13983
13954
  // should be login, and status ENABLE or TEMPORARY
13984
- if (!this.data.account || !this.data.account.pubkey ||
13985
- (this.data.account.statusId !== StatusIds.ENABLE && this.data.account.statusId !== StatusIds.TEMPORARY))
13955
+ if (!this._data.account || !this._data.account.pubkey ||
13956
+ (this._data.account.statusId !== StatusIds.ENABLE && this._data.account.statusId !== StatusIds.TEMPORARY))
13986
13957
  return false;
13987
- return this.data.account.profiles.some(function (profile) { return profile === label; });
13958
+ return this._data.account.profiles.some(function (profile) { return profile === label; });
13988
13959
  };
13989
13960
  AccountService.prototype.hasProfileAndIsEnable = function (userProfile) {
13990
13961
  // should be login, and status ENABLE
13991
- if (!this.data.account || !this.data.account.pubkey || this.data.account.statusId !== StatusIds.ENABLE)
13962
+ if (!this._data.account || !this._data.account.pubkey || this._data.account.statusId !== StatusIds.ENABLE)
13992
13963
  return false;
13993
- return PersonUtils.hasUpperOrEqualsProfile(this.data.account.profiles, userProfile);
13964
+ return PersonUtils.hasUpperOrEqualsProfile(this._data.account.profiles, userProfile);
13994
13965
  };
13995
13966
  AccountService.prototype.isAdmin = function () {
13996
13967
  return this.hasProfileAndIsEnable('ADMIN');
@@ -14010,11 +13981,11 @@
14010
13981
  };
14011
13982
  AccountService.prototype.isOnlyGuest = function () {
14012
13983
  // Should be login, and status ENABLE or TEMPORARY
14013
- if (!this.data.account || !this.data.account.pubkey ||
14014
- (this.data.account.statusId !== StatusIds.ENABLE && this.data.account.statusId !== StatusIds.TEMPORARY))
13984
+ if (!this._data.account || !this._data.account.pubkey ||
13985
+ (this._data.account.statusId !== StatusIds.ENABLE && this._data.account.statusId !== StatusIds.TEMPORARY))
14015
13986
  return false;
14016
13987
  // Profile less then user
14017
- return !PersonUtils.hasUpperOrEqualsProfile(this.data.account.profiles, 'USER');
13988
+ return !PersonUtils.hasUpperOrEqualsProfile(this._data.account.profiles, 'USER');
14018
13989
  };
14019
13990
  AccountService.prototype.canUserWriteDataForDepartment = function (recorderDepartment) {
14020
13991
  if (ReferentialUtils.isEmpty(recorderDepartment)) {
@@ -14023,17 +13994,17 @@
14023
13994
  return this.isAdmin();
14024
13995
  }
14025
13996
  // Should be login, and status ENABLE
14026
- if (!this.data.account || !this.data.account.pubkey || this.data.account.statusId !== StatusIds.ENABLE)
13997
+ if (!this._data.account || !this._data.account.pubkey || this._data.account.statusId !== StatusIds.ENABLE)
14027
13998
  return false;
14028
- if (!this.data.account.department || !this.data.account.department.id) {
13999
+ if (!this._data.account.department || !this._data.account.department.id) {
14029
14000
  console.warn('User account has no department ! Unable to check write right against recorderDepartment');
14030
14001
  return false;
14031
14002
  }
14032
14003
  // Same recorder department: OK, user can write
14033
- if (this.data.account.department.id === recorderDepartment.id)
14004
+ if (this._data.account.department.id === recorderDepartment.id)
14034
14005
  return true;
14035
14006
  // Else, check if supervisor (or more)
14036
- return PersonUtils.hasUpperOrEqualsProfile(this.data.account.profiles, 'SUPERVISOR');
14007
+ return PersonUtils.hasUpperOrEqualsProfile(this._data.account.profiles, 'SUPERVISOR');
14037
14008
  };
14038
14009
  AccountService.prototype.register = function (data) {
14039
14010
  return __awaiter(this, void 0, void 0, function () {
@@ -14048,7 +14019,7 @@
14048
14019
  throw new Error('Missing required username or password');
14049
14020
  if (this._debug)
14050
14021
  console.debug('[account] Register new user account...', data.account);
14051
- this.data.loaded = false;
14022
+ this._data.loaded = false;
14052
14023
  now = Date.now();
14053
14024
  _b.label = 1;
14054
14025
  case 1:
@@ -14062,29 +14033,29 @@
14062
14033
  data.account.settings.locale = this.settings.locale;
14063
14034
  data.account.settings.latLongFormat = this.settings.latLongFormat;
14064
14035
  data.account.department.id = data.account.department.id || this.environment.defaultDepartmentId;
14065
- this.data.keypair = keypair;
14036
+ this._data.keypair = keypair;
14066
14037
  return [4 /*yield*/, this.saveRemotely(data.account, keypair)];
14067
14038
  case 3:
14068
14039
  account = _b.sent();
14069
14040
  // Default values
14070
14041
  account.avatar = account.avatar || (this.environment.baseUrl + DEFAULT_AVATAR_IMAGE);
14071
- this.data.mainProfile = PersonUtils.getMainProfile(account.profiles);
14072
- this.data.account = account;
14073
- this.data.pubkey = account.pubkey;
14042
+ this._data.mainProfile = PersonUtils.getMainProfile(account.profiles);
14043
+ this._data.account = account;
14044
+ this._data.pubkey = account.pubkey;
14074
14045
  // Try to auth on pod
14075
14046
  return [4 /*yield*/, this.authenticate(data)];
14076
14047
  case 4:
14077
14048
  // Try to auth on pod
14078
14049
  _b.sent();
14079
- this.data.loaded = true;
14050
+ this._data.loaded = true;
14080
14051
  return [4 /*yield*/, this.saveLocally()];
14081
14052
  case 5:
14082
14053
  _b.sent();
14083
14054
  console.debug("[account] Account successfully registered in " + (Date.now() - now) + "ms");
14084
14055
  // Emit events
14085
- this.onLogin.next(this.data.account);
14086
- this.onChange.next(this.data.account);
14087
- return [2 /*return*/, this.data.account];
14056
+ this.onLogin.next(this._data.account);
14057
+ this.onChange.next(this._data.account);
14058
+ return [2 /*return*/, this._data.account];
14088
14059
  case 6:
14089
14060
  error_1 = _b.sent();
14090
14061
  console.error(error_1 && error_1.message || error_1);
@@ -14106,22 +14077,22 @@
14106
14077
  // Basic auth
14107
14078
  if (tokenType === 'basic' || tokenType === 'basic-and-token') {
14108
14079
  // Generate the authBasic, if used
14109
- if (!this.data.authBasic) {
14080
+ if (!this._data.authBasic) {
14110
14081
  // Skip if token already provided
14111
- if (!(this.data.authToken && tokenType === 'basic-and-token')) {
14082
+ if (!(this._data.authToken && tokenType === 'basic-and-token')) {
14112
14083
  if (!data || !data.username || !data.password)
14113
14084
  throw new Error('Missing username and password');
14114
- this.data.authBasic = this.cryptoService.encodeBase64(data.username + ":" + data.password);
14085
+ this._data.authBasic = this.cryptoService.encodeBase64(data.username + ":" + data.password);
14115
14086
  }
14116
14087
  }
14117
- this.onAuthBasicChange.next(this.data.authBasic);
14088
+ this.onAuthBasicChange.next(this._data.authBasic);
14118
14089
  }
14119
14090
  if (!(tokenType === 'token' || tokenType === 'basic-and-token')) return [3 /*break*/, 5];
14120
14091
  _c.label = 2;
14121
14092
  case 2:
14122
14093
  _c.trys.push([2, 4, , 5]);
14123
- _b = this.data;
14124
- return [4 /*yield*/, this.authenticateAndGetToken(this.data.authToken)];
14094
+ _b = this._data;
14095
+ return [4 /*yield*/, this.authenticateAndGetToken(this._data.authToken)];
14125
14096
  case 3:
14126
14097
  _b.authToken = _c.sent();
14127
14098
  return [3 /*break*/, 5];
@@ -14134,7 +14105,7 @@
14134
14105
  case 5:
14135
14106
  // Forget authBasic, to switch to authToken
14136
14107
  if (tokenType === 'basic-and-token') {
14137
- this.data.authBasic = undefined;
14108
+ this._data.authBasic = undefined;
14138
14109
  this.onAuthBasicChange.next(undefined);
14139
14110
  }
14140
14111
  return [2 /*return*/];
@@ -14165,18 +14136,18 @@
14165
14136
  throw { code: ErrorCodes$2.UNKNOWN_ERROR, message: 'ERROR.SCRYPT_ERROR' };
14166
14137
  case 4:
14167
14138
  // Store pubkey+keypair
14168
- this.data.pubkey = Base58.encode(keypair.publicKey);
14169
- this.data.keypair = keypair;
14139
+ this._data.pubkey = Base58.encode(keypair.publicKey);
14140
+ this._data.keypair = keypair;
14170
14141
  return [4 /*yield*/, this.storage.get(TOKEN_STORAGE_KEY)];
14171
14142
  case 5:
14172
14143
  previousToken = _b.sent();
14173
- previousToken = previousToken && previousToken.startsWith(this.data.pubkey) && previousToken || null;
14144
+ previousToken = previousToken && previousToken.startsWith(this._data.pubkey) && previousToken || null;
14174
14145
  offline = this.settings.hasOfflineFeature() && (this.network.offline || data.offline === true);
14175
14146
  if (!offline) return [3 /*break*/, 6];
14176
- this.data.authToken = previousToken;
14147
+ this._data.authToken = previousToken;
14177
14148
  // Make sure network if set as offline
14178
14149
  this.network.setForceOffline(true, { showToast: false });
14179
- console.info("[account] Login [OK] {pubkey: " + this.data.pubkey.substr(0, 8) + "}, {offline: true}");
14150
+ console.info("[account] Login [OK] {pubkey: " + this._data.pubkey.substr(0, 8) + "}, {offline: true}");
14180
14151
  return [3 /*break*/, 9];
14181
14152
  case 6:
14182
14153
  _b.trys.push([6, 8, , 9]);
@@ -14236,9 +14207,9 @@
14236
14207
  throw error_5;
14237
14208
  case 21:
14238
14209
  // Emit event to observers
14239
- this.onLogin.next(this.data.account);
14240
- this.onChange.next(this.data.account);
14241
- return [2 /*return*/, this.data.account];
14210
+ this.onLogin.next(this._data.account);
14211
+ this.onChange.next(this._data.account);
14212
+ return [2 /*return*/, this._data.account];
14242
14213
  }
14243
14214
  });
14244
14215
  });
@@ -14248,7 +14219,7 @@
14248
14219
  return __generator(this, function (_b) {
14249
14220
  switch (_b.label) {
14250
14221
  case 0:
14251
- if (!this.data.pubkey)
14222
+ if (!this._data.pubkey)
14252
14223
  throw new Error('User not logged');
14253
14224
  if (this.network.offline)
14254
14225
  throw new Error('Cannot check account in offline mode');
@@ -14260,9 +14231,9 @@
14260
14231
  _b.sent();
14261
14232
  console.debug('[account] Successfully reload account');
14262
14233
  // Emit login event to subscribers
14263
- this.onLogin.next(this.data.account);
14264
- this.onChange.next(this.data.account);
14265
- return [2 /*return*/, this.data.account];
14234
+ this.onLogin.next(this._data.account);
14235
+ this.onChange.next(this._data.account);
14236
+ return [2 /*return*/, this._data.account];
14266
14237
  }
14267
14238
  });
14268
14239
  });
@@ -14277,27 +14248,27 @@
14277
14248
  return __generator(this, function (_b) {
14278
14249
  switch (_b.label) {
14279
14250
  case 0:
14280
- if (!this.data.pubkey)
14251
+ if (!this._data.pubkey)
14281
14252
  return [2 /*return*/, Promise.reject('User not logged')];
14282
- if (this.data.pubkey !== account.pubkey)
14253
+ if (this._data.pubkey !== account.pubkey)
14283
14254
  return [2 /*return*/, Promise.reject('Not user account')];
14284
- return [4 /*yield*/, this.saveRemotely(account, this.data.keypair)];
14255
+ return [4 /*yield*/, this.saveRemotely(account, this._data.keypair)];
14285
14256
  case 1:
14286
14257
  account = _b.sent();
14287
14258
  // Set defaults
14288
14259
  account.avatar = account.avatar || (this.environment.baseUrl + DEFAULT_AVATAR_IMAGE);
14289
- this.data.mainProfile = PersonUtils.getMainProfile(account.profiles);
14290
- this.data.account = account;
14291
- this.data.loaded = true;
14260
+ this._data.mainProfile = PersonUtils.getMainProfile(account.profiles);
14261
+ this._data.account = account;
14262
+ this._data.loaded = true;
14292
14263
  // Save locally (in storage)
14293
14264
  return [4 /*yield*/, this.saveLocally()];
14294
14265
  case 2:
14295
14266
  // Save locally (in storage)
14296
14267
  _b.sent();
14297
14268
  // Send event
14298
- this.onLogin.next(this.data.account);
14299
- this.onChange.next(this.data.account);
14300
- return [2 /*return*/, this.data.account];
14269
+ this.onLogin.next(this._data.account);
14270
+ this.onChange.next(this._data.account);
14271
+ return [2 /*return*/, this._data.account];
14301
14272
  }
14302
14273
  });
14303
14274
  });
@@ -14308,9 +14279,9 @@
14308
14279
  return __generator(this, function (_b) {
14309
14280
  switch (_b.label) {
14310
14281
  case 0:
14311
- hadAuthToken = this.data.authToken && true;
14312
- hadAuthBasic = this.data.authBasic && true;
14313
- pubkey = this.data && this.data.pubkey;
14282
+ hadAuthToken = this._data.authToken && true;
14283
+ hadAuthBasic = this._data.authBasic && true;
14284
+ pubkey = this._data && this._data.pubkey;
14314
14285
  this.resetData();
14315
14286
  if (!!this.settings.hasOfflineFeature()) return [3 /*break*/, 2];
14316
14287
  // Remove all data from the local storage
@@ -14382,9 +14353,9 @@
14382
14353
  case 1:
14383
14354
  json = _b.sent();
14384
14355
  json = json && (typeof json === 'string') && JSON.parse(json) || json;
14385
- json = json && this.data.pubkey && (json.pubkey === this.data.pubkey) && json || null;
14386
- if (!(!json && this.data.pubkey)) return [3 /*break*/, 3];
14387
- return [4 /*yield*/, this.storage.get(ACCOUNT_STORAGE_KEY + '#' + this.data.pubkey)];
14356
+ json = json && this._data.pubkey && (json.pubkey === this._data.pubkey) && json || null;
14357
+ if (!(!json && this._data.pubkey)) return [3 /*break*/, 3];
14358
+ return [4 /*yield*/, this.storage.get(ACCOUNT_STORAGE_KEY + '#' + this._data.pubkey)];
14388
14359
  case 2:
14389
14360
  json = _b.sent();
14390
14361
  json = json && (typeof json === 'string') && JSON.parse(json) || json;
@@ -14515,7 +14486,8 @@
14515
14486
  });
14516
14487
  };
14517
14488
  AccountService.prototype.listenChanges = function () {
14518
- if (!this.data.pubkey)
14489
+ var _this = this;
14490
+ if (!this._data.pubkey)
14519
14491
  return rxjs.Subscription.EMPTY;
14520
14492
  var self = this;
14521
14493
  console.debug('[account] [WS] Listening account changes');
@@ -14531,15 +14503,14 @@
14531
14503
  }).subscribe({
14532
14504
  next: function (_b) {
14533
14505
  var data = _b.data;
14534
- var _a;
14535
- return __awaiter(this, void 0, void 0, function () {
14536
- var existingUpdateDate;
14506
+ return __awaiter(_this, void 0, void 0, function () {
14507
+ var _a, existingUpdateDate;
14537
14508
  return __generator(this, function (_b) {
14538
14509
  switch (_b.label) {
14539
14510
  case 0:
14540
14511
  if (!data)
14541
14512
  return [2 /*return*/];
14542
- existingUpdateDate = toDateISOString((_a = self.data.account) === null || _a === void 0 ? void 0 : _a.updateDate);
14513
+ existingUpdateDate = toDateISOString((_a = self._data.account) === null || _a === void 0 ? void 0 : _a.updateDate);
14543
14514
  if (!(existingUpdateDate !== data.updateDate)) return [3 /*break*/, 2];
14544
14515
  console.debug("[account] [WS] Detected update on {" + data.updateDate + "}");
14545
14516
  return [4 /*yield*/, self.refresh()];
@@ -14551,32 +14522,30 @@
14551
14522
  });
14552
14523
  });
14553
14524
  },
14554
- error: function (err) {
14555
- return __awaiter(this, void 0, void 0, function () {
14556
- return __generator(this, function (_b) {
14557
- switch (_b.label) {
14558
- case 0:
14559
- if (!(err && +err.code === ServerErrorCodes.NOT_FOUND)) return [3 /*break*/, 2];
14560
- console.info('[account] Account not exists anymore: force user to logout...', err);
14561
- return [4 /*yield*/, self.logout()];
14562
- case 1:
14563
- _b.sent();
14564
- return [3 /*break*/, 5];
14565
- case 2:
14566
- if (!(err && +err.code === ServerErrorCodes.UNAUTHORIZED)) return [3 /*break*/, 4];
14567
- console.info('[account] Account not authorized: force user to logout...', err);
14568
- return [4 /*yield*/, self.logout()];
14569
- case 3:
14570
- _b.sent();
14571
- return [3 /*break*/, 5];
14572
- case 4:
14573
- console.warn('[account] [WS] Received error:', err);
14574
- _b.label = 5;
14575
- case 5: return [2 /*return*/];
14576
- }
14577
- });
14525
+ error: function (err) { return __awaiter(_this, void 0, void 0, function () {
14526
+ return __generator(this, function (_b) {
14527
+ switch (_b.label) {
14528
+ case 0:
14529
+ if (!(err && +err.code === ServerErrorCodes.NOT_FOUND)) return [3 /*break*/, 2];
14530
+ console.info('[account] Account not exists anymore: force user to logout...', err);
14531
+ return [4 /*yield*/, self.logout()];
14532
+ case 1:
14533
+ _b.sent();
14534
+ return [3 /*break*/, 5];
14535
+ case 2:
14536
+ if (!(err && +err.code === ServerErrorCodes.UNAUTHORIZED)) return [3 /*break*/, 4];
14537
+ console.info('[account] Account not authorized: force user to logout...', err);
14538
+ return [4 /*yield*/, self.logout()];
14539
+ case 3:
14540
+ _b.sent();
14541
+ return [3 /*break*/, 5];
14542
+ case 4:
14543
+ console.warn('[account] [WS] Received error:', err);
14544
+ _b.label = 5;
14545
+ case 5: return [2 /*return*/];
14546
+ }
14578
14547
  });
14579
- },
14548
+ }); },
14580
14549
  complete: function () {
14581
14550
  console.debug('[account] [WS] Completed');
14582
14551
  }
@@ -14633,15 +14602,26 @@
14633
14602
  this._$additionalFields.next(values.concat(field));
14634
14603
  };
14635
14604
  /* -- protected method -- */
14605
+ AccountService.prototype.resetData = function () {
14606
+ this._data.loaded = false;
14607
+ this._data.keypair = null;
14608
+ this._data.authToken = null;
14609
+ this._data.authBasic = null;
14610
+ this._data.pubkey = null;
14611
+ this._data.mainProfile = null;
14612
+ this._data.account = new exports.Account();
14613
+ this._data.person = null;
14614
+ this._data.department = null;
14615
+ };
14636
14616
  AccountService.prototype.loadData = function (opts) {
14637
14617
  return __awaiter(this, void 0, void 0, function () {
14638
14618
  var account, error_6;
14639
14619
  return __generator(this, function (_b) {
14640
14620
  switch (_b.label) {
14641
14621
  case 0:
14642
- if (!this.data.pubkey)
14622
+ if (!this._data.pubkey)
14643
14623
  throw new Error('User not logged');
14644
- this.data.loaded = false;
14624
+ this._data.loaded = false;
14645
14625
  _b.label = 1;
14646
14626
  case 1:
14647
14627
  _b.trys.push([1, 3, , 4]);
@@ -14654,16 +14634,16 @@
14654
14634
  account.settings.locale = account.settings.locale || this.settings.locale;
14655
14635
  account.settings.latLongFormat = account.settings.latLongFormat || this.settings.latLongFormat || 'DDMM';
14656
14636
  // Read main profile
14657
- this.data.mainProfile = PersonUtils.getMainProfile(account.profiles);
14637
+ this._data.mainProfile = PersonUtils.getMainProfile(account.profiles);
14658
14638
  // Update, instead of replace it
14659
- if (this.data.account) {
14660
- this.data.account.fromObject(account);
14639
+ if (this._data.account) {
14640
+ this._data.account.fromObject(account);
14661
14641
  }
14662
14642
  else {
14663
- this.data.account = account;
14643
+ this._data.account = account;
14664
14644
  }
14665
- this.data.loaded = true;
14666
- return [2 /*return*/, this.data.account];
14645
+ this._data.loaded = true;
14646
+ return [2 /*return*/, this._data.account];
14667
14647
  case 3:
14668
14648
  error_6 = _b.sent();
14669
14649
  this.resetData();
@@ -14702,9 +14682,9 @@
14702
14682
  return [2 /*return*/];
14703
14683
  if (this._debug)
14704
14684
  console.debug("[account] Account restoration...");
14705
- this.data.authToken = token;
14706
- this.data.pubkey = pubkey;
14707
- this.data.keypair = seckey && {
14685
+ this._data.authToken = token;
14686
+ this._data.pubkey = pubkey;
14687
+ this._data.keypair = seckey && {
14708
14688
  publicKey: Base58.decode(pubkey),
14709
14689
  secretKey: Base58.decode(seckey)
14710
14690
  } || null;
@@ -14715,7 +14695,7 @@
14715
14695
  return [4 /*yield*/, this.authenticate()];
14716
14696
  case 3:
14717
14697
  _b.sent();
14718
- if (!this.data.authToken && !this.data.authBasic)
14698
+ if (!this._data.authToken && !this._data.authBasic)
14719
14699
  throw new Error('Authentication failed');
14720
14700
  return [3 /*break*/, 5];
14721
14701
  case 4:
@@ -14748,14 +14728,14 @@
14748
14728
  return [2 /*return*/];
14749
14729
  account = exports.Account.fromObject(jsonAccount);
14750
14730
  // Update data
14751
- this.data.account = account;
14752
- this.data.mainProfile = PersonUtils.getMainProfile(account.profiles);
14753
- this.data.loaded = true;
14731
+ this._data.account = account;
14732
+ this._data.mainProfile = PersonUtils.getMainProfile(account.profiles);
14733
+ this._data.loaded = true;
14754
14734
  // Emit event
14755
- this.onLogin.next(this.data.account);
14756
- this.onChange.next(this.data.account);
14735
+ this.onLogin.next(this._data.account);
14736
+ this.onChange.next(this._data.account);
14757
14737
  if (this._debug)
14758
- console.debug("[account] Account restoration [OK] {pubkey: " + pubkey.substr(0, 8) + "}, {profile: " + this.data.mainProfile + "}");
14738
+ console.debug("[account] Account restoration [OK] {pubkey: " + pubkey.substr(0, 8) + "}, {profile: " + this._data.mainProfile + "}");
14759
14739
  return [2 /*return*/, account];
14760
14740
  }
14761
14741
  });
@@ -14771,12 +14751,12 @@
14771
14751
  return __generator(this, function (_b) {
14772
14752
  switch (_b.label) {
14773
14753
  case 0:
14774
- if (!this.data.pubkey)
14754
+ if (!this._data.pubkey)
14775
14755
  throw new Error('User not logged');
14776
14756
  if (this._debug)
14777
- console.debug("[account] Saving account {" + this.data.pubkey.substring(0, 6) + "} in local storage...");
14778
- json = this.data.account.asObject({ keepTypename: true });
14779
- seckey = this.data.keypair && this.data.keypair.secretKey && Base58.encode(this.data.keypair.secretKey) || null;
14757
+ console.debug("[account] Saving account {" + this._data.pubkey.substring(0, 6) + "} in local storage...");
14758
+ json = this._data.account.asObject({ keepTypename: true });
14759
+ seckey = this._data.keypair && this._data.keypair.secretKey && Base58.encode(this._data.keypair.secretKey) || null;
14780
14760
  hasAvatarUrl = json.avatar && !json.avatar.endsWith(DEFAULT_AVATAR_IMAGE) &&
14781
14761
  (json.avatar.startsWith('http://') || (json.avatar.startsWith('https://')));
14782
14762
  if (!(hasAvatarUrl && this.network.online)) return [3 /*break*/, 2];
@@ -14799,9 +14779,9 @@
14799
14779
  case 2:
14800
14780
  _b.trys.push([2, 4, , 5]);
14801
14781
  return [4 /*yield*/, Promise.all([
14802
- this.storage.set(PUBKEY_STORAGE_KEY, this.data.pubkey),
14803
- this.storage.set(TOKEN_STORAGE_KEY, this.data.authToken),
14804
- this.storage.set(ACCOUNT_STORAGE_KEY + "#" + this.data.pubkey, json),
14782
+ this.storage.set(PUBKEY_STORAGE_KEY, this._data.pubkey),
14783
+ this.storage.set(TOKEN_STORAGE_KEY, this._data.authToken),
14784
+ this.storage.set(ACCOUNT_STORAGE_KEY + "#" + this._data.pubkey, json),
14805
14785
  // Secret key (optional)
14806
14786
  seckey && this.storage.set(SECKEY_STORAGE_KEY, seckey) || this.storage.remove(SECKEY_STORAGE_KEY),
14807
14787
  // Remove old storage key
@@ -14878,7 +14858,7 @@
14878
14858
  return __generator(this, function (_b) {
14879
14859
  switch (_b.label) {
14880
14860
  case 0:
14881
- if (!this.data.pubkey)
14861
+ if (!this._data.pubkey)
14882
14862
  throw new Error('User not logged');
14883
14863
  if (!counter)
14884
14864
  console.info('[account] Authentication on pod...');
@@ -14905,7 +14885,7 @@
14905
14885
  if (data_1 && data_1.authenticate) {
14906
14886
  // Store the token
14907
14887
  this.onAuthTokenChange.next(token);
14908
- console.info("[account] Authentication on pod [OK] {pubkey: '" + this.data.pubkey.substr(0, 8) + "'}");
14888
+ console.info("[account] Authentication on pod [OK] {pubkey: '" + this._data.pubkey.substr(0, 8) + "'}");
14909
14889
  return [2 /*return*/, token]; // return the token
14910
14890
  }
14911
14891
  _b.label = 2;
@@ -14931,10 +14911,10 @@
14931
14911
  if (!signatureOK) {
14932
14912
  console.warn('FIXME: Bad peer signature on auth challenge !', data.authChallenge);
14933
14913
  }
14934
- return [4 /*yield*/, this.cryptoService.sign(data.authChallenge.challenge, this.data.keypair)];
14914
+ return [4 /*yield*/, this.cryptoService.sign(data.authChallenge.challenge, this._data.keypair)];
14935
14915
  case 5:
14936
14916
  signature = _b.sent();
14937
- newToken = this.data.pubkey + ":" + data.authChallenge.challenge + "|" + signature;
14917
+ newToken = this._data.pubkey + ":" + data.authChallenge.challenge + "|" + signature;
14938
14918
  return [4 /*yield*/, this.authenticateAndGetToken(newToken, (counter || 1) + 1 /* increment */)];
14939
14919
  case 6:
14940
14920
  // iterate with the new token
@@ -15264,9 +15244,8 @@
15264
15244
  Object.defineProperty(ConfigService.prototype, "config", {
15265
15245
  get: function () {
15266
15246
  // If first call: start loading
15267
- if (!this._started) {
15247
+ if (!this._started)
15268
15248
  this.start();
15269
- }
15270
15249
  return this.$data.pipe(operators.filter(isNotNil));
15271
15250
  },
15272
15251
  enumerable: false,
@@ -15281,30 +15260,49 @@
15281
15260
  console.info('[config] Starting configuration...');
15282
15261
  this._startPromise = this.graphql.ready()
15283
15262
  .then(function () { return _this.loadOrRestoreLocally(); })
15284
- .then(function () {
15263
+ .then(function (data) {
15285
15264
  _this._started = true;
15286
15265
  _this._startPromise = undefined;
15266
+ _this.$data.next(data);
15267
+ return data;
15287
15268
  })
15288
15269
  .catch(function (err) {
15289
15270
  console.error(err && err.message || err, err);
15271
+ _this._started = false;
15290
15272
  _this._startPromise = undefined;
15273
+ return null;
15291
15274
  });
15292
15275
  return this._startPromise;
15293
15276
  };
15294
15277
  ConfigService.prototype.stop = function () {
15295
- this._subscription.unsubscribe();
15296
- this._subscription = new rxjs.Subscription();
15297
- this._started = false;
15298
- this._startPromise = undefined;
15278
+ return __awaiter(this, void 0, void 0, function () {
15279
+ return __generator(this, function (_a) {
15280
+ this._subscription.unsubscribe();
15281
+ this._subscription = new rxjs.Subscription();
15282
+ this._started = false;
15283
+ this._startPromise = undefined;
15284
+ return [2 /*return*/];
15285
+ });
15286
+ });
15299
15287
  };
15300
15288
  ConfigService.prototype.restart = function () {
15301
- if (this.started)
15302
- this.stop();
15303
- return this.start();
15289
+ return __awaiter(this, void 0, void 0, function () {
15290
+ return __generator(this, function (_a) {
15291
+ switch (_a.label) {
15292
+ case 0:
15293
+ if (!this.started) return [3 /*break*/, 2];
15294
+ return [4 /*yield*/, this.stop()];
15295
+ case 1:
15296
+ _a.sent();
15297
+ _a.label = 2;
15298
+ case 2: return [2 /*return*/, this.start()];
15299
+ }
15300
+ });
15301
+ });
15304
15302
  };
15305
15303
  ConfigService.prototype.ready = function () {
15306
15304
  if (this._started)
15307
- return Promise.resolve();
15305
+ return Promise.resolve(this.$data.value);
15308
15306
  if (this._startPromise)
15309
15307
  return this._startPromise;
15310
15308
  return this.start();
@@ -15375,7 +15373,7 @@
15375
15373
  return [4 /*yield*/, this.loadDefault({ fetchPolicy: 'network-only' })];
15376
15374
  case 2:
15377
15375
  reloadedConfig = _a.sent();
15378
- defaultConfig = this.$data.getValue();
15376
+ defaultConfig = this.$data.value;
15379
15377
  if (isNotNil(defaultConfig) && reloadedConfig.label === defaultConfig.label) {
15380
15378
  // Emit update event when is default config
15381
15379
  this.$data.next(reloadedConfig);
@@ -15484,8 +15482,7 @@
15484
15482
  if (wasJustLoaded) {
15485
15483
  // TODO
15486
15484
  }
15487
- this.$data.next(data);
15488
- return [2 /*return*/];
15485
+ return [2 /*return*/, data];
15489
15486
  }
15490
15487
  });
15491
15488
  });
@@ -15738,7 +15735,7 @@
15738
15735
  .then(function () {
15739
15736
  _this._started = true;
15740
15737
  _this._startPromise = undefined;
15741
- console.info("[platform] Starting platform [OK] {mobile: " + _this._mobile + ", touchUi: " + _this.touchUi + "} in " + (Date.now() - now) + "ms");
15738
+ console.info("[platform] Starting platform [OK] {mobile: " + _this._mobile + ", touchUi: " + _this.touchUi + ", downloader: " + !!_this.downloader + " in " + (Date.now() - now) + "ms");
15742
15739
  // Update cache configuration when network changed
15743
15740
  _this.networkService.onNetworkStatusChanges.subscribe(function (type) { return _this.configureCache(type !== 'none'); });
15744
15741
  // Update authentication type
@@ -15787,18 +15784,37 @@
15787
15784
  }
15788
15785
  };
15789
15786
  PlatformService.prototype.download = function (request) {
15790
- if (!request || !request.uri)
15791
- throw new Error('Missing argument \'request\' or \'request.uri\'');
15792
- if (this._android && this._cordova) {
15793
- request = Object.assign(Object.assign({ visibleInDownloadsUi: true, notificationVisibility: ngx$6.NotificationVisibility.VisibleNotifyCompleted, title: request.filename || '' }, request), { destinationInExternalFilesDir: Object.assign({ dirType: 'Downloads', subPath: request.filename || request.title || '' }, request.destinationInExternalFilesDir) });
15794
- this.downloader.download(request)
15795
- .then(function (location) { return console.info('[platform] File successfully downloaded at:' + location); })
15796
- .catch(function (error) { return console.error(error); });
15797
- }
15798
- // Web mode: open URI using the browser
15799
- else {
15800
- this.open(request.uri, '_system', 'location=yes');
15801
- }
15787
+ return __awaiter(this, void 0, void 0, function () {
15788
+ var downloader, location, err_1;
15789
+ return __generator(this, function (_a) {
15790
+ switch (_a.label) {
15791
+ case 0:
15792
+ if (!request || !request.uri)
15793
+ throw new Error('Missing argument \'request\' or \'request.uri\'');
15794
+ downloader = this.downloader || window.plugins.Downloader;
15795
+ if (!(downloader && this._android && this._cordova)) return [3 /*break*/, 5];
15796
+ request = Object.assign(Object.assign({ visibleInDownloadsUi: true, notificationVisibility: ngx$6.NotificationVisibility.VisibleNotifyCompleted, title: request.filename || '' }, request), { destinationInExternalFilesDir: Object.assign({ dirType: 'Downloads', subPath: request.filename || request.title || '' }, request.destinationInExternalFilesDir) });
15797
+ _a.label = 1;
15798
+ case 1:
15799
+ _a.trys.push([1, 3, , 4]);
15800
+ return [4 /*yield*/, downloader.download(request)];
15801
+ case 2:
15802
+ location = _a.sent();
15803
+ console.info('[platform] File successfully downloaded at:' + location);
15804
+ return [2 /*return*/, location];
15805
+ case 3:
15806
+ err_1 = _a.sent();
15807
+ console.error('[platform] Error while downloading: ' + (err_1 && err_1.message || err_1), err_1);
15808
+ throw err_1;
15809
+ case 4: return [3 /*break*/, 6];
15810
+ case 5:
15811
+ console.warn('[platform] Cannot use Cordova downloader: using browser open()');
15812
+ this.open(request.uri, '_system', 'location=no', true);
15813
+ return [2 /*return*/, undefined];
15814
+ case 6: return [2 /*return*/];
15815
+ }
15816
+ });
15817
+ });
15802
15818
  };
15803
15819
  /* -- protected methods -- */
15804
15820
  PlatformService.prototype.configureCordovaPlugins = function (mobile) {
@@ -15894,7 +15910,7 @@
15894
15910
  };
15895
15911
  PlatformService.prototype.migrateStorage = function (forage) {
15896
15912
  return __awaiter(this, void 0, void 0, function () {
15897
- var canMigrate, oldForage, keys, now, toast_1, duration, err_1;
15913
+ var canMigrate, oldForage, keys, now, toast_1, duration, err_2;
15898
15914
  return __generator(this, function (_a) {
15899
15915
  switch (_a.label) {
15900
15916
  case 0:
@@ -15947,8 +15963,8 @@
15947
15963
  _a.sent();
15948
15964
  return [3 /*break*/, 12];
15949
15965
  case 9:
15950
- err_1 = _a.sent();
15951
- console.error(err_1 && err_1.message || err_1, err_1);
15966
+ err_2 = _a.sent();
15967
+ console.error(err_2 && err_2.message || err_2, err_2);
15952
15968
  return [4 /*yield*/, toast_1.dismiss()];
15953
15969
  case 10:
15954
15970
  _a.sent();
@@ -17594,7 +17610,7 @@
17594
17610
  AutocompleteTestPage.decorators = [
17595
17611
  { type: i0.Component, args: [{
17596
17612
  selector: 'app-autocomplete-test',
17597
- template: "<ion-header>\n <ion-toolbar color=\"primary\">\n\n <ion-buttons slot=\"start\">\n <ion-back-button></ion-back-button>\n </ion-buttons>\n\n <ion-title>Autocomplete field test page</ion-title>\n </ion-toolbar>\n</ion-header>\n\n<ion-content>\n\n <!-- Tab nav - mobile/desktop mode -->\n <nav mat-tab-nav-bar>\n <a mat-tab-link\n [active]=\"mode==='mobile'\"\n (click)=\"toggleMode('mobile')\">\n <mat-label>Mobile</mat-label>\n </a>\n <a mat-tab-link [active]=\"mode==='desktop'\"\n (click)=\"toggleMode('desktop')\">\n <mat-label>Desktop</mat-label>\n </a>\n <a mat-tab-link [active]=\"mode==='memory'\"\n (click)=\"toggleMode('memory')\">\n <mat-label>Memory leak debug</mat-label>\n </a>\n <a mat-tab-link\n [active]=\"mode==='temp'\"\n (click)=\"toggleMode('temp')\">\n <mat-label>Temporary</mat-label>\n </a>\n </nav>\n\n <form class=\"form-container\" [formGroup]=\"form\" (ngSubmit)=\"doSubmit($event)\">\n\n <!--<ion-grid *ngIf=\"mode === 'temp'\">\n <ion-row>\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-text color=\"primary\">\n Items is an Observable\n </ion-text>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small><pre>items: Observable&lt;any[]&gt;</pre></small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [logPrefix]=\"'[combo-desktop-2]'\"\n [compareWith]=\"compareWithFn\"\n [mobile]=\"false\"\n ></mat-autocomplete-field>\n </ion-card-content>\n\n </ion-card>\n </ion-col>\n </ion-row>\n\n\n </ion-grid>-->\n\n\n <ion-grid *ngIf=\"mode === 'memory'\">\n\n <!-- debugging memory leak -->\n <ion-row><ion-col><ion-text><h4>Debug memory leak</h4></ion-text></ion-col></ion-row>\n <ion-row>\n <ion-col size=\"2\">\n <mat-form-field floatLabel=\"always\">\n <input matInput type=\"text\" hidden placeholder=\"Items type\">\n <mat-select (selectionChange)=\"memoryAutocompleteFieldName=$event.value\" [value]=\"memoryAutocompleteFieldName\">\n <mat-option value=\"entity-$items\">Observable</mat-option>\n <mat-option value=\"entity-suggestFn\">Suggest function</mat-option>\n </mat-select>\n </mat-form-field>\n </ion-col>\n <ion-col size=\"1\" class=\"ion-no-padding\">\n <mat-form-field floatLabel=\"always\" >\n <input matInput type=\"text\" hidden placeholder=\"Mobile ?\">\n <mat-checkbox (change)=\"memoryMobile=$event.checked\" [checked]=\"memoryMobile\">\n </mat-checkbox>\n </mat-form-field>\n </ion-col>\n\n <ion-col size=\"2\">\n <ion-button *ngIf=\"!memoryTimer\" (click)=\"startMemoryTimer()\" color=\"tertiary\">Start</ion-button>\n <ion-button *ngIf=\"memoryTimer\" (click)=\"stopMemoryTimer()\" color=\"tertiary\">Stop</ion-button>\n </ion-col>\n </ion-row>\n\n <ion-row>\n <ion-col>\n <mat-autocomplete-field formControlName=\"entity\"\n *ngIf=\"!memoryHide && memoryAutocompleteFieldName\"\n [config]=\"autocompleteFields.get(memoryAutocompleteFieldName)\"\n [mobile]=\"memoryMobile\"\n [compareWith]=\"compareWithFn\"\n [logPrefix]=\"'[combo-memory-leak]'\">\n </mat-autocomplete-field>\n </ion-col>\n </ion-row>\n\n </ion-grid>\n\n <!-- Mobile mode -->\n <ion-grid *ngIf=\"mode === 'mobile'\">\n\n <ion-row>\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Suggest() function\n </ion-label>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small><pre>mobile: true, suggestFn: (searchText, filter) =&gt; any[]</pre></small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-suggestFn')\"\n [mobile]=\"true\"\n [compareWith]=\"compareWithFn\"\n [logPrefix]=\"'[combo-mobile-1]'\"\n [required]=\"true\"\n placeholder=\"Suggest field\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Items is an Observable\n </ion-label>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small><pre>mobile: true, items: Observable&lt;any[]&gt;</pre></small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [mobile]=\"true\"\n [compareWith]=\"compareWithFn\"\n [logPrefix]=\"'[combo-mobile-observable]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Control value if missing in items\n </ion-label>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small><pre>mobile: true, items: Observable&lt;any[]&gt;</pre></small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"missingEntity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [mobile]=\"true\"\n [compareWith]=\"compareWithFn\"\n [logPrefix]=\"'[combo-mobile-2]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Disabled control\n </ion-label>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"disableEntity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [mobile]=\"true\"\n [compareWith]=\"compareWithFn\"\n [logPrefix]=\"'[combo-mobile-2]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n </ion-row>\n\n <ion-row>\n <ion-col size=\"9\">\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Change filter\n </ion-label>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small><pre></pre></small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <ion-grid class=\"ion-no-padding\">\n <ion-row>\n <ion-col>\n <ion-button (click)=\"updateFilter('entity-items-filter')\">Filter on: {{autocompleteFields.get('entity-items-filter').filter?.searchAttribute }}</ion-button>\n </ion-col>\n <ion-col>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-items-filter')\"\n [mobile]=\"true\"\n [compareWith]=\"compareWithFn\"\n [logPrefix]=\"'[combo-mobile-filter]'\"\n ></mat-autocomplete-field>\n </ion-col>\n </ion-row>\n </ion-grid>\n\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col size=\"3\">\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Large combo\n </ion-label>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small><pre>class=\"min-width-large\"</pre></small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-items-large')\"\n [mobile]=\"true\"\n class=\"min-width-large\"\n panelWidth=\"400px\"\n [compareWith]=\"compareWithFn\"\n [logPrefix]=\"'[combo-mobile-large]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n </ion-row>\n </ion-grid>\n\n <!-- Desktop mode -->\n <ion-grid *ngIf=\"mode === 'desktop'\">\n <ion-row>\n <ion-col><ion-text><h4>Desktop mode</h4></ion-text></ion-col>\n </ion-row>\n <ion-row>\n\n\n <ion-col size=\"6\">\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-text color=\"primary\">\n items from suggest function\n </ion-text>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small><pre>suggest: (value, filter) => LoadResult&lt;any&gt;\nsuggestLengthThreshold: '3'</pre></small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-items-filter')\"\n [placeholder]=\"'Entity'\"\n [mobile]=\"false\"\n [compareWith]=\"compareWithFn\"\n suggestLengthThreshold=\"3\"\n [logPrefix]=\"'[combo-desktop-suggest]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col size=\"6\">\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-text color=\"primary\">\n Items is an array\n </ion-text>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small><pre>items: [], value: {{stringify(form.controls.entity.value)}}</pre></small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-items')\"\n [logPrefix]=\"'[combo-desktop-array-1]'\"\n [compareWith]=\"compareWithFn\"\n [mobile]=\"false\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-text color=\"primary\">\n Items is an Observable\n </ion-text>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small><pre>items: Observable&lt;any[]&gt;</pre></small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [logPrefix]=\"'[combo-desktop-2]'\"\n [compareWith]=\"compareWithFn\"\n [mobile]=\"false\"\n ></mat-autocomplete-field>\n </ion-card-content>\n\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Control value if missing in items\n </ion-label>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small><pre>items: Observable&lt;any[]&gt;</pre></small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"missingEntity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [compareWith]=\"compareWithFn\"\n [logPrefix]=\"'[combo-desktop-missing]'\"\n [mobile]=\"false\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Full size panel\n </ion-label>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small><pre>class: 'mat-autocomplete-panel-full-size'</pre></small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [mobile]=\"false\"\n [class]=\"'mat-autocomplete-panel-full-size'\"\n [compareWith]=\"compareWithFn\"\n [matAutocompletePosition]=\"'above'\"\n [logPrefix]=\"'[combo-desktop-full-size]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Disabled control\n </ion-label>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"disableEntity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [mobile]=\"false\"\n [compareWith]=\"compareWithFn\"\n [logPrefix]=\"'[combo-desktop-disable]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Readonly control\n </ion-label>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [mobile]=\"false\"\n [compareWith]=\"compareWithFn\"\n [logPrefix]=\"'[combo-desktop-readonly]'\"\n [readonly]=\"true\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n </ion-row>\n\n <ion-row>\n <ion-col size=\"9\" >\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Full size combo\n </ion-label>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small><pre>class=\"mat-autocomplete-panel-full-size\"</pre></small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-items-large')\"\n [mobile]=\"false\"\n class=\"mat-autocomplete-panel-full-size\"\n panelWidth=\"100vw\"\n [compareWith]=\"compareWithFn\"\n [logPrefix]=\"'[combo-desktop-full-size]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col size=\"3\" >\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Large combo\n </ion-label>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small><pre>panelWidth: string</pre></small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-items-large')\"\n [mobile]=\"false\"\n panelWidth=\"400px\"\n [compareWith]=\"compareWithFn\"\n [logPrefix]=\"'[combo-desktop-large]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n\n <ion-col size=\"6\">\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-text color=\"primary\">\n Readonly toggle\n </ion-text>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <mat-checkbox (change)=\"readonlyField.readonly=$event.checked\" [checked]=\"readonlyField.readonly\">\n </mat-checkbox>\n\n <mat-autocomplete-field #readonlyField formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-items-filter')\"\n [placeholder]=\"'Entity'\"\n [mobile]=\"false\"\n [compareWith]=\"compareWithFn\"\n [logPrefix]=\"'[combo-desktop-readonly]'\"\n [readonly]=\"true\">\n </mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n </ion-row>\n\n </ion-grid>\n\n </form>\n\n</ion-content>\n"
17613
+ template: "<ion-header>\n <ion-toolbar color=\"primary\">\n\n <ion-buttons slot=\"start\">\n <ion-back-button></ion-back-button>\n </ion-buttons>\n\n <ion-title>Autocomplete field test page</ion-title>\n </ion-toolbar>\n</ion-header>\n\n<ion-content class=\"ion-padding\">\n\n <!-- Tab nav - mobile/desktop mode -->\n <nav mat-tab-nav-bar>\n <a mat-tab-link\n [active]=\"mode==='mobile'\"\n (click)=\"toggleMode('mobile')\">\n <mat-label>Mobile</mat-label>\n </a>\n <a mat-tab-link [active]=\"mode==='desktop'\"\n (click)=\"toggleMode('desktop')\">\n <mat-label>Desktop</mat-label>\n </a>\n <a mat-tab-link [active]=\"mode==='memory'\"\n (click)=\"toggleMode('memory')\">\n <mat-label>Memory leak debug</mat-label>\n </a>\n <a mat-tab-link\n [active]=\"mode==='temp'\"\n (click)=\"toggleMode('temp')\">\n <mat-label>Temporary</mat-label>\n </a>\n </nav>\n\n <form class=\"form-container\" [formGroup]=\"form\" (ngSubmit)=\"doSubmit($event)\">\n\n <!--<ion-grid *ngIf=\"mode === 'temp'\">\n <ion-row>\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-text color=\"primary\">\n Items is an Observable\n </ion-text>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small><pre>items: Observable&lt;any[]&gt;</pre></small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [logPrefix]=\"'[combo-desktop-2]'\"\n [compareWith]=\"compareWithFn\"\n [mobile]=\"false\"\n ></mat-autocomplete-field>\n </ion-card-content>\n\n </ion-card>\n </ion-col>\n </ion-row>\n\n\n </ion-grid>-->\n\n\n <ion-grid *ngIf=\"mode === 'memory'\">\n\n <!-- debugging memory leak -->\n <ion-row><ion-col><ion-text><h4>Debug memory leak</h4></ion-text></ion-col></ion-row>\n <ion-row>\n <ion-col size=\"2\">\n <mat-form-field floatLabel=\"always\">\n <input matInput type=\"text\" hidden placeholder=\"Items type\">\n <mat-select (selectionChange)=\"memoryAutocompleteFieldName=$event.value\" [value]=\"memoryAutocompleteFieldName\">\n <mat-option value=\"entity-$items\">Observable</mat-option>\n <mat-option value=\"entity-suggestFn\">Suggest function</mat-option>\n </mat-select>\n </mat-form-field>\n </ion-col>\n <ion-col size=\"1\" class=\"ion-no-padding\">\n <mat-form-field floatLabel=\"always\" >\n <input matInput type=\"text\" hidden placeholder=\"Mobile ?\">\n <mat-checkbox (change)=\"memoryMobile=$event.checked\" [checked]=\"memoryMobile\">\n </mat-checkbox>\n </mat-form-field>\n </ion-col>\n\n <ion-col size=\"2\">\n <ion-button *ngIf=\"!memoryTimer\" (click)=\"startMemoryTimer()\" color=\"tertiary\">Start</ion-button>\n <ion-button *ngIf=\"memoryTimer\" (click)=\"stopMemoryTimer()\" color=\"tertiary\">Stop</ion-button>\n </ion-col>\n </ion-row>\n\n <ion-row>\n <ion-col>\n <mat-autocomplete-field formControlName=\"entity\"\n *ngIf=\"!memoryHide && memoryAutocompleteFieldName\"\n [config]=\"autocompleteFields.get(memoryAutocompleteFieldName)\"\n [mobile]=\"memoryMobile\"\n [compareWith]=\"compareWithFn\"\n [logPrefix]=\"'[combo-memory-leak]'\">\n </mat-autocomplete-field>\n </ion-col>\n </ion-row>\n\n </ion-grid>\n\n <!-- Mobile mode -->\n <ion-grid *ngIf=\"mode === 'mobile'\">\n\n <ion-row>\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Suggest() function\n </ion-label>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small><pre>mobile: true, suggestFn: (searchText, filter) =&gt; any[]</pre></small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-suggestFn')\"\n [mobile]=\"true\"\n [compareWith]=\"compareWithFn\"\n [logPrefix]=\"'[combo-mobile-1]'\"\n [required]=\"true\"\n placeholder=\"Suggest field\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Items is an Observable\n </ion-label>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small><pre>mobile: true, items: Observable&lt;any[]&gt;</pre></small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [mobile]=\"true\"\n [compareWith]=\"compareWithFn\"\n [logPrefix]=\"'[combo-mobile-observable]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Control value if missing in items\n </ion-label>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small><pre>mobile: true, items: Observable&lt;any[]&gt;</pre></small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"missingEntity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [mobile]=\"true\"\n [compareWith]=\"compareWithFn\"\n [logPrefix]=\"'[combo-mobile-2]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Disabled control\n </ion-label>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"disableEntity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [mobile]=\"true\"\n [compareWith]=\"compareWithFn\"\n [logPrefix]=\"'[combo-mobile-2]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n </ion-row>\n\n <ion-row>\n <ion-col size=\"9\">\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Change filter\n </ion-label>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small><pre></pre></small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <ion-grid class=\"ion-no-padding\">\n <ion-row>\n <ion-col>\n <ion-button (click)=\"updateFilter('entity-items-filter')\">Filter on: {{autocompleteFields.get('entity-items-filter').filter?.searchAttribute }}</ion-button>\n </ion-col>\n <ion-col>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-items-filter')\"\n [mobile]=\"true\"\n [compareWith]=\"compareWithFn\"\n [logPrefix]=\"'[combo-mobile-filter]'\"\n ></mat-autocomplete-field>\n </ion-col>\n </ion-row>\n </ion-grid>\n\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col size=\"3\">\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Large combo\n </ion-label>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small><pre>class=\"min-width-large\"</pre></small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-items-large')\"\n [mobile]=\"true\"\n class=\"min-width-large\"\n panelWidth=\"400px\"\n [compareWith]=\"compareWithFn\"\n [logPrefix]=\"'[combo-mobile-large]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n </ion-row>\n </ion-grid>\n\n <!-- Desktop mode -->\n <ion-grid *ngIf=\"mode === 'desktop'\">\n <ion-row>\n <ion-col><ion-text><h4>Desktop mode</h4></ion-text></ion-col>\n </ion-row>\n <ion-row>\n\n\n <ion-col size=\"6\">\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-text color=\"primary\">\n items from suggest function\n </ion-text>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small><pre>suggest: (value, filter) => LoadResult&lt;any&gt;\nsuggestLengthThreshold: '3'</pre></small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-items-filter')\"\n [mobile]=\"false\"\n [compareWith]=\"compareWithFn\"\n suggestLengthThreshold=\"3\"\n [logPrefix]=\"'[combo-desktop-suggest]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col size=\"6\">\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-text color=\"primary\">\n Items is an array\n </ion-text>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small><pre>items: [], value: {{stringify(form.controls.entity.value)}}</pre></small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-items')\"\n [logPrefix]=\"'[combo-desktop-array-1]'\"\n [compareWith]=\"compareWithFn\"\n [mobile]=\"false\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-text color=\"primary\">\n Items is an Observable\n </ion-text>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small><pre>items: Observable&lt;any[]&gt;</pre></small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [logPrefix]=\"'[combo-desktop-2]'\"\n [compareWith]=\"compareWithFn\"\n [mobile]=\"false\"\n ></mat-autocomplete-field>\n </ion-card-content>\n\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Control value if missing in items\n </ion-label>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small><pre>items: Observable&lt;any[]&gt;</pre></small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"missingEntity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [compareWith]=\"compareWithFn\"\n [logPrefix]=\"'[combo-desktop-missing]'\"\n [mobile]=\"false\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Full size panel\n </ion-label>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small><pre>class: 'mat-autocomplete-panel-full-size'</pre></small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [mobile]=\"false\"\n [class]=\"'mat-autocomplete-panel-full-size'\"\n [compareWith]=\"compareWithFn\"\n [matAutocompletePosition]=\"'above'\"\n [logPrefix]=\"'[combo-desktop-full-size]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Disabled control\n </ion-label>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"disableEntity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [mobile]=\"false\"\n [compareWith]=\"compareWithFn\"\n [logPrefix]=\"'[combo-desktop-disable]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Readonly control\n </ion-label>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [mobile]=\"false\"\n [compareWith]=\"compareWithFn\"\n [logPrefix]=\"'[combo-desktop-readonly]'\"\n [readonly]=\"true\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n </ion-row>\n\n <ion-row>\n <ion-col size=\"9\" >\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Full size combo\n </ion-label>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small><pre>class=\"mat-autocomplete-panel-full-size\"</pre></small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-items-large')\"\n [mobile]=\"false\"\n class=\"mat-autocomplete-panel-full-size\"\n panelWidth=\"100vw\"\n [compareWith]=\"compareWithFn\"\n [logPrefix]=\"'[combo-desktop-full-size]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col size=\"3\" >\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Large combo\n </ion-label>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small><pre>panelWidth: string</pre></small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-items-large')\"\n [mobile]=\"false\"\n panelWidth=\"400px\"\n [compareWith]=\"compareWithFn\"\n [logPrefix]=\"'[combo-desktop-large]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n </ion-row>\n\n\n\n\n </ion-grid>\n\n </form>\n\n</ion-content>\n"
17598
17614
  },] }
17599
17615
  ];
17600
17616
  AutocompleteTestPage.ctorParameters = function () { return [
@@ -21331,6 +21347,7 @@
21331
21347
  AppInstallUpgradeCard.prototype.downloadLink = function (event, link) {
21332
21348
  if (!link || !link.url)
21333
21349
  return; // Skip
21350
+ console.info("[install-upgrade-card] Downloading '" + link.url + "' ...");
21334
21351
  this.platform.download({
21335
21352
  uri: link.url,
21336
21353
  filename: link.downloadFilename,
@@ -21377,15 +21394,20 @@
21377
21394
  };
21378
21395
  AppInstallUpgradeCard.prototype.getCompatibleUpgradeLinks = function (installLinks, config) {
21379
21396
  var _this = this;
21380
- var appMinVersion = config.getProperty(CORE_CONFIG_OPTIONS.APP_MIN_VERSION);
21381
- var needUpgrade = appMinVersion && !VersionUtils.isCompatible(appMinVersion, this.environment.version);
21397
+ var appVersion = this.environment.version;
21398
+ var appMinVersionFromPod = config.getProperty(CORE_CONFIG_OPTIONS.APP_MIN_VERSION);
21399
+ if (!appVersion) {
21400
+ console.error('Missing value for \'environment.version\': cannot check app compatibility!');
21401
+ return undefined;
21402
+ }
21403
+ var needUpgrade = appMinVersionFromPod && !VersionUtils.isCompatible(appMinVersionFromPod, appVersion);
21382
21404
  if (!needUpgrade)
21383
21405
  return undefined;
21384
21406
  var upgradeLinks = installLinks
21385
21407
  .filter(function (link) { return _this.platform.is('mobileweb') || (link.platform && _this.platform.is(link.platform)); });
21386
21408
  // Use min version as default version
21387
21409
  upgradeLinks.forEach(function (link) {
21388
- link.version = link.version || appMinVersion;
21410
+ link.version = link.version || appMinVersionFromPod;
21389
21411
  });
21390
21412
  return isNotEmptyArray(upgradeLinks) ? upgradeLinks : undefined;
21391
21413
  };
@@ -21397,6 +21419,7 @@
21397
21419
  var url = config.getProperty(CORE_CONFIG_OPTIONS.ANDROID_INSTALL_URL);
21398
21420
  if (isNilOrBlank(url))
21399
21421
  url = this.environment.defaultAndroidInstallUrl || null;
21422
+ var minVersion = config.getProperty(CORE_CONFIG_OPTIONS.APP_MIN_VERSION);
21400
21423
  // Compute App name
21401
21424
  var name = isNotNilOrBlank(url) && config.label || this.environment.defaultAppName || 'SUMARiS';
21402
21425
  if (url) {
@@ -21405,7 +21428,7 @@
21405
21428
  var mimeType = void 0;
21406
21429
  // Get file name
21407
21430
  var filename = this.getFilename(url);
21408
- version = config.getProperty(CORE_CONFIG_OPTIONS.APP_MIN_VERSION);
21431
+ version = minVersion || 'latest';
21409
21432
  // OK, this is a downloadable APK file (e.g. NOT a link to a playstore)
21410
21433
  if (filename === null || filename === void 0 ? void 0 : filename.endsWith('.apk')) {
21411
21434
  // Define mime type
@@ -21415,13 +21438,12 @@
21415
21438
  version = versionMatches && versionMatches[1] || version;
21416
21439
  // Compute a new file name, with the version
21417
21440
  if (isNotNilOrBlank(name)) {
21418
- downloadFilename = name + "-" + version + ".apk";
21441
+ downloadFilename = (name + "-v" + version + ".apk").toLowerCase();
21419
21442
  }
21420
21443
  else {
21421
21444
  downloadFilename = filename;
21422
- // Replace 'latest' with the app min version
21445
+ // Replace 'latest' with the app version, if present
21423
21446
  if (downloadFilename.indexOf('latest')) {
21424
- version = config.getProperty(CORE_CONFIG_OPTIONS.APP_MIN_VERSION);
21425
21447
  downloadFilename = downloadFilename.replace('latest', version);
21426
21448
  }
21427
21449
  }
@@ -27155,6 +27177,7 @@
27155
27177
  _this.filterCriteriaCount = 0;
27156
27178
  _this.statusList = StatusList;
27157
27179
  _this.statusById = StatusById;
27180
+ _this.useSticky = false;
27158
27181
  _this.referentialToString = referentialToString;
27159
27182
  _this.inlineEdition = accountService.isAdmin(); // Allow inline edition only if admin
27160
27183
  _this.canEdit = accountService.isAdmin();
@@ -27273,13 +27296,13 @@
27273
27296
  UsersPage.decorators = [
27274
27297
  { type: i0.Component, args: [{
27275
27298
  selector: 'app-users-table',
27276
- template: "<app-toolbar [title]=\"'USER.LIST.TITLE'|translate\"\n color=\"primary\"\n [canGoBack]=\"false\"\n [hasValidate]=\"!(loadingSubject|async) && dirty\"\n (onValidate)=\"save()\">\n <ion-buttons slot=\"end\">\n <ng-container *ngIf=\"!selection.hasValue(); else hasSelection\">\n <!-- Add -->\n <button mat-icon-button\n *ngIf=\"canEdit && !mobile\"\n [title]=\"'COMMON.BTN_ADD'|translate\"\n (click)=\"addRow()\">\n <mat-icon>add</mat-icon>\n </button>\n\n <!-- Refresh -->\n <button mat-icon-button *ngIf=\"!mobile\"\n [title]=\"'COMMON.BTN_REFRESH'|translate\"\n (click)=\"onRefresh.emit()\">\n <mat-icon>refresh</mat-icon>\n </button>\n\n <!-- reset filter -->\n <button mat-icon-button (click)=\"resetFilter()\"\n *ngIf=\"filterCriteriaCount\">\n <mat-icon color=\"accent\">filter_list_alt</mat-icon>\n <mat-icon class=\"icon-secondary\" style=\"left: 16px; top: 5px; font-weight: bold;\">close</mat-icon>\n </button>\n\n <!-- show filter -->\n <button mat-icon-button (click)=\"filterExpansionPanel.toggle()\">\n <mat-icon *ngIf=\"filterCriteriaCount; else emptyFilter\"\n [matBadge]=\"filterCriteriaCount\"\n matBadgeColor=\"accent\"\n matBadgeSize=\"small\"\n matBadgePosition=\"above after\">filter_list_alt</mat-icon>\n <ng-template #emptyFilter>\n <mat-icon>filter_list_alt</mat-icon>\n </ng-template>\n </button>\n </ng-container>\n\n <ng-template #hasSelection>\n <!-- delete -->\n <button mat-icon-button\n class=\"hidden-xs hidden-sm\"\n [title]=\"'COMMON.BTN_DELETE'|translate\"\n (click)=\"deleteSelection($event)\">\n <mat-icon>delete</mat-icon>\n </button>\n </ng-template>\n </ion-buttons>\n</app-toolbar>\n\n<ion-content class=\"ion-no-padding\">\n\n <!-- error -->\n <ion-item *ngIf=\"errorSubject|async ; let error\" lines=\"none\" @slideUpDownAnimation>\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 <!-- search -->\n <mat-expansion-panel #filterExpansionPanel class=\"ion-no-padding filter-panel filter-panel-floating\">\n <form class=\"form-container ion-padding\" [formGroup]=\"filterForm\" (ngSubmit)=\"onRefresh.emit()\">\n <ion-grid>\n <ion-row>\n <ion-col>\n <!-- search -->\n <mat-form-field>\n <input matInput [placeholder]=\"'USER.LIST.FILTER.SEARCH'|translate\" formControlName=\"searchText\">\n\n <button mat-icon-button matSuffix tabindex=\"-1\"\n type=\"button\"\n (click)=\"clearControlValue($event, filterForm.controls.searchText)\"\n [hidden]=\"filterForm.controls.searchText.disabled || !filterForm.controls.searchText.value\">\n <mat-icon>close</mat-icon>\n </button>\n </mat-form-field>\n </ion-col>\n\n <ion-col>\n <!-- status -->\n <mat-form-field>\n <mat-select formControlName=\"statusId\" [placeholder]=\"'USER.STATUS'|translate\" >\n <mat-option [value]=\"null\"><i><span translate>COMMON.EMPTY_OPTION</span></i></mat-option>\n <mat-option *ngFor=\"let item of statusList\" [value]=\"item.id\">\n <ion-icon [name]=\"item.icon\"></ion-icon>\n {{ item.label |translate }}\n </mat-option>\n </mat-select>\n\n <button mat-icon-button matSuffix tabindex=\"-1\"\n type=\"button\"\n (click)=\"clearControlValue($event, filterForm.controls.statusId)\"\n [hidden]=\"filterForm.controls.statusId.disabled || !filterForm.controls.statusId.value\">\n <mat-icon>close</mat-icon>\n </button>\n </mat-form-field>\n </ion-col>\n </ion-row>\n </ion-grid>\n </form>\n\n <mat-action-row>\n <!-- Counter -->\n <ion-label [hidden]=\"(loadingSubject|async) || filterForm.dirty\"\n [color]=\"empty && 'danger'\"\n class=\"ion-padding\">\n {{ (totalRowCount ? 'COMMON.RESULT_COUNT' : 'COMMON.NO_RESULT') | translate: {count: (totalRowCount |\n numberFormat)} }}\n </ion-label>\n\n <div class=\"toolbar-spacer\"></div>\n\n <!-- Close panel -->\n <ion-button mat-button fill=\"clear\" color=\"dark\"\n (click)=\"filterExpansionPanel.close()\"\n [disabled]=\"loadingSubject|async\">\n <ion-text translate>COMMON.BTN_CLOSE</ion-text>\n </ion-button>\n\n <!-- Search button -->\n <ion-button mat-button\n [color]=\"filterForm.dirty ? 'tertiary' : 'dark'\"\n [fill]=\"filterForm.dirty ? 'solid' : 'clear'\"\n (click)=\"applyFilterAndClosePanel($event)\"\n [disabled]=\"loadingSubject|async\">\n <ion-text translate>COMMON.BTN_APPLY</ion-text>\n </ion-button>\n\n </mat-action-row>\n </mat-expansion-panel>\n\n <!-- error -->\n <ion-item *ngIf=\"error\" visible-xs visible-sm visible-mobile 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 <div class=\"table-container\">\n <table #table mat-table matSort\n [dataSource]=\"dataSource\"\n [matSortActive]=\"defaultSortBy\" [matSortDirection]=\"defaultSortDirection\"\n matSortDisableClear [trackBy]=\"trackByFn\">\n\n <ng-container matColumnDef=\"select\">\n <th mat-header-cell *matHeaderCellDef [class.cdk-visually-hidden]=\"!inlineEdition\">\n <mat-checkbox (change)=\"$event ? masterToggle() : null\" [checked]=\"selection.hasValue() && isAllSelected()\"\n [indeterminate]=\"selection.hasValue() && !isAllSelected()\">\n </mat-checkbox>\n </th>\n <td mat-cell *matCellDef=\"let row\" [class.cdk-visually-hidden]=\"!inlineEdition\">\n <mat-checkbox (click)=\"$event.stopPropagation()\" (change)=\"$event ? selection.toggle(row) : null\" [checked]=\"selection.isSelected(row)\">\n </mat-checkbox>\n </td>\n </ng-container>\n\n <!-- Id Column -->\n <ng-container matColumnDef=\"id\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>\n <ion-label>#</ion-label>\n </th>\n <td mat-cell *matCellDef=\"let row\">{{ row.currentData.id }}</td>\n </ng-container>\n\n <!-- avatar Column -->\n <ng-container matColumnDef=\"avatar\">\n <th mat-header-cell *matHeaderCellDef></th>\n <td mat-cell *matCellDef=\"let row\">\n <div class=\"avatar\" *ngIf=\"row.currentData.avatar; else generateIcon\"\n [ngStyle]=\"{'background-image':'url('+row.currentData.avatar+')'}\"></div>\n <ng-template #generateIcon>\n <div class=\"avatar\">\n <svg width=\"38\" width=\"38\" [data-jdenticon-value]=\"row.currentData.id\"></svg>\n </div>\n </ng-template>\n </td>\n </ng-container>\n\n <!-- lastName -->\n <ng-container matColumnDef=\"lastName\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>\n <span translate>USER.LAST_NAME</span>\n </th>\n <td mat-cell *matCellDef=\"let row\">\n <mat-form-field floatLabel=\"never\">\n <input matInput [formControl]=\"row.validator.controls['lastName']\" [placeholder]=\"'USER.LAST_NAME'|translate\"\n [readonly]=\"!row.editing\" [appAutofocus]=\"row == -1 && row.editing\">\n <mat-error *ngIf=\"row.validator.controls['lastName'].hasError('required')\" translate>ERROR.FIELD_REQUIRED</mat-error>\n <mat-error *ngIf=\"row.validator.controls['lastName'].hasError('minlength')\">\n <span>{{'ERROR.FIELD_MIN_LENGTH' | translate: {minLength: 2} }}</span>\n </mat-error>\n </mat-form-field>\n\n </td>\n\n </ng-container>\n\n <!-- firstname -->\n <ng-container matColumnDef=\"firstName\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>\n <span translate>USER.FIRST_NAME</span>\n </th>\n <td mat-cell *matCellDef=\"let row\" [class.mat-form-field-disabled]=\"!row.editing\">\n <mat-form-field floatLabel=\"never\">\n <input matInput [formControl]=\"row.validator.controls['firstName']\" [placeholder]=\"'USER.FIRST_NAME'|translate\"\n [readonly]=\"!row.editing\">\n <mat-error *ngIf=\"row.validator.controls['firstName'].hasError('required')\" translate>ERROR.FIELD_REQUIRED</mat-error>\n <mat-error *ngIf=\"row.validator.controls['firstName'].hasError('minlength')\">\n <span>{{'ERROR.FIELD_MIN_LENGTH' | translate: {minLength: 2} }}</span>\n </mat-error>\n </mat-form-field>\n </td>\n </ng-container>\n\n <!-- email -->\n <ng-container matColumnDef=\"email\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>\n <span translate>USER.EMAIL</span>\n </th>\n <td mat-cell *matCellDef=\"let row\">\n <mat-form-field floatLabel=\"never\">\n <input matInput [formControl]=\"row.validator.controls['email']\" [placeholder]=\"'USER.EMAIL'|translate\"\n [readonly]=\"!row.editing\">\n <mat-error *ngIf=\"row.validator.controls['email'].hasError('required')\" translate>ERROR.FIELD_REQUIRED</mat-error>\n <mat-error *ngIf=\"row.validator.controls['email'].hasError('email')\">\n <span translate>ERROR.FIELD_NOT_VALID_EMAIL</span>\n </mat-error>\n </mat-form-field>\n </td>\n </ng-container>\n\n <!-- additional fields -->\n <ng-container *ngFor=\"let definition of additionalFields\" [matColumnDef]=\"definition.key\">\n <th mat-header-cell *matHeaderCellDef>\n <span>{{definition.label|translate}}</span>\n </th>\n <td mat-cell *matCellDef=\"let row\">\n <app-form-field floatLabel=\"never\"\n [definition]=\"definition\"\n [formControl]=\"row.validator.controls[definition.key]\"\n [required]=\"definition.extra?.account?.required\">\n </app-form-field>\n </td>\n </ng-container>\n\n <!-- profile column -->\n <ng-container matColumnDef=\"profile\">\n <th mat-header-cell *matHeaderCellDef>\n <span translate>USER.PROFILE</span>\n </th>\n <td mat-cell *matCellDef=\"let row\" [class.mat-form-field-disabled]=\"!row.editing\">\n <mat-form-field floatLabel=\"never\">\n <mat-select [formControl]=\"row.validator.controls['mainProfile']\" [placeholder]=\"'USER.PROFILE'|translate\">\n <mat-option *ngFor=\"let item of profiles\" [value]=\"item\">\n {{ ('USER.PROFILE_ENUM.' + item) | uppercase |translate }}\n </mat-option>\n </mat-select>\n <mat-error *ngIf=\"row.validator.controls['mainProfile'].hasError('required')\" translate>ERROR.FIELD_REQUIRED</mat-error>\n </mat-form-field>\n </td>\n </ng-container>\n\n <!-- Status column -->\n <ng-container matColumnDef=\"status\">\n <th mat-header-cell *matHeaderCellDef>\n <span translate>USER.STATUS</span>\n </th>\n <td mat-cell *matCellDef=\"let row\" [class.mat-form-field-disabled]=\"!row.editing\">\n <mat-form-field floatLabel=\"never\">\n <ion-icon matPrefix *ngIf=\"row.validator.controls['statusId'].value &gt;=0\" [name]=\"statusById[row.validator.controls['statusId'].value]?.icon\"></ion-icon>\n\n <mat-select [formControl]=\"row.validator.controls['statusId']\" [placeholder]=\"'REFERENTIAL.STATUS'|translate\">\n <mat-select-trigger>\n <span *ngIf=\"row.validator.controls['statusId'].value &gt;=0\">\n {{ statusById[row.validator.controls['statusId'].value]?.label | translate}}</span>\n </mat-select-trigger>\n <mat-option *ngFor=\"let item of statusList\" [value]=\"item.id\">\n <ion-icon [name]=\"item.icon\"></ion-icon>\n {{ item.label |translate }}\n </mat-option>\n </mat-select>\n <mat-error *ngIf=\"row.validator.controls['statusId'].hasError('required')\" translate>ERROR.FIELD_REQUIRED</mat-error>\n </mat-form-field>\n </td>\n </ng-container>\n\n <!-- username -->\n <ng-container matColumnDef=\"username\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>\n <span translate>USER.USERNAME</span>\n </th>\n <td mat-cell *matCellDef=\"let row\" [class.mat-form-field-disabled]=\"!row.editing\">\n <mat-form-field floatLabel=\"never\">\n <input matInput [formControl]=\"row.validator.controls.username\" [placeholder]=\"'USER.USERNAME'|translate\"\n [readonly]=\"!row.editing\">\n <mat-error *ngIf=\"row.validator.controls.username.hasError('required')\" translate>ERROR.FIELD_REQUIRED</mat-error>\n </mat-form-field>\n </td>\n </ng-container>\n\n <!-- username extranet -->\n <ng-container matColumnDef=\"usernameExtranet\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>\n <span translate>USER.USERNAME_EXTRANET</span>\n </th>\n <td mat-cell *matCellDef=\"let row\" [class.mat-form-field-disabled]=\"!row.editing\">\n <mat-form-field floatLabel=\"never\">\n <input matInput [formControl]=\"row.validator.controls.usernameExtranet\" [placeholder]=\"'USER.USERNAME_EXTRANET'|translate\"\n [readonly]=\"!row.editing\">\n <mat-error *ngIf=\"row.validator.controls.usernameExtranet.hasError('required')\" translate>ERROR.FIELD_REQUIRED</mat-error>\n </mat-form-field>\n </td>\n </ng-container>\n\n <!-- pubkey -->\n <ng-container matColumnDef=\"pubkey\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>\n <span translate>USER.PUBKEY</span>\n </th>\n <td mat-cell *matCellDef=\"let row\" [class.mat-form-field-disabled]=\"!row.editing\" [title]=\"row.validator.controls['pubkey'].value\">\n <mat-form-field floatLabel=\"never\">\n <input matInput [formControl]=\"row.validator.controls['pubkey']\" [placeholder]=\"'USER.PUBKEY'|translate\"\n [readonly]=\"!row.editing\" autocomplete=\"off\">\n <mat-error *ngIf=\"row.validator.controls['pubkey'].hasError('required')\" translate>ERROR.FIELD_REQUIRED</mat-error>\n <mat-error *ngIf=\"row.validator.controls['pubkey'].hasError('pubkey')\">\n <span translate>ERROR.FIELD_NOT_VALID_PUBKEY</span>\n </mat-error>\n </mat-form-field>\n </td>\n </ng-container>\n\n <!-- Actions buttons column -->\n <app-actions-column [stickyEnd]=\"true\"\n (optionsClick)=\"openSelectColumnsModal($event)\"\n (cancelOrDeleteClick)=\"cancelOrDelete($event.event, $event.row)\"\n (confirmAndAddClick)=\"confirmAndAdd($event.event, $event.row)\"\n (backward)=\"confirmAndBackward($event.event, $event.row)\"\n (forward)=\"confirmAndForward($event.event, $event.row)\">\n </app-actions-column>\n\n <tr mat-header-row *matHeaderRowDef=\"displayedColumns; sticky\"></tr>\n <tr mat-row *matRowDef=\"let row; columns: displayedColumns;\"\n [class.mat-row-error]=\"row.validator.invalid\"\n [class.mat-row-dirty]=\"row.currentData.dirty\"\n [class.mat-row-disabled]=\"!row.editing\"\n (click)=\"clickRow($event, row)\"></tr>\n </table>\n\n <ng-container *ngIf=\"loadingSubject|async; else noResult\">\n <ion-item>\n <ion-skeleton-text animated></ion-skeleton-text>\n </ion-item>\n </ng-container>\n\n <ng-template #noResult>\n <ion-item *ngIf=\"totalRowCount === 0\">\n <ion-text color=\"danger\" class=\"text-italic\" translate>COMMON.NO_RESULT</ion-text>\n </ion-item>\n </ng-template>\n </div>\n</ion-content>\n\n<ion-footer>\n <mat-paginator class=\"mat-paginator-footer\"\n [length]=\"totalRowCount\" [pageSize]=\"defaultPageSize\"\n [pageSizeOptions]=\"defaultPageSizeOptions\" showFirstLastButtons>\n </mat-paginator>\n\n <app-form-buttons-bar *ngIf=\"!mobile && inlineEdition\"\n (onCancel)=\"onRefresh.emit()\" (onSave)=\"save()\" [disabled]=\"(loadingSubject|async) || !dirty\"></app-form-buttons-bar>\n</ion-footer>\n\n<ion-fab slot=\"fixed\" vertical=\"bottom\" horizontal=\"end\" *ngIf=\"mobile\">\n <ion-fab-button color=\"tertiary\" (click)=\"addRow()\">\n <ion-icon name=\"add\"></ion-icon>\n </ion-fab-button>\n</ion-fab>\n",
27299
+ template: "<app-toolbar [title]=\"'USER.LIST.TITLE'|translate\"\n color=\"primary\"\n [canGoBack]=\"false\"\n [hasValidate]=\"!(loadingSubject|async) && dirty\"\n (onValidate)=\"save()\">\n <ion-buttons slot=\"end\">\n <ng-container *ngIf=\"!selection.hasValue(); else hasSelection\">\n <!-- Add -->\n <button mat-icon-button\n *ngIf=\"canEdit && !mobile\"\n [title]=\"'COMMON.BTN_ADD'|translate\"\n (click)=\"addRow()\">\n <mat-icon>add</mat-icon>\n </button>\n\n <!-- Refresh -->\n <button mat-icon-button *ngIf=\"!mobile\"\n [title]=\"'COMMON.BTN_REFRESH'|translate\"\n (click)=\"onRefresh.emit()\">\n <mat-icon>refresh</mat-icon>\n </button>\n\n <!-- reset filter -->\n <button mat-icon-button (click)=\"resetFilter()\"\n *ngIf=\"filterCriteriaCount\">\n <mat-icon color=\"accent\">filter_list_alt</mat-icon>\n <mat-icon class=\"icon-secondary\" style=\"left: 16px; top: 5px; font-weight: bold;\">close</mat-icon>\n </button>\n\n <!-- show filter -->\n <button mat-icon-button (click)=\"filterExpansionPanel.toggle()\">\n <mat-icon *ngIf=\"filterCriteriaCount; else emptyFilter\"\n [matBadge]=\"filterCriteriaCount\"\n matBadgeColor=\"accent\"\n matBadgeSize=\"small\"\n matBadgePosition=\"above after\">filter_list_alt\n </mat-icon>\n <ng-template #emptyFilter>\n <mat-icon>filter_list_alt</mat-icon>\n </ng-template>\n </button>\n </ng-container>\n\n <ng-template #hasSelection>\n <!-- delete -->\n <button mat-icon-button\n class=\"hidden-xs hidden-sm\"\n [title]=\"'COMMON.BTN_DELETE'|translate\"\n (click)=\"deleteSelection($event)\">\n <mat-icon>delete</mat-icon>\n </button>\n </ng-template>\n </ion-buttons>\n</app-toolbar>\n\n<ion-content class=\"ion-no-padding\">\n\n <!-- error -->\n <ion-item *ngIf=\"errorSubject|async ; let error\" lines=\"none\" @slideUpDownAnimation>\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 <!-- search -->\n <mat-expansion-panel #filterExpansionPanel class=\"ion-no-padding filter-panel filter-panel-floating\">\n <form class=\"form-container ion-padding\" [formGroup]=\"filterForm\" (ngSubmit)=\"onRefresh.emit()\">\n <ion-grid>\n <ion-row>\n <ion-col>\n <!-- search -->\n <mat-form-field>\n <input matInput [placeholder]=\"'USER.LIST.FILTER.SEARCH'|translate\" formControlName=\"searchText\">\n\n <button mat-icon-button matSuffix tabindex=\"-1\"\n type=\"button\"\n (click)=\"clearControlValue($event, filterForm.controls.searchText)\"\n [hidden]=\"filterForm.controls.searchText.disabled || !filterForm.controls.searchText.value\">\n <mat-icon>close</mat-icon>\n </button>\n </mat-form-field>\n </ion-col>\n\n <ion-col>\n <!-- status -->\n <mat-form-field>\n <mat-select formControlName=\"statusId\" [placeholder]=\"'USER.STATUS'|translate\">\n <mat-option [value]=\"null\"><i><span translate>COMMON.EMPTY_OPTION</span></i></mat-option>\n <mat-option *ngFor=\"let item of statusList\" [value]=\"item.id\">\n <ion-icon [name]=\"item.icon\"></ion-icon>\n {{ item.label |translate }}\n </mat-option>\n </mat-select>\n\n <button mat-icon-button matSuffix tabindex=\"-1\"\n type=\"button\"\n (click)=\"clearControlValue($event, filterForm.controls.statusId)\"\n [hidden]=\"filterForm.controls.statusId.disabled || !filterForm.controls.statusId.value\">\n <mat-icon>close</mat-icon>\n </button>\n </mat-form-field>\n </ion-col>\n </ion-row>\n </ion-grid>\n </form>\n\n <mat-action-row>\n <!-- Counter -->\n <ion-label [hidden]=\"(loadingSubject|async) || filterForm.dirty\"\n [color]=\"empty && 'danger'\"\n class=\"ion-padding\">\n {{ (totalRowCount ? 'COMMON.RESULT_COUNT' : 'COMMON.NO_RESULT') | translate: {\n count: (totalRowCount |\n numberFormat)\n } }}\n </ion-label>\n\n <div class=\"toolbar-spacer\"></div>\n\n <!-- Close panel -->\n <ion-button mat-button fill=\"clear\" color=\"dark\"\n (click)=\"filterExpansionPanel.close()\"\n [disabled]=\"loadingSubject|async\">\n <ion-text translate>COMMON.BTN_CLOSE</ion-text>\n </ion-button>\n\n <!-- Search button -->\n <ion-button mat-button\n [color]=\"filterForm.dirty ? 'tertiary' : 'dark'\"\n [fill]=\"filterForm.dirty ? 'solid' : 'clear'\"\n (click)=\"applyFilterAndClosePanel($event)\"\n [disabled]=\"loadingSubject|async\">\n <ion-text translate>COMMON.BTN_APPLY</ion-text>\n </ion-button>\n\n </mat-action-row>\n </mat-expansion-panel>\n\n <!-- error -->\n <ion-item *ngIf=\"error\" visible-xs visible-sm visible-mobile 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 <div class=\"table-container\">\n <table #table mat-table matSort\n [dataSource]=\"dataSource\"\n [matSortActive]=\"defaultSortBy\" [matSortDirection]=\"defaultSortDirection\"\n matSortDisableClear [trackBy]=\"trackByFn\">\n\n <ng-container matColumnDef=\"select\" [sticky]=\"useSticky\">\n <th mat-header-cell *matHeaderCellDef [class.cdk-visually-hidden]=\"!inlineEdition\">\n <mat-checkbox (change)=\"$event ? masterToggle() : null\" [checked]=\"selection.hasValue() && isAllSelected()\"\n [indeterminate]=\"selection.hasValue() && !isAllSelected()\">\n </mat-checkbox>\n </th>\n <td mat-cell *matCellDef=\"let row\" [class.cdk-visually-hidden]=\"!inlineEdition\">\n <mat-checkbox (click)=\"$event.stopPropagation()\" (change)=\"$event ? selection.toggle(row) : null\" [checked]=\"selection.isSelected(row)\">\n </mat-checkbox>\n </td>\n </ng-container>\n\n <!-- Id Column -->\n <ng-container matColumnDef=\"id\" [sticky]=\"useSticky\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>\n <ion-label>#</ion-label>\n </th>\n <td mat-cell *matCellDef=\"let row\">{{ row.currentData.id }}</td>\n </ng-container>\n\n <!-- avatar Column -->\n <ng-container matColumnDef=\"avatar\" [sticky]=\"useSticky\">\n <th mat-header-cell *matHeaderCellDef></th>\n <td mat-cell *matCellDef=\"let row\">\n <div class=\"avatar\" *ngIf=\"row.currentData.avatar; else generateIcon\"\n [ngStyle]=\"{'background-image':'url('+row.currentData.avatar+')'}\"></div>\n <ng-template #generateIcon>\n <div class=\"avatar\">\n <svg width=\"38\" width=\"38\" [data-jdenticon-value]=\"row.currentData.id\"></svg>\n </div>\n </ng-template>\n </td>\n </ng-container>\n\n <!-- lastName -->\n <ng-container matColumnDef=\"lastName\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>\n <span translate>USER.LAST_NAME</span>\n </th>\n <td mat-cell *matCellDef=\"let row\" (click)=\"focusColumn='lastName'\">\n <mat-form-field floatLabel=\"never\">\n <input matInput\n [formControl]=\"row.validator.controls['lastName']\"\n [placeholder]=\"'USER.LAST_NAME'|translate\"\n [readonly]=\"!row.editing\"\n [appAutofocus]=\"row.editing && focusColumn==='lastName'\">\n <mat-error *ngIf=\"row.validator.controls['lastName'].hasError('required')\" translate>ERROR.FIELD_REQUIRED</mat-error>\n <mat-error *ngIf=\"row.validator.controls['lastName'].hasError('minlength')\">\n <span>{{'ERROR.FIELD_MIN_LENGTH' | translate: {minLength: 2} }}</span>\n </mat-error>\n </mat-form-field>\n\n </td>\n\n </ng-container>\n\n <!-- firstname -->\n <ng-container matColumnDef=\"firstName\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>\n <span translate>USER.FIRST_NAME</span>\n </th>\n <td mat-cell *matCellDef=\"let row\"\n\n (click)=\"focusColumn='firstName'\">\n <mat-form-field floatLabel=\"never\">\n <input matInput [formControl]=\"row.validator.controls['firstName']\"\n [placeholder]=\"'USER.FIRST_NAME'|translate\"\n [readonly]=\"!row.editing\"\n [appAutofocus]=\"row.editing && focusColumn==='firstName'\">\n <mat-error *ngIf=\"row.validator.controls['firstName'].hasError('required')\" translate>ERROR.FIELD_REQUIRED</mat-error>\n <mat-error *ngIf=\"row.validator.controls['firstName'].hasError('minlength')\">\n <span>{{'ERROR.FIELD_MIN_LENGTH' | translate: {minLength: 2} }}</span>\n </mat-error>\n </mat-form-field>\n </td>\n </ng-container>\n\n <!-- email -->\n <ng-container matColumnDef=\"email\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>\n <span translate>USER.EMAIL</span>\n </th>\n <td mat-cell *matCellDef=\"let row\" (click)=\"focusColumn='email'\">\n <mat-form-field floatLabel=\"never\">\n <input matInput [formControl]=\"row.validator.controls['email']\"\n [placeholder]=\"'USER.EMAIL'|translate\"\n [readonly]=\"!row.editing\"\n [appAutofocus]=\"row.editing && focusColumn==='email'\">\n <mat-error *ngIf=\"row.validator.controls['email'].hasError('required')\" translate>ERROR.FIELD_REQUIRED</mat-error>\n <mat-error *ngIf=\"row.validator.controls['email'].hasError('email')\">\n <span translate>ERROR.FIELD_NOT_VALID_EMAIL</span>\n </mat-error>\n </mat-form-field>\n </td>\n </ng-container>\n\n <!-- additional fields -->\n <ng-container *ngFor=\"let definition of additionalFields\" [matColumnDef]=\"definition.key\">\n <th mat-header-cell *matHeaderCellDef>\n <span>{{definition.label|translate}}</span>\n </th>\n <td mat-cell *matCellDef=\"let row\"\n (click)=\"focusColumn=definition.key\">\n <app-form-field floatLabel=\"never\"\n [definition]=\"definition\"\n [formControl]=\"row.validator.controls[definition.key]\"\n [required]=\"definition.extra?.account?.required\"\n [autofocus]=\"row.editing && focusColumn===definition.ke\">\n </app-form-field>\n </td>\n </ng-container>\n\n <!-- profile column -->\n <ng-container matColumnDef=\"profile\">\n <th mat-header-cell *matHeaderCellDef>\n <span translate>USER.PROFILE</span>\n </th>\n <td mat-cell *matCellDef=\"let row\"\n (click)=\"focusColumn='profile'\">\n <mat-form-field floatLabel=\"never\">\n <mat-select [formControl]=\"row.validator.controls['mainProfile']\"\n [placeholder]=\"'USER.PROFILE'|translate\">\n <mat-option *ngFor=\"let item of profiles\" [value]=\"item\">\n {{ ('USER.PROFILE_ENUM.' + item) | uppercase |translate }}\n </mat-option>\n </mat-select>\n <mat-error *ngIf=\"row.validator.controls['mainProfile'].hasError('required')\" translate>ERROR.FIELD_REQUIRED</mat-error>\n </mat-form-field>\n </td>\n </ng-container>\n\n <!-- Status column -->\n <ng-container matColumnDef=\"status\">\n <th mat-header-cell *matHeaderCellDef>\n <span translate>USER.STATUS</span>\n </th>\n <td mat-cell *matCellDef=\"let row\"\n (click)=\"focusColumn='status'\">\n <mat-form-field floatLabel=\"never\">\n <ion-icon matPrefix *ngIf=\"row.validator.controls['statusId'].value &gt;=0\" [name]=\"statusById[row.validator.controls['statusId'].value]?.icon\"></ion-icon>\n\n <mat-select [formControl]=\"row.validator.controls['statusId']\"\n [placeholder]=\"'REFERENTIAL.STATUS'|translate\">\n <mat-select-trigger>\n <span *ngIf=\"row.validator.controls['statusId'].value &gt;=0\">\n {{ statusById[row.validator.controls['statusId'].value]?.label | translate}}</span>\n </mat-select-trigger>\n <mat-option *ngFor=\"let item of statusList\" [value]=\"item.id\">\n <ion-icon [name]=\"item.icon\"></ion-icon>\n {{ item.label |translate }}\n </mat-option>\n </mat-select>\n <mat-error *ngIf=\"row.validator.controls['statusId'].hasError('required')\" translate>ERROR.FIELD_REQUIRED</mat-error>\n </mat-form-field>\n </td>\n </ng-container>\n\n <!-- username -->\n <ng-container matColumnDef=\"username\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>\n <span translate>USER.USERNAME</span>\n </th>\n <td mat-cell *matCellDef=\"let row\"\n (click)=\"focusColumn='username'\">\n <mat-form-field floatLabel=\"never\" >\n <input matInput [formControl]=\"row.validator.controls.username\"\n [placeholder]=\"'USER.USERNAME'|translate\"\n [readonly]=\"!row.editing\"\n [appAutofocus]=\"row.editing && focusColumn==='username'\">\n <mat-error *ngIf=\"row.validator.controls.username.hasError('required')\" translate>ERROR.FIELD_REQUIRED</mat-error>\n </mat-form-field>\n </td>\n </ng-container>\n\n <!-- username extranet -->\n <ng-container matColumnDef=\"usernameExtranet\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>\n <span translate>USER.USERNAME_EXTRANET</span>\n </th>\n <td mat-cell *matCellDef=\"let row\"\n (click)=\"focusColumn='usernameExtranet'\">\n <mat-form-field floatLabel=\"never\">\n <input matInput [formControl]=\"row.validator.controls.usernameExtranet\"\n [placeholder]=\"'USER.USERNAME_EXTRANET'|translate\"\n [readonly]=\"!row.editing\"\n [appAutofocus]=\"row.editing && focusColumn==='usernameExtranet'\">\n <mat-error *ngIf=\"row.validator.controls.usernameExtranet.hasError('required')\" translate>ERROR.FIELD_REQUIRED</mat-error>\n </mat-form-field>\n </td>\n </ng-container>\n\n <!-- pubkey -->\n <ng-container matColumnDef=\"pubkey\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>\n <span translate>USER.PUBKEY</span>\n </th>\n <td mat-cell *matCellDef=\"let row\"\n [title]=\"row.validator.controls['pubkey'].value\"\n (click)=\"focusColumn='pubkey'\">\n <mat-form-field floatLabel=\"never\" >\n <input matInput [formControl]=\"row.validator.controls['pubkey']\" [placeholder]=\"'USER.PUBKEY'|translate\"\n [readonly]=\"!row.editing\" autocomplete=\"off\">\n <mat-error *ngIf=\"row.validator.controls['pubkey'].hasError('required')\" translate>ERROR.FIELD_REQUIRED</mat-error>\n <mat-error *ngIf=\"row.validator.controls['pubkey'].hasError('pubkey')\">\n <span translate>ERROR.FIELD_NOT_VALID_PUBKEY</span>\n </mat-error>\n </mat-form-field>\n </td>\n </ng-container>\n\n <!-- Actions buttons column -->\n <app-actions-column [stickyEnd]=\"useSticky\"\n (optionsClick)=\"openSelectColumnsModal($event)\"\n (cancelOrDeleteClick)=\"cancelOrDelete($event.event, $event.row)\"\n (confirmAndAddClick)=\"confirmAndAdd($event.event, $event.row)\"\n (backward)=\"confirmAndBackward($event.event, $event.row)\"\n (forward)=\"confirmAndForward($event.event, $event.row)\"\n [canCancel]=\"false\">\n </app-actions-column>\n\n <tr mat-header-row *matHeaderRowDef=\"displayedColumns; sticky: true\"></tr>\n <tr mat-row *matRowDef=\"let row; columns: displayedColumns;\"\n [class.mat-row-error]=\"row.validator.invalid\"\n [class.mat-row-dirty]=\"row.currentData.dirty\"\n [class.mat-row-disabled]=\"!row.editing\"\n (click)=\"clickRow($event, row)\"\n (keydown.escape)=\"escapeEditingRow($event)\"\n [cdkTrapFocus]=\"row.validator.invalid\"></tr>\n </table>\n\n <ng-container *ngIf=\"loadingSubject|async; else noResult\">\n <ion-item>\n <ion-skeleton-text animated></ion-skeleton-text>\n </ion-item>\n </ng-container>\n\n <ng-template #noResult>\n <ion-item *ngIf=\"totalRowCount === 0\">\n <ion-text color=\"danger\" class=\"text-italic\" translate>COMMON.NO_RESULT</ion-text>\n </ion-item>\n </ng-template>\n </div>\n</ion-content>\n\n<ion-footer>\n <mat-paginator class=\"mat-paginator-footer\"\n [length]=\"totalRowCount\" [pageSize]=\"defaultPageSize\"\n [pageSizeOptions]=\"defaultPageSizeOptions\" showFirstLastButtons>\n </mat-paginator>\n\n <app-form-buttons-bar *ngIf=\"!mobile && inlineEdition\"\n (onCancel)=\"onRefresh.emit()\" (onSave)=\"save()\" [disabled]=\"(loadingSubject|async) || !dirty\"></app-form-buttons-bar>\n</ion-footer>\n\n<ion-fab slot=\"fixed\" vertical=\"bottom\" horizontal=\"end\" *ngIf=\"mobile\">\n <ion-fab-button color=\"tertiary\" (click)=\"addRow()\">\n <ion-icon name=\"add\"></ion-icon>\n </ion-fab-button>\n</ion-fab>\n",
27277
27300
  providers: [
27278
27301
  { provide: ngxMaterialTable.ValidatorService, useExisting: PersonValidatorService }
27279
27302
  ],
27280
27303
  animations: [slideUpDownAnimation],
27281
27304
  changeDetection: i0.ChangeDetectionStrategy.OnPush,
27282
- styles: [".mat-expansion-panel{margin-bottom:5px}.mat-expansion-panel .form-container mat-form-field{width:100%}.mat-expansion-panel mat-action-row ion-label{line-height:36px}.table-container{height:100%}.mat-table .mat-cell .avatar{height:40px;width:40px;margin:2px 0 0;background-size:cover;background-repeat:no-repeat;background-position:50%}.mat-table .mat-column-avatar{min-width:50px}.mat-table .mat-column-profile{min-width:110px}.mat-table .mat-column-status{min-width:130px}.mat-table .mat-column-department{min-width:150px}"]
27305
+ styles: [".mat-expansion-panel{margin-bottom:5px}.mat-expansion-panel .form-container mat-form-field{width:100%}.mat-expansion-panel mat-action-row ion-label{line-height:36px}.table-container{height:100%}.mat-table .mat-cell .avatar{height:40px;width:40px;margin:2px 0 0;background-size:cover;background-repeat:no-repeat;background-position:50%}.mat-table .mat-column-id{width:30px}.mat-table .mat-column-avatar{width:50px}.mat-table .mat-column-avatar .avatar{border-radius:5px;border:1px solid rgba(var(--ion-color-secondary-rgb),.5)}.mat-table .mat-column-firstName,.mat-table .mat-column-lastName{min-width:80px}.mat-table .mat-column-email{min-width:120px}.mat-table .mat-column-profile{min-width:110px}.mat-table .mat-column-status{min-width:130px}.mat-table .mat-column-department{min-width:150px}"]
27283
27306
  },] }
27284
27307
  ];
27285
27308
  UsersPage.ctorParameters = function () { return [
@@ -27299,6 +27322,7 @@
27299
27322
  { type: undefined, decorators: [{ type: i0.Inject, args: [ENVIRONMENT,] }] }
27300
27323
  ]; };
27301
27324
  UsersPage.propDecorators = {
27325
+ useSticky: [{ type: i0.Input }],
27302
27326
  filterExpansionPanel: [{ type: i0.ViewChild, args: [expansion.MatExpansionPanel, { static: true },] }]
27303
27327
  };
27304
27328
 
@@ -27572,6 +27596,7 @@
27572
27596
  exports.SharedTestingModule = SharedTestingModule;
27573
27597
  exports.SharedValidators = SharedValidators;
27574
27598
  exports.SocialModule = SocialModule;
27599
+ exports.StartableService = StartableService;
27575
27600
  exports.StatusById = StatusById;
27576
27601
  exports.StatusIds = StatusIds;
27577
27602
  exports.StatusList = StatusList;