@sumaris-net/ngx-components 0.24.3 → 0.25.3

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 (40) hide show
  1. package/bundles/sumaris-net.ngx-components.umd.js +1100 -906
  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 +26 -74
  9. package/esm2015/src/app/core/install/install-upgrade-card.component.js +134 -46
  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/model/peer.model.js +15 -2
  14. package/esm2015/src/app/core/services/network.service.js +134 -169
  15. package/esm2015/src/app/core/services/platform.service.js +46 -17
  16. package/esm2015/src/app/core/services/storage/entities-storage.service.js +59 -63
  17. package/esm2015/src/app/shared/audio/audio.js +10 -39
  18. package/esm2015/src/app/shared/material/autocomplete/testing/autocomplete.test.js +2 -2
  19. package/esm2015/src/app/shared/services/startable-service.class.js +77 -0
  20. package/esm2015/src/environments/environment.class.js +1 -1
  21. package/esm2015/src/environments/environment.js +5 -1
  22. package/fesm2015/sumaris-net.ngx-components.js +724 -649
  23. package/fesm2015/sumaris-net.ngx-components.js.map +1 -1
  24. package/package.json +1 -1
  25. package/public_api.d.ts +1 -0
  26. package/src/app/admin/users/list/users.d.ts +1 -0
  27. package/src/app/core/graphql/graphql.service.d.ts +5 -13
  28. package/src/app/core/install/install-upgrade-card.component.d.ts +14 -7
  29. package/src/app/core/services/account.service.d.ts +11 -10
  30. package/src/app/core/services/config.service.d.ts +6 -5
  31. package/src/app/core/services/local-settings.service.d.ts +4 -9
  32. package/src/app/core/services/model/peer.model.d.ts +1 -0
  33. package/src/app/core/services/network.service.d.ts +27 -38
  34. package/src/app/core/services/platform.service.d.ts +3 -1
  35. package/src/app/core/services/storage/entities-storage.service.d.ts +13 -13
  36. package/src/app/shared/audio/audio.d.ts +4 -8
  37. package/src/app/shared/services/startable-service.class.d.ts +26 -0
  38. package/src/assets/i18n/fr.json +3 -0
  39. package/src/environments/environment.class.d.ts +1 -0
  40. package/sumaris-net.ngx-components.metadata.json +1 -1
@@ -7732,39 +7732,134 @@
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
+ Object.defineProperty(StartableService.prototype, "starting", {
7820
+ get: function () {
7821
+ return !!this._startPromise;
7822
+ },
7823
+ enumerable: false,
7824
+ configurable: true
7825
+ });
7826
+ StartableService.prototype.ready = function () {
7827
+ if (this._started)
7828
+ return Promise.resolve(this._data);
7829
+ return this.start();
7830
+ };
7831
+ StartableService.prototype.ngOnStop = function () {
7832
+ return __awaiter(this, void 0, void 0, function () {
7833
+ return __generator(this, function (_a) {
7834
+ return [2 /*return*/];
7835
+ });
7836
+ });
7837
+ };
7838
+ return StartableService;
7839
+ }());
7840
+ StartableService.ctorParameters = function () { return [
7841
+ { type: undefined, decorators: [{ type: i0.Optional }] }
7842
+ ]; };
7843
+
7844
+ var SYSTEM_SOUNDS = [
7845
+ { id: 'beep-confirm', assetPath: 'assets/audio/beep-confirm.mp3', vibration: 250 },
7846
+ { id: 'beep-error', assetPath: 'assets/audio/beep-error.mp3', vibration: 1000 },
7847
+ { id: 'startup', assetPath: 'assets/audio/unfa-ping.mp3', vibration: [1, 500, 250, 750] },
7848
+ ];
7849
+ var AudioProvider = /** @class */ (function (_super) {
7850
+ __extends(AudioProvider, _super);
7851
+ function AudioProvider(platform, nativeAudio, vibration, audioManagement) {
7852
+ var _this = _super.call(this, platform) || this;
7853
+ _this.platform = platform;
7854
+ _this.nativeAudio = nativeAudio;
7855
+ _this.vibration = vibration;
7856
+ _this.audioManagement = audioManagement;
7857
+ _this._audioMode = ngx$1.AudioManagement.AudioMode.NORMAL;
7858
+ _this._preloadedSounds = {};
7859
+ _this._htmlAudioCache = {};
7860
+ _this.start();
7861
+ return _this;
7862
+ }
7768
7863
  AudioProvider.prototype.playBeepConfirm = function () {
7769
7864
  return this.play('beep-confirm', {
7770
7865
  // Vibrate only if in vibration mode
@@ -7828,7 +7923,7 @@
7828
7923
  return __generator(this, function (_a) {
7829
7924
  switch (_a.label) {
7830
7925
  case 0:
7831
- if (!!this._started) return [3 /*break*/, 2];
7926
+ if (!!this.started) return [3 /*break*/, 2];
7832
7927
  return [4 /*yield*/, this.ready()];
7833
7928
  case 1:
7834
7929
  _a.sent();
@@ -7912,7 +8007,7 @@
7912
8007
  return __generator(this, function (_a) {
7913
8008
  switch (_a.label) {
7914
8009
  case 0:
7915
- if (!!this._started) return [3 /*break*/, 2];
8010
+ if (!!this.started) return [3 /*break*/, 2];
7916
8011
  return [4 /*yield*/, this.ready()];
7917
8012
  case 1:
7918
8013
  _a.sent();
@@ -7927,15 +8022,10 @@
7927
8022
  });
7928
8023
  };
7929
8024
  /* -- 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 () {
8025
+ AudioProvider.prototype.ngOnStart = function () {
8026
+ return __awaiter(this, void 0, void 0, function () {
8027
+ var cordova;
8028
+ var _this = this;
7939
8029
  return __generator(this, function (_a) {
7940
8030
  switch (_a.label) {
7941
8031
  case 0:
@@ -7947,33 +8037,21 @@
7947
8037
  case 1:
7948
8038
  _a.sent();
7949
8039
  _a.label = 2;
7950
- case 2: return [2 /*return*/];
8040
+ case 2:
8041
+ // Pre-loading system sounds
8042
+ console.debug('[audio] Preloading audio sounds...');
8043
+ return [4 /*yield*/, Promise.all(SYSTEM_SOUNDS.map(function (s) {
8044
+ // Disable vibration is cordova not enabled
8045
+ if (!cordova)
8046
+ s.vibration = undefined;
8047
+ return _this.preload(s);
8048
+ }))];
8049
+ case 3:
8050
+ _a.sent();
8051
+ return [2 /*return*/];
7951
8052
  }
7952
8053
  });
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
8054
  });
7976
- return this._startPromise;
7977
8055
  };
7978
8056
  AudioProvider.prototype.readAudioMode = function () {
7979
8057
  return __awaiter(this, void 0, void 0, function () {
@@ -7991,7 +8069,7 @@
7991
8069
  });
7992
8070
  };
7993
8071
  return AudioProvider;
7994
- }());
8072
+ }(StartableService));
7995
8073
  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
8074
  AudioProvider.decorators = [
7997
8075
  { type: i0.Injectable, args: [{ providedIn: 'root' },] }
@@ -8977,6 +9055,10 @@
8977
9055
  {
8978
9056
  host: 'sih.sfa.sc',
8979
9057
  port: 80
9058
+ },
9059
+ {
9060
+ host: 'test.sumaris.net',
9061
+ port: 443
8980
9062
  }
8981
9063
  ],
8982
9064
  defaultAppName: 'SUMARiS',
@@ -9130,6 +9212,24 @@
9130
9212
  path: noTrailingSlash(url.pathname)
9131
9213
  });
9132
9214
  };
9215
+ Peer.path = function (peer) {
9216
+ var _a;
9217
+ var paths = [];
9218
+ for (var _i = 1; _i < arguments.length; _i++) {
9219
+ paths[_i - 1] = arguments[_i];
9220
+ }
9221
+ if (!peer)
9222
+ throw new Error('Missing required argument \'peer\'!');
9223
+ // Remove starting slashes
9224
+ paths = (paths || []).map(function (path) {
9225
+ if (path.startsWith('./'))
9226
+ return path.substring(2);
9227
+ if (path.startsWith('/'))
9228
+ return path.substring(1);
9229
+ return path;
9230
+ }).filter(isNotNilOrBlank);
9231
+ return (_a = [noTrailingSlash(Peer_1.fromObject(peer).url)]).concat.apply(_a, __spread(paths)).join('/');
9232
+ };
9133
9233
  Peer.prototype.asObject = function (options) {
9134
9234
  return _super.prototype.asObject.call(this, options);
9135
9235
  };
@@ -9589,110 +9689,87 @@
9589
9689
  };
9590
9690
  var APP_LOCAL_SETTINGS = new i0.InjectionToken('DefaultLocalSettings');
9591
9691
  var APP_LOCAL_SETTINGS_OPTIONS = new i0.InjectionToken('LocalSettingsOptions');
9592
- var LocalSettingsService = /** @class */ (function () {
9692
+ var LocalSettingsService = /** @class */ (function (_super) {
9693
+ __extends(LocalSettingsService, _super);
9593
9694
  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)
9695
+ var _this = _super.call(this, platform) || this;
9696
+ _this.translate = translate;
9697
+ _this.platform = platform;
9698
+ _this.storage = storage;
9699
+ _this.environment = environment;
9700
+ _this.defaultSettings = defaultSettings;
9701
+ _this.onChange = new rxjs.Subject();
9702
+ _this.defaultSettings = Object.assign(Object.assign({}, DEFAULT_SETTINGS), _this.defaultSettings);
9703
+ _this._optionDefs = Object.values(defaultOptionsMap);
9704
+ _this.resetData();
9705
+ _this._debug = !environment.production;
9706
+ if (_this._debug)
9606
9707
  console.debug('[settings] Creating service');
9708
+ return _this;
9607
9709
  }
9608
9710
  Object.defineProperty(LocalSettingsService.prototype, "settings", {
9609
9711
  get: function () {
9610
- return this.data || this.defaultSettings;
9712
+ return this._data || this.defaultSettings;
9611
9713
  },
9612
9714
  enumerable: false,
9613
9715
  configurable: true
9614
9716
  });
9615
9717
  Object.defineProperty(LocalSettingsService.prototype, "locale", {
9616
9718
  get: function () {
9617
- return this.data && this.data.locale || this.translate.currentLang || this.translate.defaultLang;
9719
+ return this._data && this._data.locale || this.translate.currentLang || this.translate.defaultLang;
9618
9720
  },
9619
9721
  enumerable: false,
9620
9722
  configurable: true
9621
9723
  });
9622
9724
  Object.defineProperty(LocalSettingsService.prototype, "latLongFormat", {
9623
9725
  get: function () {
9624
- return this.data && this.data.latLongFormat || 'DDMM';
9726
+ return this._data && this._data.latLongFormat || 'DDMM';
9625
9727
  },
9626
9728
  enumerable: false,
9627
9729
  configurable: true
9628
9730
  });
9629
9731
  Object.defineProperty(LocalSettingsService.prototype, "usageMode", {
9630
9732
  get: function () {
9631
- return (this.data && this.data.usageMode || (this.mobile ? 'FIELD' : 'DESK'));
9733
+ return (this._data && this._data.usageMode || (this.mobile ? 'FIELD' : 'DESK'));
9632
9734
  },
9633
9735
  enumerable: false,
9634
9736
  configurable: true
9635
9737
  });
9636
9738
  Object.defineProperty(LocalSettingsService.prototype, "mobile", {
9637
9739
  get: function () {
9638
- return this.data && toBoolean(this.data.mobile, this.platform.is('mobile'));
9740
+ return this._data && toBoolean(this._data.mobile, this.platform.is('mobile'));
9639
9741
  },
9640
9742
  set: function (value) {
9641
- this.data.mobile = value;
9743
+ this._data.mobile = value;
9642
9744
  },
9643
9745
  enumerable: false,
9644
9746
  configurable: true
9645
9747
  });
9646
9748
  Object.defineProperty(LocalSettingsService.prototype, "touchUi", {
9647
9749
  get: function () {
9648
- return this.data.touchUi;
9750
+ return this._data.touchUi;
9649
9751
  },
9650
9752
  set: function (value) {
9651
- this.data.touchUi = value;
9753
+ this._data.touchUi = value;
9652
9754
  },
9653
9755
  enumerable: false,
9654
9756
  configurable: true
9655
9757
  });
9656
9758
  Object.defineProperty(LocalSettingsService.prototype, "pageHistory", {
9657
9759
  get: function () {
9658
- return (this.data && this.data.pageHistory || []);
9760
+ return (this._data && this._data.pageHistory || []);
9659
9761
  },
9660
9762
  enumerable: false,
9661
9763
  configurable: true
9662
9764
  });
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...');
9765
+ LocalSettingsService.prototype.ngOnStart = function () {
9766
+ console.info('[settings] Starting service...');
9670
9767
  // 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();
9768
+ this._data.mobile = isNotNil(this._data.mobile) ? this._data.mobile : this.platform.is('mobile');
9769
+ this._data.touchUi = this._data.mobile || this.platform.is('phablet') || this.platform.is('tablet');
9770
+ this._data.usageMode = this.platform.is('android') ? 'FIELD' : 'DESK'; // FIELD by default if Android
9771
+ // Restoring local settings
9772
+ return this.restoreLocally();
9696
9773
  };
9697
9774
  LocalSettingsService.prototype.isUsageMode = function (mode) {
9698
9775
  return this.usageMode === mode;
@@ -9702,10 +9779,12 @@
9702
9779
  };
9703
9780
  LocalSettingsService.prototype.restoreLocally = function () {
9704
9781
  return __awaiter(this, void 0, void 0, function () {
9705
- var settingsStr, restoredData_1;
9782
+ var data, settingsStr, restoredData_1;
9706
9783
  return __generator(this, function (_a) {
9707
9784
  switch (_a.label) {
9708
- case 0: return [4 /*yield*/, this.storage.get(SETTINGS_STORAGE_KEY)];
9785
+ case 0:
9786
+ data = this._data || {};
9787
+ return [4 /*yield*/, this.storage.get(SETTINGS_STORAGE_KEY)];
9709
9788
  case 1:
9710
9789
  settingsStr = _a.sent();
9711
9790
  // Restore local settings (or keep old settings)
@@ -9716,30 +9795,31 @@
9716
9795
  SETTINGS_TRANSIENT_PROPERTIES.forEach(function (transientKey) {
9717
9796
  delete restoredData_1[transientKey];
9718
9797
  });
9719
- this.data = Object.assign(this.data, restoredData_1);
9798
+ // Merge into existing data
9799
+ data = Object.assign(data, restoredData_1);
9720
9800
  }
9721
9801
  // Emit event
9722
- this.onChange.next(this.data);
9723
- return [2 /*return*/, this.data];
9802
+ this.onChange.next(data);
9803
+ return [2 /*return*/, data];
9724
9804
  }
9725
9805
  });
9726
9806
  });
9727
9807
  };
9728
9808
  LocalSettingsService.prototype.setProperty = function (keyOrDef, value) {
9729
- if (!this.data)
9809
+ if (!this._data)
9730
9810
  return;
9731
9811
  if (typeof keyOrDef === 'object') {
9732
9812
  this.setProperty(keyOrDef.key, value);
9733
9813
  return;
9734
9814
  }
9735
- this.data.properties = this.data.properties || {};
9736
- this.data.properties[keyOrDef] = isNil(value) ? undefined : value.toString();
9815
+ this._data.properties = this._data.properties || {};
9816
+ this._data.properties[keyOrDef] = isNil(value) ? undefined : value.toString();
9737
9817
  };
9738
9818
  LocalSettingsService.prototype.getProperty = function (keyOrDef, defaultValue) {
9739
9819
  if (typeof keyOrDef === 'object') {
9740
9820
  return this.getProperty(keyOrDef.key, isNil(defaultValue) ? keyOrDef.defaultValue : defaultValue);
9741
9821
  }
9742
- var value = this.data && this.data.properties && this.data.properties[keyOrDef];
9822
+ var value = this._data && this._data.properties && this._data.properties[keyOrDef];
9743
9823
  return isNotNil(value) ? value : defaultValue;
9744
9824
  };
9745
9825
  LocalSettingsService.prototype.getPropertyAsBoolean = function (definition, defaultValue) {
@@ -9765,19 +9845,25 @@
9765
9845
  return __generator(this, function (_a) {
9766
9846
  switch (_a.label) {
9767
9847
  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)];
9848
+ if (!!this.started) return [3 /*break*/, 2];
9849
+ return [4 /*yield*/, this.ready()];
9771
9850
  case 1:
9772
9851
  _a.sent();
9773
- return [3 /*break*/, 3];
9852
+ _a.label = 2;
9774
9853
  case 2:
9775
- this.persistLocally(); // No AWAIT
9776
- _a.label = 3;
9854
+ this._data = Object.assign(Object.assign({}, this._data), settings);
9855
+ if (!(opts && opts.persistImmediate)) return [3 /*break*/, 4];
9856
+ return [4 /*yield*/, this.persistLocally(true)];
9777
9857
  case 3:
9858
+ _a.sent();
9859
+ return [3 /*break*/, 5];
9860
+ case 4:
9861
+ this.persistLocally(); // No AWAIT
9862
+ _a.label = 5;
9863
+ case 5:
9778
9864
  // Emit event
9779
9865
  if (!opts || opts.emitEvent !== false) {
9780
- this.onChange.next(this.data);
9866
+ this.onChange.next(this._data);
9781
9867
  }
9782
9868
  return [2 /*return*/];
9783
9869
  }
@@ -9801,27 +9887,27 @@
9801
9887
  });
9802
9888
  };
9803
9889
  LocalSettingsService.prototype.getPageSettings = function (pageId, propertyName) {
9804
- if (!this.data || !this.data.pages)
9890
+ if (!this._data || !this._data.pages)
9805
9891
  return undefined;
9806
9892
  var key = pageId.replace(/[/]/g, '__');
9807
9893
  if (isNotNilOrBlank(propertyName)) {
9808
- return getPropertyByPath(this.data.pages, key + '.' + propertyName);
9894
+ return getPropertyByPath(this._data.pages, key + '.' + propertyName);
9809
9895
  }
9810
- return this.data.pages[key];
9896
+ return this._data.pages[key];
9811
9897
  };
9812
9898
  LocalSettingsService.prototype.savePageSetting = function (pageId, value, propertyName) {
9813
9899
  return __awaiter(this, void 0, void 0, function () {
9814
9900
  var key;
9815
9901
  return __generator(this, function (_a) {
9816
- this.data = this.data || this.defaultSettings;
9817
- this.data.pages = this.data.pages || {};
9902
+ this._data = this._data || this.defaultSettings;
9903
+ this._data.pages = this._data.pages || {};
9818
9904
  key = pageId.replace(/[/]/g, '__');
9819
9905
  if (propertyName) {
9820
- this.data.pages[key] = this.data.pages[key] || {};
9821
- this.data.pages[key][propertyName] = value;
9906
+ this._data.pages[key] = this._data.pages[key] || {};
9907
+ this._data.pages[key][propertyName] = value;
9822
9908
  }
9823
9909
  else {
9824
- this.data.pages[key] = value;
9910
+ this._data.pages[key] = value;
9825
9911
  }
9826
9912
  // Update local settings
9827
9913
  this.persistLocally();
@@ -9830,13 +9916,13 @@
9830
9916
  });
9831
9917
  };
9832
9918
  LocalSettingsService.prototype.getOfflineFeature = function (featureName) {
9833
- if (!this.data || !this.data.offlineFeatures || isEmptyArray(this.data.offlineFeatures))
9919
+ if (!this._data || !this._data.offlineFeatures || isEmptyArray(this._data.offlineFeatures))
9834
9920
  return undefined;
9835
9921
  if (!featureName)
9836
9922
  throw Error('Missing \'featureName\' argument');
9837
9923
  featureName = featureName.toLowerCase();
9838
9924
  var featurePrefix = featureName + '#';
9839
- var feature = this.data.offlineFeatures.find(function (f) {
9925
+ var feature = this._data.offlineFeatures.find(function (f) {
9840
9926
  if (typeof f === 'string')
9841
9927
  return f.toLowerCase().startsWith(featurePrefix);
9842
9928
  if (typeof f === 'object' && f.name)
@@ -9859,24 +9945,24 @@
9859
9945
  LocalSettingsService.prototype.hasOfflineFeature = function (featureName) {
9860
9946
  if (featureName)
9861
9947
  return isNotNil(this.getOfflineFeature(featureName));
9862
- return this.data && isNotEmptyArray(this.data.offlineFeatures);
9948
+ return this._data && isNotEmptyArray(this._data.offlineFeatures);
9863
9949
  };
9864
9950
  LocalSettingsService.prototype.saveOfflineFeature = function (feature) {
9865
- this.data = this.data || this.defaultSettings;
9866
- this.data.offlineFeatures = this.data.offlineFeatures || [];
9951
+ this._data = this._data || this.defaultSettings;
9952
+ this._data.offlineFeatures = this._data.offlineFeatures || [];
9867
9953
  feature.name = feature.name.toLowerCase();
9868
9954
  var featurePrefix = feature.name + '#';
9869
- var existingIndex = this.data.offlineFeatures.findIndex(function (f) {
9955
+ var existingIndex = this._data.offlineFeatures.findIndex(function (f) {
9870
9956
  if (typeof f === 'string')
9871
9957
  return f.toLowerCase().startsWith(featurePrefix);
9872
9958
  if (typeof f === 'object' && f.name)
9873
9959
  return f.name === feature.name;
9874
9960
  });
9875
9961
  if (existingIndex !== -1) {
9876
- this.data.offlineFeatures[existingIndex] = feature;
9962
+ this._data.offlineFeatures[existingIndex] = feature;
9877
9963
  }
9878
9964
  else {
9879
- this.data.offlineFeatures.push(feature);
9965
+ this._data.offlineFeatures.push(feature);
9880
9966
  }
9881
9967
  // Update local settings
9882
9968
  this.persistLocally();
@@ -9895,14 +9981,14 @@
9895
9981
  this.saveOfflineFeature(feature);
9896
9982
  };
9897
9983
  LocalSettingsService.prototype.removeOfflineFeatures = function () {
9898
- if (this.data && this.data.offlineFeatures) {
9899
- this.data.offlineFeatures = [];
9984
+ if (this._data && this._data.offlineFeatures) {
9985
+ this._data.offlineFeatures = [];
9900
9986
  // Update local settings
9901
9987
  this.persistLocally();
9902
9988
  }
9903
9989
  };
9904
9990
  LocalSettingsService.prototype.getFieldDisplayAttributes = function (fieldName, defaultAttributes) {
9905
- var value = this.data && this.data.properties && this.data.properties["sumaris.field." + fieldName + ".attributes"];
9991
+ var value = this._data && this._data.properties && this._data.properties["sumaris.field." + fieldName + ".attributes"];
9906
9992
  // Nothing found in settings: return defaults
9907
9993
  if (!value)
9908
9994
  return defaultAttributes || ['label', 'name'];
@@ -9940,7 +10026,7 @@
9940
10026
  // If not inside recursive call: fill page history defaults
9941
10027
  if (!pageHistory)
9942
10028
  this.fillPageHistoryDefaults(page, opts);
9943
- pageHistory = pageHistory || this.data.pageHistory;
10029
+ pageHistory = pageHistory || this._data.pageHistory;
9944
10030
  index = pageHistory.findIndex(function (p) { return (
9945
10031
  // same path
9946
10032
  p.path === page.path
@@ -9975,10 +10061,10 @@
9975
10061
  _a.sent();
9976
10062
  _a.label = 4;
9977
10063
  case 4:
9978
- if (!(pageHistory === this.data.pageHistory)) return [3 /*break*/, 6];
10064
+ if (!(pageHistory === this._data.pageHistory)) return [3 /*break*/, 6];
9979
10065
  // 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);
10066
+ if (this._data.pageHistory.length > this._data.pageHistoryMaxSize) {
10067
+ removedPages = pageHistory.splice(this._data.pageHistoryMaxSize, pageHistory.length - this._data.pageHistoryMaxSize);
9982
10068
  console.debug('[settings] Pages removed from history: ', removedPages);
9983
10069
  }
9984
10070
  // Apply new value
@@ -10000,7 +10086,7 @@
10000
10086
  return __generator(this, function (_a) {
10001
10087
  switch (_a.label) {
10002
10088
  case 0:
10003
- pageHistory = pageHistory || this.data.pageHistory;
10089
+ pageHistory = pageHistory || this._data.pageHistory;
10004
10090
  index = pageHistory.findIndex(function (p) { return p.path === path; });
10005
10091
  found = index !== -1;
10006
10092
  if (found) {
@@ -10015,9 +10101,9 @@
10015
10101
  .filter(isNotEmptyArray)
10016
10102
  .findIndex(function (children) { return _this.removePageHistory(path, opts, children); }) !== -1;
10017
10103
  }
10018
- if (!(found && pageHistory === this.data.pageHistory)) return [3 /*break*/, 2];
10104
+ if (!(found && pageHistory === this._data.pageHistory)) return [3 /*break*/, 2];
10019
10105
  // Apply changes
10020
- return [4 /*yield*/, this.applyProperty('pageHistory', this.data.pageHistory)];
10106
+ return [4 /*yield*/, this.applyProperty('pageHistory', this._data.pageHistory)];
10021
10107
  case 1:
10022
10108
  // Apply changes
10023
10109
  _a.sent();
@@ -10044,27 +10130,27 @@
10044
10130
  };
10045
10131
  /* -- Protected methods -- */
10046
10132
  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 = [];
10133
+ this._data = Object.assign(Object.assign({}, this._data), this.defaultSettings);
10134
+ this._data.locale = this.translate.currentLang || this.translate.defaultLang;
10135
+ this._data.mobile = undefined;
10136
+ this._data.usageMode = undefined;
10137
+ this._data.pageHistory = [];
10052
10138
  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);
10139
+ this._data.peerUrl = defaultPeer && defaultPeer.url || undefined;
10140
+ if (this.started)
10141
+ this.onChange.next(this._data);
10056
10142
  };
10057
10143
  LocalSettingsService.prototype.persistLocally = function (immediate) {
10058
10144
  var _this = this;
10059
10145
  // Execute immediate
10060
10146
  if (immediate) {
10061
- if (!this.data) {
10147
+ if (!this._data) {
10062
10148
  console.debug('[settings] Removing local settings from storage');
10063
10149
  return this.storage.remove(SETTINGS_STORAGE_KEY);
10064
10150
  }
10065
10151
  else {
10066
- console.debug('[settings] Store local settings', this.data);
10067
- return this.storage.set(SETTINGS_STORAGE_KEY, JSON.stringify(this.data));
10152
+ console.debug('[settings] Store local settings', this._data);
10153
+ return this.storage.set(SETTINGS_STORAGE_KEY, JSON.stringify(this._data));
10068
10154
  }
10069
10155
  }
10070
10156
  // Execute with delay
@@ -10074,7 +10160,7 @@
10074
10160
  this._$persist = new i0.EventEmitter(true);
10075
10161
  this._$persist
10076
10162
  .pipe(operators.debounceTime(2000), // add a delay of 2s
10077
- operators.filter(function () { return _this._started; }))
10163
+ operators.filter(function () { return _this.started; }))
10078
10164
  .subscribe(function () { return _this.persistLocally(true); });
10079
10165
  }
10080
10166
  this._$persist.emit();
@@ -10100,7 +10186,7 @@
10100
10186
  return page;
10101
10187
  };
10102
10188
  return LocalSettingsService;
10103
- }());
10189
+ }(StartableService));
10104
10190
  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
10191
  LocalSettingsService.decorators = [
10106
10192
  { type: i0.Injectable, args: [{
@@ -10271,41 +10357,42 @@
10271
10357
  MOBILE: 1000,
10272
10358
  DESKTOP: 1000
10273
10359
  }*/
10274
- var NetworkService = /** @class */ (function () {
10360
+ var NetworkService = /** @class */ (function (_super) {
10361
+ __extends(NetworkService, _super);
10275
10362
  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
10363
+ var _this = _super.call(this, platform) || this;
10364
+ _this._document = _document;
10365
+ _this.platform = platform;
10366
+ _this.modalCtrl = modalCtrl;
10367
+ _this.cryptoService = cryptoService;
10368
+ _this.storage = storage;
10369
+ _this.settings = settings;
10370
+ _this.cache = cache;
10371
+ _this.http = http;
10372
+ _this.environment = environment;
10373
+ _this.network = network;
10374
+ _this.splashScreen = splashScreen;
10375
+ _this.translate = translate;
10376
+ _this.toastController = toastController;
10377
+ _this.onPeerChanges = _this.onStart.pipe(operators.map(function (peer) { return peer && peer.url; }), operators.filter(isNotNilOrBlank), operators.distinctUntilChanged());
10378
+ _this.onNetworkStatusChanges = new rxjs.BehaviorSubject(null);
10379
+ _this.onResetNetworkCache = new i0.EventEmitter(true);
10380
+ _this._subscription = new rxjs.Subscription();
10381
+ _this._listeners = {};
10382
+ _this._mobile = _this.platform.is('mobile');
10383
+ if (_this._mobile) {
10384
+ _this._timerRefreshPeriod = NetworkRefreshTimerPeriod.MOBILE;
10385
+ _this._timerRefreshCondition = function () { return _this.online; }; // Check only when online, and stop when offline
10301
10386
  }
10302
10387
  else {
10303
- this._timerRefreshPeriod = NetworkRefreshTimerPeriod.DESKTOP;
10304
- this._timerRefreshCondition = function () { return true; }; // Always check
10388
+ _this._timerRefreshPeriod = NetworkRefreshTimerPeriod.DESKTOP;
10389
+ _this._timerRefreshCondition = function () { return true; }; // Always check
10305
10390
  }
10306
- this.resetData();
10391
+ _this.resetData();
10392
+ _this.onStart.subscribe(function () { return _this.ngOnAfterStart(); });
10307
10393
  // For DEV only
10308
- this._debug = !environment.production;
10394
+ _this._debug = !environment.production;
10395
+ return _this;
10309
10396
  }
10310
10397
  Object.defineProperty(NetworkService.prototype, "online", {
10311
10398
  get: function () {
@@ -10326,24 +10413,18 @@
10326
10413
  // If force offline: return 'none'
10327
10414
  return this._forceOffline && 'none'
10328
10415
  // Else, return device connection type (or unknown)
10329
- || (this._started && this._deviceConnectionType || 'unknown');
10416
+ || (this.started && this._deviceConnectionType || 'unknown');
10330
10417
  },
10331
10418
  enumerable: false,
10332
10419
  configurable: true
10333
10420
  });
10334
10421
  Object.defineProperty(NetworkService.prototype, "peer", {
10335
10422
  get: function () {
10336
- return this._peer && this._peer.clone();
10423
+ return this._data && this._data.clone();
10337
10424
  },
10338
10425
  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;
10426
+ this._startingPeer = peer;
10427
+ this.restart();
10347
10428
  },
10348
10429
  enumerable: false,
10349
10430
  configurable: true
@@ -10368,109 +10449,11 @@
10368
10449
  return this.addListener(eventType, callback);
10369
10450
  }
10370
10451
  };
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) {
10452
+ NetworkService.prototype.tryOnline = function (opts) {
10449
10453
  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
- NetworkService.prototype.tryOnline = function (opts) {
10470
- 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) {
10454
+ var now, showLoadingToast, loadingToast, settings, peer, peerInfo, peerAliveAndCompatible, _b, err_1, online;
10455
+ return __generator(this, function (_c) {
10456
+ switch (_c.label) {
10474
10457
  case 0:
10475
10458
  // If offline mode not forced, and device says there is no connection: skip
10476
10459
  if (!this._forceOffline || this._deviceConnectionType === 'none')
@@ -10481,57 +10464,57 @@
10481
10464
  return [4 /*yield*/, this.showToast({ message: 'NETWORK.INFO.RETRY_TO_CONNECT',
10482
10465
  duration: 10000, onWillPresent: function (t) { return loadingToast = t; } })];
10483
10466
  case 1:
10484
- _b.sent();
10485
- _b.label = 2;
10467
+ _c.sent();
10468
+ _c.label = 2;
10486
10469
  case 2:
10487
- _b.trys.push([2, 10, , 11]);
10470
+ _c.trys.push([2, 10, , 11]);
10488
10471
  console.info('[network] Checking connection to pod...');
10489
10472
  return [4 /*yield*/, this.settings.ready()];
10490
10473
  case 3:
10491
- settings = _b.sent();
10474
+ settings = _c.sent();
10492
10475
  if (!settings.peerUrl)
10493
10476
  return [2 /*return*/, false]; // No peer define. Skip
10494
10477
  peer = exports.Peer.parseUrl(settings.peerUrl);
10495
10478
  return [4 /*yield*/, this.checkPeerAlive(peer)];
10496
10479
  case 4:
10497
- peerInfo = _b.sent();
10498
- _a = peerInfo;
10499
- if (!_a) return [3 /*break*/, 6];
10480
+ peerInfo = _c.sent();
10481
+ _b = peerInfo;
10482
+ if (!_b) return [3 /*break*/, 6];
10500
10483
  return [4 /*yield*/, this.checkPeerCompatible(peerInfo)];
10501
10484
  case 5:
10502
- _a = (_b.sent());
10503
- _b.label = 6;
10485
+ _b = (_c.sent());
10486
+ _c.label = 6;
10504
10487
  case 6:
10505
- peerAliveAndCompatible = _a;
10488
+ peerAliveAndCompatible = _b;
10506
10489
  if (!peerAliveAndCompatible) return [3 /*break*/, 9];
10507
10490
  // Disable the offline mode
10508
10491
  this.setForceOffline(false);
10509
10492
  // Restart
10510
- return [4 /*yield*/, this.restart(peer)];
10493
+ this._startingPeer = peer;
10494
+ return [4 /*yield*/, this.restart()];
10511
10495
  case 7:
10512
- // Restart
10513
- _b.sent();
10496
+ _c.sent();
10514
10497
  // Wait a promise, before recheck
10515
10498
  return [4 /*yield*/, this.emit('beforeTryOnlineFinish', this.online)];
10516
10499
  case 8:
10517
10500
  // Wait a promise, before recheck
10518
- _b.sent();
10519
- _b.label = 9;
10501
+ _c.sent();
10502
+ _c.label = 9;
10520
10503
  case 9: return [3 /*break*/, 11];
10521
10504
  case 10:
10522
- err_1 = _b.sent();
10505
+ err_1 = _c.sent();
10523
10506
  console.error(err_1 && err_1.message || err_1);
10524
10507
  return [3 /*break*/, 11];
10525
10508
  case 11:
10526
10509
  if (!showLoadingToast) return [3 /*break*/, 14];
10527
10510
  return [4 /*yield*/, sleep(2000 - (Date.now() - now))];
10528
10511
  case 12:
10529
- _b.sent();
10512
+ _c.sent();
10530
10513
  if (!loadingToast) return [3 /*break*/, 14];
10531
10514
  return [4 /*yield*/, loadingToast.dismiss()];
10532
10515
  case 13:
10533
- _b.sent();
10534
- _b.label = 14;
10516
+ _c.sent();
10517
+ _c.label = 14;
10535
10518
  case 14:
10536
10519
  online = this.online;
10537
10520
  // Display a toast to user
@@ -10545,7 +10528,7 @@
10545
10528
  // Display toast (without await, because not need to wait toast close event)
10546
10529
  return [2 /*return*/, this.showOfflineToast({ showRetryButton: false })];
10547
10530
  }
10548
- return [2 /*return*/, this._started && online];
10531
+ return [2 /*return*/, this.started && online];
10549
10532
  }
10550
10533
  });
10551
10534
  });
@@ -10553,8 +10536,8 @@
10553
10536
  NetworkService.prototype.showOfflineToast = function (opts) {
10554
10537
  return __awaiter(this, void 0, void 0, function () {
10555
10538
  var toastResult, online;
10556
- return __generator(this, function (_a) {
10557
- switch (_a.label) {
10539
+ return __generator(this, function (_b) {
10540
+ switch (_b.label) {
10558
10541
  case 0:
10559
10542
  if (this.online)
10560
10543
  return [2 /*return*/]; // Skip if online
@@ -10573,7 +10556,7 @@
10573
10556
  ]
10574
10557
  })];
10575
10558
  case 1:
10576
- toastResult = _a.sent();
10559
+ toastResult = _b.sent();
10577
10560
  // User don't click reconnect: return
10578
10561
  if (!toastResult || toastResult.role !== 'refresh')
10579
10562
  return [2 /*return*/, false];
@@ -10586,7 +10569,7 @@
10586
10569
  showLoadingToast: toBoolean(opts && opts.showRetryLoadingToast, true)
10587
10570
  })];
10588
10571
  case 2:
10589
- online = _a.sent();
10572
+ online = _b.sent();
10590
10573
  if (online) {
10591
10574
  // Call success callback (async)
10592
10575
  if (opts && opts.onRetrySuccess) {
@@ -10595,7 +10578,7 @@
10595
10578
  return [2 /*return*/, true];
10596
10579
  }
10597
10580
  opts = Object.assign(Object.assign({}, opts), { showRetryButton: false, showCloseButton: true });
10598
- _a.label = 3;
10581
+ _b.label = 3;
10599
10582
  case 3:
10600
10583
  // Simple toast, without 'await', because not need to wait toast's dismiss
10601
10584
  this.showToast(Object.assign({ message: 'ERROR.NETWORK_REQUIRED', type: 'error' }, opts));
@@ -10604,97 +10587,6 @@
10604
10587
  });
10605
10588
  });
10606
10589
  };
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
10590
  /**
10699
10591
  * Check if the peer is alive
10700
10592
  *
@@ -10703,24 +10595,24 @@
10703
10595
  NetworkService.prototype.checkPeerAlive = function (peer, opts) {
10704
10596
  return __awaiter(this, void 0, void 0, function () {
10705
10597
  var settings, err_2;
10706
- return __generator(this, function (_a) {
10707
- switch (_a.label) {
10598
+ return __generator(this, function (_b) {
10599
+ switch (_b.label) {
10708
10600
  case 0:
10709
10601
  peer = peer || this.peer;
10710
10602
  if (!!peer) return [3 /*break*/, 2];
10711
10603
  return [4 /*yield*/, this.settings.ready()];
10712
10604
  case 1:
10713
- settings = _a.sent();
10605
+ settings = _b.sent();
10714
10606
  if (!settings.peerUrl)
10715
10607
  return [2 /*return*/, undefined]; // No peer define. Skip
10716
10608
  peer = exports.Peer.parseUrl(settings.peerUrl);
10717
- _a.label = 2;
10609
+ _b.label = 2;
10718
10610
  case 2:
10719
- _a.trys.push([2, 4, , 5]);
10611
+ _b.trys.push([2, 4, , 5]);
10720
10612
  return [4 /*yield*/, this.getNodeInfo(peer)];
10721
- case 3: return [2 /*return*/, _a.sent()];
10613
+ case 3: return [2 /*return*/, _b.sent()];
10722
10614
  case 4:
10723
- err_2 = _a.sent();
10615
+ err_2 = _b.sent();
10724
10616
  console.debug('[network] Cannot get /api/node/info from peer: ' + (err_2 && err_2.message || err_2), err_2);
10725
10617
  return [2 /*return*/, undefined];
10726
10618
  case 5: return [2 /*return*/];
@@ -10731,8 +10623,8 @@
10731
10623
  NetworkService.prototype.checkPeerCompatible = function (peerInfo, opts) {
10732
10624
  return __awaiter(this, void 0, void 0, function () {
10733
10625
  var isCompatible;
10734
- return __generator(this, function (_a) {
10735
- switch (_a.label) {
10626
+ return __generator(this, function (_b) {
10627
+ switch (_b.label) {
10736
10628
  case 0:
10737
10629
  if (!this.environment.peerMinVersion)
10738
10630
  return [2 /*return*/, true]; // Skip compatibility check
@@ -10747,8 +10639,8 @@
10747
10639
  showCloseButton: true
10748
10640
  })];
10749
10641
  case 1:
10750
- _a.sent();
10751
- _a.label = 2;
10642
+ _b.sent();
10643
+ _b.label = 2;
10752
10644
  case 2: return [2 /*return*/, isCompatible];
10753
10645
  }
10754
10646
  });
@@ -10788,8 +10680,8 @@
10788
10680
  return __awaiter(this, void 0, void 0, function () {
10789
10681
  var $onRefresh, peers$, modal, data;
10790
10682
  var _this = this;
10791
- return __generator(this, function (_a) {
10792
- switch (_a.label) {
10683
+ return __generator(this, function (_b) {
10684
+ switch (_b.label) {
10793
10685
  case 0:
10794
10686
  opts = opts || {};
10795
10687
  $onRefresh = new i0.EventEmitter();
@@ -10806,14 +10698,14 @@
10806
10698
  showBackdrop: true
10807
10699
  })];
10808
10700
  case 1:
10809
- modal = _a.sent();
10701
+ modal = _b.sent();
10810
10702
  return [4 /*yield*/, modal.present()];
10811
10703
  case 2:
10812
- _a.sent();
10704
+ _b.sent();
10813
10705
  $onRefresh.emit();
10814
10706
  return [4 /*yield*/, modal.onWillDismiss()];
10815
10707
  case 3:
10816
- data = (_a.sent()).data;
10708
+ data = (_b.sent()).data;
10817
10709
  $onRefresh.complete();
10818
10710
  return [2 /*return*/, data && data || undefined];
10819
10711
  }
@@ -10824,7 +10716,7 @@
10824
10716
  return __awaiter(this, void 0, void 0, function () {
10825
10717
  var now;
10826
10718
  var _this = this;
10827
- return __generator(this, function (_a) {
10719
+ return __generator(this, function (_b) {
10828
10720
  now = this._debug && Date.now();
10829
10721
  console.info('[network] Clearing all caches...');
10830
10722
  return [2 /*return*/, this.cache.clearAll()
@@ -10843,10 +10735,164 @@
10843
10735
  });
10844
10736
  });
10845
10737
  };
10738
+ /* -- protected functions -- */
10739
+ NetworkService.prototype.ngOnStart = function () {
10740
+ return __awaiter(this, void 0, void 0, function () {
10741
+ var peer, _b;
10742
+ return __generator(this, function (_c) {
10743
+ switch (_c.label) {
10744
+ case 0:
10745
+ console.info('[network] Starting network...', this._startingPeer);
10746
+ _b = this._startingPeer;
10747
+ if (_b) return [3 /*break*/, 2];
10748
+ return [4 /*yield*/, this.restoreLocally()];
10749
+ case 1:
10750
+ _b = (_c.sent());
10751
+ _c.label = 2;
10752
+ case 2:
10753
+ peer = _b;
10754
+ // Make sure to hide the splashscreen, before open the modal
10755
+ if (!peer && this.splashScreen)
10756
+ this.splashScreen.hide();
10757
+ _c.label = 3;
10758
+ case 3:
10759
+ if (!!peer) return [3 /*break*/, 5];
10760
+ console.debug('[network] No peer defined. Asking user to choose a peer.');
10761
+ return [4 /*yield*/, this.showSelectPeerModal({ allowSelectDownPeer: false })];
10762
+ case 4:
10763
+ peer = _c.sent();
10764
+ return [3 /*break*/, 3];
10765
+ case 5:
10766
+ console.info("[network] Starting service [OK] {peer: '" + peer.url + "', online: " + this.online + "}");
10767
+ return [2 /*return*/, peer];
10768
+ }
10769
+ });
10770
+ });
10771
+ };
10772
+ NetworkService.prototype.ngOnAfterStart = function () {
10773
+ var _a;
10774
+ return __awaiter(this, void 0, void 0, function () {
10775
+ var _this = this;
10776
+ return __generator(this, function (_b) {
10777
+ switch (_b.label) {
10778
+ case 0:
10779
+ // Wait settings starts, then save peer in settings
10780
+ return [4 /*yield*/, this.settings.apply({ peerUrl: this._data.url })];
10781
+ case 1:
10782
+ // Wait settings starts, then save peer in settings
10783
+ _b.sent();
10784
+ this.onDeviceConnectionChanged(((_a = this.network) === null || _a === void 0 ? void 0 : _a.type) || 'unknown');
10785
+ // Start the refresh timer
10786
+ this.startRefreshTimer();
10787
+ // Listen for device network changes
10788
+ if (this.network) {
10789
+ this._subscription.add(this.network.onDisconnect().subscribe(function () { return _this.onDeviceConnectionChanged('none'); }));
10790
+ this._subscription.add(this.network.onConnect().subscribe(function () { return _this.onDeviceConnectionChanged(_this.network.type); }));
10791
+ }
10792
+ return [2 /*return*/];
10793
+ }
10794
+ });
10795
+ });
10796
+ };
10797
+ NetworkService.prototype.ngOnStop = function () {
10798
+ return __awaiter(this, void 0, void 0, function () {
10799
+ return __generator(this, function (_b) {
10800
+ this.resetData();
10801
+ // Stop timer if cannot refresh anymore
10802
+ if (this._timerRefreshCondition() === false) {
10803
+ this.stopRefreshTimer();
10804
+ }
10805
+ this._subscription.unsubscribe();
10806
+ this._subscription = new rxjs.Subscription();
10807
+ return [2 /*return*/];
10808
+ });
10809
+ });
10810
+ };
10811
+ /**
10812
+ * Try to restore peer from the local storage
10813
+ */
10814
+ NetworkService.prototype.restoreLocally = function () {
10815
+ return __awaiter(this, void 0, void 0, function () {
10816
+ var settingsStr, settings, location, hostname, detectedPeer;
10817
+ return __generator(this, function (_b) {
10818
+ switch (_b.label) {
10819
+ case 0: return [4 /*yield*/, this.storage.get(SETTINGS_STORAGE_KEY)];
10820
+ case 1:
10821
+ settingsStr = _b.sent();
10822
+ settings = settingsStr && JSON.parse(settingsStr) || undefined;
10823
+ if (settings && settings.peerUrl) {
10824
+ console.debug("[network] Use peer {" + settings.peerUrl + "} (found in the local storage)");
10825
+ return [2 /*return*/, exports.Peer.parseUrl(settings.peerUrl)];
10826
+ }
10827
+ // Else, use default peer in env, if exists
10828
+ if (this.environment.defaultPeer) {
10829
+ return [2 /*return*/, exports.Peer.fromObject(this.environment.defaultPeer)];
10830
+ }
10831
+ location = this._document && this._document.location;
10832
+ if (!(location && location.protocol && location.protocol.startsWith('http'))) return [3 /*break*/, 3];
10833
+ hostname = this._document.location.host;
10834
+ detectedPeer = exports.Peer.parseUrl("" + this._document.location.protocol + hostname + this.environment.baseUrl);
10835
+ return [4 /*yield*/, this.checkPeerAlive(detectedPeer)];
10836
+ case 2:
10837
+ if (_b.sent()) {
10838
+ return [2 /*return*/, detectedPeer];
10839
+ }
10840
+ _b.label = 3;
10841
+ case 3: return [2 /*return*/, undefined];
10842
+ }
10843
+ });
10844
+ });
10845
+ };
10846
+ /**
10847
+ * Stop to network state
10848
+ *
10849
+ * @protected
10850
+ */
10851
+ NetworkService.prototype.stopRefreshTimer = function () {
10852
+ if (this._timerSubscription) {
10853
+ this._timerSubscription.unsubscribe();
10854
+ this._timerSubscription = undefined;
10855
+ }
10856
+ };
10857
+ /**
10858
+ * Refresh the network state
10859
+ *
10860
+ * @protected
10861
+ */
10862
+ NetworkService.prototype.startRefreshTimer = function () {
10863
+ var _this = this;
10864
+ if (this._timerSubscription)
10865
+ return; // Already running: skip
10866
+ console.info("[network] Starting refresh timer, every " + this._timerRefreshPeriod + "ms...");
10867
+ var lastInfo;
10868
+ this._timerSubscription = rxjs.timer(this._timerRefreshPeriod, this._timerRefreshPeriod)
10869
+ .pipe(
10870
+ // Skip some timer event (see constructor)
10871
+ operators.filter(this._timerRefreshCondition),
10872
+ // Checkin if peer alive
10873
+ operators.tap(function () { return console.debug('[network] Checking connection to pod...'); }), operators.mergeMap(function () { return _this.checkPeerAlive(_this.peer); }),
10874
+ // Filter to keep only changes
10875
+ operators.filter(function (info) { return !!info !== !!lastInfo; }), operators.tap(function (info) { return lastInfo = info; }),
10876
+ // Check compatibility
10877
+ operators.mergeMap(function (info) { return _this.checkPeerCompatible(info, { showToast: true }); }))
10878
+ .subscribe(function (alive) {
10879
+ if (alive && _this.offline) {
10880
+ _this.setForceOffline(false);
10881
+ // Restart the service (to force re auth)
10882
+ _this.restart();
10883
+ }
10884
+ else if (!alive && _this.online) {
10885
+ _this.setForceOffline(true);
10886
+ // Stop the service
10887
+ _this.stop();
10888
+ }
10889
+ });
10890
+ this._timerSubscription.add(function () { return console.debug('[network] Refresh timer stopped'); });
10891
+ };
10846
10892
  NetworkService.prototype.get = function (path, opts) {
10847
10893
  return __awaiter(this, void 0, void 0, function () {
10848
10894
  var uri, peerUrl;
10849
- return __generator(this, function (_a) {
10895
+ return __generator(this, function (_b) {
10850
10896
  uri = path;
10851
10897
  // If path is not an URI: prepend with peer URL
10852
10898
  if (!uri.startsWith('http://') && !uri.startsWith('https://')) {
@@ -10897,7 +10943,7 @@
10897
10943
  }
10898
10944
  };
10899
10945
  NetworkService.prototype.resetData = function () {
10900
- this._peer = null;
10946
+ this._data = null;
10901
10947
  };
10902
10948
  /**
10903
10949
  * Get default peers, from environment
@@ -10905,7 +10951,7 @@
10905
10951
  NetworkService.prototype.getDefaultPeers = function () {
10906
10952
  return __awaiter(this, void 0, void 0, function () {
10907
10953
  var peers;
10908
- return __generator(this, function (_a) {
10954
+ return __generator(this, function (_b) {
10909
10955
  peers = (this.environment.defaultPeers || []).map(exports.Peer.fromObject);
10910
10956
  return [2 /*return*/, Promise.resolve(peers)];
10911
10957
  });
@@ -10918,7 +10964,7 @@
10918
10964
  var _this = this;
10919
10965
  this._listeners[name] = this._listeners[name] || [];
10920
10966
  this._listeners[name].push(callback);
10921
- // When unsubcribe, remove from the listener
10967
+ // When unsubscribe, remove from the listener
10922
10968
  return new rxjs.Subscription(function () {
10923
10969
  var index = _this._listeners[name].indexOf(callback);
10924
10970
  if (index !== -1) {
@@ -10929,7 +10975,7 @@
10929
10975
  NetworkService.prototype.emit = function (name, data) {
10930
10976
  return __awaiter(this, void 0, void 0, function () {
10931
10977
  var hooks;
10932
- return __generator(this, function (_a) {
10978
+ return __generator(this, function (_b) {
10933
10979
  hooks = this._listeners[name];
10934
10980
  if (isNotEmptyArray(hooks)) {
10935
10981
  console.info("[network-service] Trigger " + name + " hook: Executing " + hooks.length + " callbacks...");
@@ -10945,7 +10991,7 @@
10945
10991
  });
10946
10992
  };
10947
10993
  return NetworkService;
10948
- }());
10994
+ }(StartableService));
10949
10995
  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
10996
  NetworkService.decorators = [
10951
10997
  { type: i0.Injectable, args: [{ providedIn: 'root' },] }
@@ -11548,31 +11594,31 @@
11548
11594
  }());
11549
11595
 
11550
11596
  var APP_LOCAL_STORAGE_TYPE_POLICIES = new i0.InjectionToken('localStorageTypePolicies');
11551
- var EntitiesStorage = /** @class */ (function () {
11597
+ ;
11598
+ var EntitiesStorage = /** @class */ (function (_super) {
11599
+ __extends(EntitiesStorage, _super);
11552
11600
  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 || {};
11601
+ var _this = _super.call(this, platform) || this;
11602
+ _this.platform = platform;
11603
+ _this.progressBarService = progressBarService;
11604
+ _this.storage = storage;
11605
+ _this.environment = environment;
11606
+ _this._subscription = new rxjs.Subscription();
11607
+ _this._$save = new i0.EventEmitter(true);
11608
+ _this._dirty = false;
11609
+ _this._saving = false;
11610
+ _this._typePolicies = typePolicies || {};
11611
+ _this._saveTimerPeriod = environment.storageSavePeriodMs || 10000 /* = 10s */;
11612
+ _this._data = {};
11565
11613
  // For DEV only
11566
- this._debug = !environment.production;
11567
- if (this._debug)
11614
+ _this._debug = !environment.production;
11615
+ if (_this._debug)
11568
11616
  console.debug('[entities-storage] Creating service');
11617
+ return _this;
11569
11618
  }
11570
11619
  Object.defineProperty(EntitiesStorage.prototype, "dirty", {
11571
11620
  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;
11621
+ return this._dirty || Object.values(this._data).some(function (store) { return store.dirty; });
11576
11622
  },
11577
11623
  enumerable: false,
11578
11624
  configurable: true
@@ -11580,7 +11626,7 @@
11580
11626
  EntitiesStorage.prototype.watchAll = function (entityName, variables, opts) {
11581
11627
  var _this = this;
11582
11628
  // Make sure store is ready
11583
- if (!this._started) {
11629
+ if (!this.started) {
11584
11630
  return rxjs.defer(function () { return _this.ready(); })
11585
11631
  .pipe(operators.switchMap(function () { return _this.watchAll(entityName, variables, opts); })); // Loop
11586
11632
  }
@@ -11597,7 +11643,7 @@
11597
11643
  return __generator(this, function (_a) {
11598
11644
  switch (_a.label) {
11599
11645
  case 0:
11600
- if (!!this._started) return [3 /*break*/, 2];
11646
+ if (!!this.started) return [3 /*break*/, 2];
11601
11647
  return [4 /*yield*/, this.ready()];
11602
11648
  case 1:
11603
11649
  _a.sent();
@@ -11700,9 +11746,12 @@
11700
11746
  case 0:
11701
11747
  if (!entity)
11702
11748
  return [2 /*return*/]; // skip
11749
+ if (!!this.started) return [3 /*break*/, 2];
11703
11750
  return [4 /*yield*/, this.ready()];
11704
11751
  case 1:
11705
11752
  _a.sent();
11753
+ _a.label = 2;
11754
+ case 2:
11706
11755
  try {
11707
11756
  this.progressBarService.increase();
11708
11757
  this._dirty = true;
@@ -11729,9 +11778,12 @@
11729
11778
  case 0:
11730
11779
  if (isEmptyArray(entities) && (!opts || opts.reset !== true))
11731
11780
  return [2 /*return*/, entities]; // Skip (nothing to save)
11781
+ if (!!this.started) return [3 /*break*/, 2];
11732
11782
  return [4 /*yield*/, this.ready()];
11733
11783
  case 1:
11734
11784
  _a.sent();
11785
+ _a.label = 2;
11786
+ case 2:
11735
11787
  try {
11736
11788
  this.progressBarService.increase();
11737
11789
  this._dirty = true;
@@ -11753,10 +11805,12 @@
11753
11805
  case 0:
11754
11806
  if (!entity)
11755
11807
  return [2 /*return*/, undefined]; // skip
11808
+ if (!!this.started) return [3 /*break*/, 2];
11756
11809
  return [4 /*yield*/, this.ready()];
11757
11810
  case 1:
11758
11811
  _a.sent();
11759
- return [2 /*return*/, this.deleteById(entity.id, Object.assign(Object.assign({}, opts), { entityName: opts && opts.entityName || this.detectEntityName(entity) }))];
11812
+ _a.label = 2;
11813
+ case 2: return [2 /*return*/, this.deleteById(entity.id, Object.assign(Object.assign({}, opts), { entityName: opts && opts.entityName || this.detectEntityName(entity) }))];
11760
11814
  }
11761
11815
  });
11762
11816
  });
@@ -11766,9 +11820,13 @@
11766
11820
  var entityStore, deletedEntity;
11767
11821
  return __generator(this, function (_a) {
11768
11822
  switch (_a.label) {
11769
- case 0: return [4 /*yield*/, this.ready()];
11823
+ case 0:
11824
+ if (!!this.started) return [3 /*break*/, 2];
11825
+ return [4 /*yield*/, this.ready()];
11770
11826
  case 1:
11771
11827
  _a.sent();
11828
+ _a.label = 2;
11829
+ case 2:
11772
11830
  if (!opts || isNilOrBlank(opts.entityName))
11773
11831
  throw new Error('Missing argument \'opts\' or \'entityName\'');
11774
11832
  //if (id >= 0) throw new Error('Invalid id a local entity (not a negative number): ' + id);
@@ -11800,9 +11858,13 @@
11800
11858
  var entityStore, deletedEntities;
11801
11859
  return __generator(this, function (_a) {
11802
11860
  switch (_a.label) {
11803
- case 0: return [4 /*yield*/, this.ready()];
11861
+ case 0:
11862
+ if (!!this.started) return [3 /*break*/, 2];
11863
+ return [4 /*yield*/, this.ready()];
11804
11864
  case 1:
11805
11865
  _a.sent();
11866
+ _a.label = 2;
11867
+ case 2:
11806
11868
  if (!opts || isNilOrBlank(opts.entityName))
11807
11869
  throw new Error('Missing argument \'opts\' or \'opts.entityName\'');
11808
11870
  try {
@@ -11835,10 +11897,12 @@
11835
11897
  case 0:
11836
11898
  if (!entity)
11837
11899
  return [2 /*return*/, undefined]; // skip
11900
+ if (!!this.started) return [3 /*break*/, 2];
11838
11901
  return [4 /*yield*/, this.ready()];
11839
11902
  case 1:
11840
11903
  _a.sent();
11841
- return [2 /*return*/, this.deleteFromTrashById(entity.id, Object.assign(Object.assign({}, opts), { entityName: opts && opts.entityName || this.detectEntityName(entity) }))];
11904
+ _a.label = 2;
11905
+ case 2: return [2 /*return*/, this.deleteFromTrashById(entity.id, Object.assign(Object.assign({}, opts), { entityName: opts && opts.entityName || this.detectEntityName(entity) }))];
11842
11906
  }
11843
11907
  });
11844
11908
  });
@@ -11865,9 +11929,12 @@
11865
11929
  case 0:
11866
11930
  if (!entity)
11867
11931
  return [2 /*return*/, undefined]; // skip
11932
+ if (!!this.started) return [3 /*break*/, 2];
11868
11933
  return [4 /*yield*/, this.ready()];
11869
11934
  case 1:
11870
11935
  _a.sent();
11936
+ _a.label = 2;
11937
+ case 2:
11871
11938
  entityName = opts && opts.entityName || this.detectEntityName(entity);
11872
11939
  entityStore = this.getEntityStore(entityName);
11873
11940
  if (entityStore)
@@ -11887,9 +11954,13 @@
11887
11954
  var entityStore, deletedEntities, trashName;
11888
11955
  return __generator(this, function (_a) {
11889
11956
  switch (_a.label) {
11890
- case 0: return [4 /*yield*/, this.ready()];
11957
+ case 0:
11958
+ if (!!this.started) return [3 /*break*/, 2];
11959
+ return [4 /*yield*/, this.ready()];
11891
11960
  case 1:
11892
11961
  _a.sent();
11962
+ _a.label = 2;
11963
+ case 2:
11893
11964
  if (!opts || isNilOrBlank(opts.entityName))
11894
11965
  throw new Error('Missing argument \'opts.entityName\'');
11895
11966
  entityStore = this.getEntityStore(opts.entityName, { create: false });
@@ -11921,9 +11992,12 @@
11921
11992
  case 0:
11922
11993
  if (!entity)
11923
11994
  return [2 /*return*/, undefined]; // skip
11995
+ if (!!this.started) return [3 /*break*/, 2];
11924
11996
  return [4 /*yield*/, this.ready()];
11925
11997
  case 1:
11926
11998
  _a.sent();
11999
+ _a.label = 2;
12000
+ case 2:
11927
12001
  entityName = opts && opts.entityName || this.detectEntityName(entity);
11928
12002
  trashName = EntitiesStorage.TRASH_PREFIX + entityName;
11929
12003
  this.getEntityStore(trashName).save(entity, opts);
@@ -11938,9 +12012,13 @@
11938
12012
  var trashName, entityStore;
11939
12013
  return __generator(this, function (_a) {
11940
12014
  switch (_a.label) {
11941
- case 0: return [4 /*yield*/, this.ready()];
12015
+ case 0:
12016
+ if (!!this.started) return [3 /*break*/, 2];
12017
+ return [4 /*yield*/, this.ready()];
11942
12018
  case 1:
11943
12019
  _a.sent();
12020
+ _a.label = 2;
12021
+ case 2:
11944
12022
  trashName = EntitiesStorage.TRASH_PREFIX + entityName;
11945
12023
  entityStore = this.getEntityStore(trashName, { create: false });
11946
12024
  if (!entityStore)
@@ -11959,79 +12037,58 @@
11959
12037
  }
11960
12038
  return Promise.resolve();
11961
12039
  };
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 () {
12040
+ EntitiesStorage.prototype.ngOnStart = function () {
11991
12041
  return __awaiter(this, void 0, void 0, function () {
12042
+ var now;
12043
+ var _this = this;
11992
12044
  return __generator(this, function (_a) {
11993
12045
  switch (_a.label) {
11994
12046
  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()];
12047
+ now = Date.now();
12048
+ console.info("[entities-storage] Starting entity storage...");
12049
+ // Restore sequences
12050
+ return [4 /*yield*/, this.restoreLocally()];
12000
12051
  case 1:
12052
+ // Restore sequences
12001
12053
  _a.sent();
12002
- _a.label = 2;
12003
- case 2: return [2 /*return*/];
12054
+ // Start a save timer
12055
+ this._subscription.add(rxjs.merge(this._$save, rxjs.timer(this._saveTimerPeriod, this._saveTimerPeriod))
12056
+ .pipe(
12057
+ // Avoid to many call (e.g. when $save AND timer are triggered
12058
+ operators.throttleTime(this._saveTimerPeriod))
12059
+ .subscribe(function () { return _this.storeLocally(); }));
12060
+ console.info("[entities-storage] Starting [OK] in " + (Date.now() - now) + "ms");
12061
+ return [2 /*return*/, this._data];
12004
12062
  }
12005
12063
  });
12006
12064
  });
12007
12065
  };
12008
- EntitiesStorage.prototype.restart = function () {
12066
+ EntitiesStorage.prototype.ngOnStop = function () {
12009
12067
  return __awaiter(this, void 0, void 0, function () {
12010
12068
  return __generator(this, function (_a) {
12011
12069
  switch (_a.label) {
12012
12070
  case 0:
12013
- if (!this._started) return [3 /*break*/, 2];
12014
- return [4 /*yield*/, this.stop()];
12071
+ this._subscription.unsubscribe();
12072
+ this._subscription = new rxjs.Subscription();
12073
+ if (!this.dirty) return [3 /*break*/, 2];
12074
+ return [4 /*yield*/, this.storeLocally()];
12015
12075
  case 1:
12016
12076
  _a.sent();
12017
12077
  _a.label = 2;
12018
- case 2: return [4 /*yield*/, this.start()];
12019
- case 3:
12020
- _a.sent();
12021
- return [2 /*return*/];
12078
+ case 2: return [2 /*return*/];
12022
12079
  }
12023
12080
  });
12024
12081
  });
12025
12082
  };
12026
12083
  /* -- protected methods -- */
12027
12084
  EntitiesStorage.prototype.getEntityStore = function (name, opts) {
12028
- var store = this._stores[name];
12085
+ var store = this._data[name];
12029
12086
  if (!store && (!opts || opts.create !== false)) {
12030
12087
  if (this._debug)
12031
12088
  console.debug("[entities-storage] Creating store " + name);
12032
12089
  var typePolicy = this._typePolicies[name];
12033
12090
  store = new EntityStore(name, this.storage, typePolicy);
12034
- this._stores[name] = store;
12091
+ this._data[name] = store;
12035
12092
  }
12036
12093
  return store;
12037
12094
  };
@@ -12088,11 +12145,11 @@
12088
12145
  this._saving = true;
12089
12146
  this._dirty = false;
12090
12147
  this.progressBarService.increase();
12091
- entityNames = this._stores && Object.keys(this._stores) || [];
12148
+ entityNames = this._data && Object.keys(this._data) || [];
12092
12149
  now = Date.now();
12093
12150
  if (this._debug)
12094
12151
  console.debug('[entities-storage] Persisting...');
12095
- return [2 /*return*/, rxjs.concat.apply(void 0, __spread(entityNames.map(function (entityName) { return rxjs.defer(function () {
12152
+ return [2 /*return*/, chainPromises(entityNames.map(function (entityName) { return function () {
12096
12153
  currentEntityName = entityName;
12097
12154
  var entityStore = _this.getEntityStore(entityName, { create: false });
12098
12155
  if (!entityStore) {
@@ -12106,17 +12163,20 @@
12106
12163
  entityNames.splice(entityNames.findIndex(function (e) { return e === entityName; }), 1);
12107
12164
  }
12108
12165
  });
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) {
12166
+ }; }))
12167
+ .then(function () {
12168
+ currentEntityName = undefined;
12169
+ return isEmptyArray(entityNames) ?
12170
+ _this.storage.remove(ENTITIES_STORAGE_KEY_PREFIX) :
12171
+ _this.storage.set(ENTITIES_STORAGE_KEY_PREFIX, entityNames);
12172
+ })
12173
+ .then(function () {
12174
+ if (_this._debug)
12175
+ console.debug("[entities-storage] Persisting [OK] " + entityNames.length + " stores saved in " + (Date.now() - now) + "ms...");
12176
+ _this._saving = false;
12177
+ _this.progressBarService.decrease();
12178
+ })
12179
+ .catch(function (err) {
12120
12180
  _this._saving = false;
12121
12181
  _this.progressBarService.decrease();
12122
12182
  if (currentEntityName) {
@@ -12125,13 +12185,13 @@
12125
12185
  else {
12126
12186
  console.error("[entities-storage] Error while persisting: " + (err && err.message || err), err);
12127
12187
  }
12128
- return err;
12129
- })).toPromise()];
12188
+ throw err;
12189
+ })];
12130
12190
  });
12131
12191
  });
12132
12192
  };
12133
12193
  return EntitiesStorage;
12134
- }());
12194
+ }(StartableService));
12135
12195
  EntitiesStorage.TRASH_PREFIX = 'Trash#';
12136
12196
  EntitiesStorage.REMOTE_PREFIX = 'Remote#';
12137
12197
  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 +12202,7 @@
12142
12202
  { type: i1$5.Platform },
12143
12203
  { type: ProgressBarService },
12144
12204
  { type: i3$2.Storage },
12145
- { type: undefined, decorators: [{ type: i0.Inject, args: [ENVIRONMENT,] }] },
12205
+ { type: Environment, decorators: [{ type: i0.Inject, args: [ENVIRONMENT,] }] },
12146
12206
  { type: undefined, decorators: [{ type: i0.Optional }, { type: i0.Inject, args: [APP_LOCAL_STORAGE_TYPE_POLICIES,] }] }
12147
12207
  ]; };
12148
12208
 
@@ -12667,59 +12727,36 @@
12667
12727
  }
12668
12728
 
12669
12729
  var APP_GRAPHQL_TYPE_POLICIES = new i0.InjectionToken('graphqlTypePolicies');
12670
- var GraphqlService = /** @class */ (function () {
12730
+ var GraphqlService = /** @class */ (function (_super) {
12731
+ __extends(GraphqlService, _super);
12671
12732
  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;
12689
- // Restart if network restart
12690
- this.network.on('start', function () { return _this.restart(); });
12691
- // Clear cache
12692
- this.network.on('resetCache', function () { return __awaiter(_this, void 0, void 0, function () {
12693
- return __generator(this, function (_a) {
12694
- switch (_a.label) {
12695
- case 0: return [4 /*yield*/, this.ready()];
12696
- case 1:
12697
- _a.sent();
12698
- return [4 /*yield*/, this.clearCache()];
12699
- case 2:
12700
- _a.sent();
12701
- return [2 /*return*/];
12702
- }
12703
- });
12704
- }); });
12733
+ var _this = _super.call(this, platform) || this;
12734
+ _this.platform = platform;
12735
+ _this.apollo = apollo;
12736
+ _this.httpLink = httpLink;
12737
+ _this.network = network;
12738
+ _this.storage = storage;
12739
+ _this.cryptoService = cryptoService;
12740
+ _this.environment = environment;
12741
+ _this.typePolicies = typePolicies;
12742
+ _this._subscription = new rxjs.Subscription();
12743
+ _this.connectionParams = {};
12744
+ _this.onNetworkError = new rxjs.Subject();
12745
+ _this.customErrors = {};
12746
+ _this._debug = !environment.production;
12747
+ _this._defaultFetchPolicy = environment.apolloFetchPolicy;
12705
12748
  // Listen network status
12706
- this._networkStatusChanged$ = network.onNetworkStatusChanges
12749
+ _this._networkStatusChanged$ = network.onNetworkStatusChanges
12707
12750
  .pipe(operators.filter(isNotNil), operators.distinctUntilChanged());
12708
12751
  // When getting network error: try to ping peer, and toggle to offline
12709
- this.onNetworkError
12752
+ _this.onNetworkError
12710
12753
  .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
12754
  .subscribe(function () { return _this.network.setForceOffline(true, { showToast: true }); });
12755
+ return _this;
12712
12756
  }
12713
- Object.defineProperty(GraphqlService.prototype, "started", {
12714
- get: function () {
12715
- return this._started;
12716
- },
12717
- enumerable: false,
12718
- configurable: true
12719
- });
12720
12757
  Object.defineProperty(GraphqlService.prototype, "client", {
12721
12758
  get: function () {
12722
- return this.apollo.client;
12759
+ return this._data;
12723
12760
  },
12724
12761
  enumerable: false,
12725
12762
  configurable: true
@@ -12738,34 +12775,6 @@
12738
12775
  enumerable: false,
12739
12776
  configurable: true
12740
12777
  });
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
12778
  GraphqlService.prototype.setAuthToken = function (token) {
12770
12779
  if (token) {
12771
12780
  console.debug('[graphql] Apply token authentication to headers');
@@ -12798,15 +12807,18 @@
12798
12807
  */
12799
12808
  GraphqlService.prototype.addResolver = function (resolvers) {
12800
12809
  return __awaiter(this, void 0, void 0, function () {
12801
- var client;
12802
- var _this = this;
12803
12810
  return __generator(this, function (_a) {
12804
- if (!this._started) {
12805
- this.onStart.toPromise().then(function () { return _this.addResolver(resolvers); }); // Loop
12811
+ switch (_a.label) {
12812
+ case 0:
12813
+ if (!!this.started) return [3 /*break*/, 2];
12814
+ return [4 /*yield*/, this.ready()];
12815
+ case 1:
12816
+ _a.sent();
12817
+ _a.label = 2;
12818
+ case 2:
12819
+ this.apollo.client.addResolvers(resolvers);
12820
+ return [2 /*return*/];
12806
12821
  }
12807
- client = this.apollo.getClient();
12808
- client.addResolvers(resolvers);
12809
- return [2 /*return*/];
12810
12822
  });
12811
12823
  });
12812
12824
  };
@@ -12816,21 +12828,26 @@
12816
12828
  return __generator(this, function (_a) {
12817
12829
  switch (_a.label) {
12818
12830
  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()];
12831
+ if (!!this.started) return [3 /*break*/, 2];
12832
+ return [4 /*yield*/, this.ready()];
12833
+ case 1:
12834
+ _a.sent();
12835
+ _a.label = 2;
12826
12836
  case 2:
12827
- res = _a.sent();
12828
- return [3 /*break*/, 4];
12837
+ _a.trys.push([2, 4, , 5]);
12838
+ return [4 /*yield*/, this.client.query({
12839
+ query: opts.query,
12840
+ variables: opts.variables,
12841
+ fetchPolicy: opts.fetchPolicy || this._defaultFetchPolicy || undefined
12842
+ })];
12829
12843
  case 3:
12844
+ res = _a.sent();
12845
+ return [3 /*break*/, 5];
12846
+ case 4:
12830
12847
  err_1 = _a.sent();
12831
12848
  res = this.toApolloError(err_1, opts.error);
12832
- return [3 /*break*/, 4];
12833
- case 4:
12849
+ return [3 /*break*/, 5];
12850
+ case 5:
12834
12851
  if (res.errors) {
12835
12852
  throw res.errors[0];
12836
12853
  }
@@ -13188,12 +13205,14 @@
13188
13205
  return __generator(this, function (_a) {
13189
13206
  switch (_a.label) {
13190
13207
  case 0:
13191
- client = (client || this.apollo.client);
13208
+ client = (client || this.client);
13192
13209
  if (!client) return [3 /*break*/, 2];
13193
- now = this._debug && Date.now();
13194
13210
  console.info('[graphql] Clearing Apollo client\'s cache... ');
13211
+ now = this._debug && Date.now();
13212
+ // Clearing the cache
13195
13213
  return [4 /*yield*/, client.cache.reset()];
13196
13214
  case 1:
13215
+ // Clearing the cache
13197
13216
  _a.sent();
13198
13217
  if (this._debug)
13199
13218
  console.debug("[graphql] Apollo client's cache cleared, in " + (Date.now() - now) + "ms");
@@ -13207,13 +13226,16 @@
13207
13226
  this.customErrors = Object.assign(Object.assign({}, this.customErrors), error);
13208
13227
  };
13209
13228
  /* -- protected methods -- */
13210
- GraphqlService.prototype.initApollo = function () {
13229
+ GraphqlService.prototype.ngOnStart = function () {
13211
13230
  return __awaiter(this, void 0, void 0, function () {
13212
13231
  var mobile, enableTrackMutationQueries, peer, uri, wsUri, storage, client, wsLink, retryLink, authLink, httpLink, cache, mutationLinks, serializingLink, trackerLink, queueLink_1, err_2;
13213
13232
  var _this = this;
13214
13233
  return __generator(this, function (_a) {
13215
13234
  switch (_a.label) {
13216
- case 0:
13235
+ case 0: return [4 /*yield*/, this.network.ready()];
13236
+ case 1:
13237
+ _a.sent();
13238
+ console.info('[graphql] Starting graphql...');
13217
13239
  mobile = this.platform.is('mobile') || this.platform.is('mobileweb');
13218
13240
  enableTrackMutationQueries = !mobile;
13219
13241
  peer = this.network.peer;
@@ -13232,7 +13254,7 @@
13232
13254
  }, webSocketImpl: AppWebSocket, uri: wsUri });
13233
13255
  storage = new apollo3CachePersist.IonicStorageWrapper(this.storage);
13234
13256
  client = this.apollo.client;
13235
- if (!!client) return [3 /*break*/, 3];
13257
+ if (!!client) return [3 /*break*/, 4];
13236
13258
  console.debug('[apollo] Creating GraphQL client...');
13237
13259
  wsLink = new ws.WebSocketLink(this.wsParams);
13238
13260
  retryLink = new retry.RetryLink();
@@ -13258,7 +13280,7 @@
13258
13280
  cache = new core.InMemoryCache({
13259
13281
  typePolicies: this.typePolicies
13260
13282
  });
13261
- if (!this.environment.persistCache) return [3 /*break*/, 2];
13283
+ if (!this.environment.persistCache) return [3 /*break*/, 3];
13262
13284
  console.debug('[graphql] Starting persistence cache...');
13263
13285
  return [4 /*yield*/, apollo3CachePersist.persistCache({
13264
13286
  cache: cache,
@@ -13267,10 +13289,10 @@
13267
13289
  debounce: 1000,
13268
13290
  debug: true
13269
13291
  })];
13270
- case 1:
13271
- _a.sent();
13272
- _a.label = 2;
13273
13292
  case 2:
13293
+ _a.sent();
13294
+ _a.label = 3;
13295
+ case 3:
13274
13296
  mutationLinks = void 0;
13275
13297
  // Add queue to store tracked queries, when offline
13276
13298
  if (enableTrackMutationQueries) {
@@ -13284,9 +13306,8 @@
13284
13306
  queueLink_1 = new QueueLink__default["default"]();
13285
13307
  this._subscription.add(this._networkStatusChanged$
13286
13308
  .subscribe(function (type) {
13287
- var offline = type === 'none';
13288
13309
  // Network is offline: start buffering into queue
13289
- if (offline) {
13310
+ if (type === 'none') {
13290
13311
  console.info('[graphql] offline mode: enable mutations buffer');
13291
13312
  queueLink_1.close();
13292
13313
  }
@@ -13331,30 +13352,36 @@
13331
13352
  connectToDevTools: !this.environment.production
13332
13353
  });
13333
13354
  this.apollo.client = client;
13334
- _a.label = 3;
13335
- case 3:
13336
- if (!(enableTrackMutationQueries && this.environment.persistCache)) return [3 /*break*/, 7];
13337
13355
  _a.label = 4;
13338
13356
  case 4:
13339
- _a.trys.push([4, 6, , 7]);
13357
+ if (!(enableTrackMutationQueries && this.environment.persistCache)) return [3 /*break*/, 8];
13358
+ _a.label = 5;
13359
+ case 5:
13360
+ _a.trys.push([5, 7, , 8]);
13340
13361
  return [4 /*yield*/, restoreTrackedQueries({
13341
13362
  apolloClient: client,
13342
13363
  storage: storage,
13343
13364
  debug: true
13344
13365
  })];
13345
- case 5:
13346
- _a.sent();
13347
- return [3 /*break*/, 7];
13348
13366
  case 6:
13367
+ _a.sent();
13368
+ return [3 /*break*/, 8];
13369
+ case 7:
13349
13370
  err_2 = _a.sent();
13350
13371
  console.error('[graphql] Failed to restore tracked queries from storage: ' + (err_2 && err_2.message || err_2), err_2);
13351
- return [3 /*break*/, 7];
13352
- case 7: return [2 /*return*/];
13372
+ return [3 /*break*/, 8];
13373
+ case 8:
13374
+ // Listen for network restart
13375
+ this._subscription.add(this.network.on('start', function () { return _this.restart(); }));
13376
+ // Listen for clear cache request, from network
13377
+ this._subscription.add(this.network.on('resetCache', function () { return _this.clearCache(); }));
13378
+ console.info('[graphql] Starting graphql [OK]');
13379
+ return [2 /*return*/, client];
13353
13380
  }
13354
13381
  });
13355
13382
  });
13356
13383
  };
13357
- GraphqlService.prototype.stop = function () {
13384
+ GraphqlService.prototype.ngOnStop = function () {
13358
13385
  return __awaiter(this, void 0, void 0, function () {
13359
13386
  return __generator(this, function (_a) {
13360
13387
  switch (_a.label) {
@@ -13365,24 +13392,11 @@
13365
13392
  return [4 /*yield*/, this.resetClient()];
13366
13393
  case 1:
13367
13394
  _a.sent();
13368
- this._started = false;
13369
- this._startPromise = undefined;
13370
13395
  return [2 /*return*/];
13371
13396
  }
13372
13397
  });
13373
13398
  });
13374
13399
  };
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
13400
  GraphqlService.prototype.resetClient = function (client) {
13387
13401
  return __awaiter(this, void 0, void 0, function () {
13388
13402
  return __generator(this, function (_a) {
@@ -13484,24 +13498,8 @@
13484
13498
  }
13485
13499
  return undefined;
13486
13500
  };
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
13501
  return GraphqlService;
13504
- }());
13502
+ }(StartableService));
13505
13503
  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
13504
  GraphqlService.decorators = [
13507
13505
  { type: i0.Injectable, args: [{
@@ -13784,7 +13782,12 @@
13784
13782
  _this.storage = storage;
13785
13783
  _this.file = file;
13786
13784
  _this.environment = environment;
13787
- _this.data = {
13785
+ _this.onLogin = new rxjs.Subject();
13786
+ _this.onLogout = new rxjs.Subject();
13787
+ _this.onChange = new rxjs.Subject();
13788
+ _this.onAuthTokenChange = new rxjs.Subject();
13789
+ _this.onAuthBasicChange = new rxjs.Subject();
13790
+ _this._data = {
13788
13791
  loaded: false,
13789
13792
  keypair: null,
13790
13793
  authToken: null,
@@ -13798,11 +13801,6 @@
13798
13801
  _this._started = false;
13799
13802
  _this._$additionalFields = new rxjs.BehaviorSubject([]);
13800
13803
  _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
13804
  _this._debug = !environment.production;
13807
13805
  if (_this._debug)
13808
13806
  console.debug('[account-service] Creating service');
@@ -13810,7 +13808,7 @@
13810
13808
  // Send auth token to the graphql layer, when changed
13811
13809
  _this.onAuthTokenChange.subscribe(function (token) { return _this.graphql.setAuthToken(token); });
13812
13810
  _this.onAuthBasicChange.subscribe(function (basic) { return _this.graphql.setAuthBasic(basic); });
13813
- // Listen network restart
13811
+ // Listen graphql start (or restart)
13814
13812
  _this.graphql.onStart.subscribe(function () { return __awaiter(_this, void 0, void 0, function () {
13815
13813
  return __generator(this, function (_b) {
13816
13814
  if (!this._started) {
@@ -13844,27 +13842,27 @@
13844
13842
  }
13845
13843
  Object.defineProperty(AccountService.prototype, "account", {
13846
13844
  get: function () {
13847
- return this.data.loaded ? this.data.account : undefined;
13845
+ return this._data.loaded ? this._data.account : undefined;
13848
13846
  },
13849
13847
  enumerable: false,
13850
13848
  configurable: true
13851
13849
  });
13852
13850
  Object.defineProperty(AccountService.prototype, "person", {
13853
13851
  get: function () {
13854
- if (this.data.loaded && !this.data.person) {
13855
- this.data.person = this.data.loaded ? this.data.account.asPerson() : undefined;
13852
+ if (this._data.loaded && !this._data.person) {
13853
+ this._data.person = this._data.loaded ? this._data.account.asPerson() : undefined;
13856
13854
  }
13857
- return this.data.person;
13855
+ return this._data.person;
13858
13856
  },
13859
13857
  enumerable: false,
13860
13858
  configurable: true
13861
13859
  });
13862
13860
  Object.defineProperty(AccountService.prototype, "department", {
13863
13861
  get: function () {
13864
- if (this.data.loaded && !this.data.department) {
13865
- this.data.department = this.data.loaded ? this.data.account.asPerson().department : undefined;
13862
+ if (this._data.loaded && !this._data.department) {
13863
+ this._data.department = this._data.loaded ? this._data.account.asPerson().department : undefined;
13866
13864
  }
13867
- return this.data.department;
13865
+ return this._data.department;
13868
13866
  },
13869
13867
  enumerable: false,
13870
13868
  configurable: true
@@ -13878,32 +13876,21 @@
13878
13876
  console.info('[account] Using authentication token type: ' + value);
13879
13877
  this._tokenType$.next(value);
13880
13878
  // Reset values
13881
- this.data.authToken = undefined;
13879
+ this._data.authToken = undefined;
13882
13880
  this.onAuthTokenChange.next(undefined);
13883
- this.data.authBasic = undefined;
13881
+ this._data.authBasic = undefined;
13884
13882
  this.onAuthBasicChange.next(undefined);
13885
13883
  }
13886
13884
  },
13887
13885
  enumerable: false,
13888
13886
  configurable: true
13889
13887
  });
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
13888
  AccountService.prototype.start = function () {
13902
13889
  var _this = this;
13903
13890
  if (this._startPromise)
13904
13891
  return this._startPromise;
13905
13892
  if (this._started)
13906
- return Promise.resolve();
13893
+ return Promise.resolve(this.account);
13907
13894
  // Restoring local settings
13908
13895
  this._startPromise = Promise.all([
13909
13896
  this.settings.ready(),
@@ -13914,6 +13901,7 @@
13914
13901
  .then(function () {
13915
13902
  _this._started = true;
13916
13903
  _this._startPromise = undefined;
13904
+ return _this.account;
13917
13905
  });
13918
13906
  return this._startPromise;
13919
13907
  };
@@ -13931,8 +13919,8 @@
13931
13919
  if (this._started || this._startPromise) {
13932
13920
  this._started = false;
13933
13921
  this._startPromise = undefined;
13934
- hadAuthToken = this.data.authToken && true;
13935
- hadAuthBasic = this.data.authBasic && true;
13922
+ hadAuthToken = this._data.authToken && true;
13923
+ hadAuthBasic = this._data.authBasic && true;
13936
13924
  hadAuth = hadAuthToken || hadAuthBasic;
13937
13925
  this.resetData();
13938
13926
  if (hadAuth) {
@@ -13962,35 +13950,35 @@
13962
13950
  };
13963
13951
  AccountService.prototype.ready = function () {
13964
13952
  if (this._started)
13965
- return Promise.resolve();
13953
+ return Promise.resolve(this.account);
13966
13954
  return this.start();
13967
13955
  };
13968
13956
  AccountService.prototype.isLogin = function () {
13969
- return !!(this.data.pubkey && this.data.loaded);
13957
+ return !!(this._data.pubkey && this._data.loaded);
13970
13958
  };
13971
13959
  AccountService.prototype.isAuth = function () {
13972
- return !!(this.data.pubkey && this.data.keypair && this.data.keypair.secretKey);
13960
+ return !!(this._data.pubkey && this._data.keypair && this._data.keypair.secretKey);
13973
13961
  };
13974
13962
  AccountService.prototype.hasMinProfile = function (userProfile) {
13975
13963
  // 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)) {
13964
+ if (!this._data.account || !this._data.account.pubkey ||
13965
+ (this._data.account.statusId !== StatusIds.ENABLE && this._data.account.statusId !== StatusIds.TEMPORARY)) {
13978
13966
  return false;
13979
13967
  }
13980
- return PersonUtils.hasUpperOrEqualsProfile(this.data.account.profiles, userProfile);
13968
+ return PersonUtils.hasUpperOrEqualsProfile(this._data.account.profiles, userProfile);
13981
13969
  };
13982
13970
  AccountService.prototype.hasExactProfile = function (label) {
13983
13971
  // 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))
13972
+ if (!this._data.account || !this._data.account.pubkey ||
13973
+ (this._data.account.statusId !== StatusIds.ENABLE && this._data.account.statusId !== StatusIds.TEMPORARY))
13986
13974
  return false;
13987
- return this.data.account.profiles.some(function (profile) { return profile === label; });
13975
+ return this._data.account.profiles.some(function (profile) { return profile === label; });
13988
13976
  };
13989
13977
  AccountService.prototype.hasProfileAndIsEnable = function (userProfile) {
13990
13978
  // should be login, and status ENABLE
13991
- if (!this.data.account || !this.data.account.pubkey || this.data.account.statusId !== StatusIds.ENABLE)
13979
+ if (!this._data.account || !this._data.account.pubkey || this._data.account.statusId !== StatusIds.ENABLE)
13992
13980
  return false;
13993
- return PersonUtils.hasUpperOrEqualsProfile(this.data.account.profiles, userProfile);
13981
+ return PersonUtils.hasUpperOrEqualsProfile(this._data.account.profiles, userProfile);
13994
13982
  };
13995
13983
  AccountService.prototype.isAdmin = function () {
13996
13984
  return this.hasProfileAndIsEnable('ADMIN');
@@ -14010,11 +13998,11 @@
14010
13998
  };
14011
13999
  AccountService.prototype.isOnlyGuest = function () {
14012
14000
  // 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))
14001
+ if (!this._data.account || !this._data.account.pubkey ||
14002
+ (this._data.account.statusId !== StatusIds.ENABLE && this._data.account.statusId !== StatusIds.TEMPORARY))
14015
14003
  return false;
14016
14004
  // Profile less then user
14017
- return !PersonUtils.hasUpperOrEqualsProfile(this.data.account.profiles, 'USER');
14005
+ return !PersonUtils.hasUpperOrEqualsProfile(this._data.account.profiles, 'USER');
14018
14006
  };
14019
14007
  AccountService.prototype.canUserWriteDataForDepartment = function (recorderDepartment) {
14020
14008
  if (ReferentialUtils.isEmpty(recorderDepartment)) {
@@ -14023,17 +14011,17 @@
14023
14011
  return this.isAdmin();
14024
14012
  }
14025
14013
  // Should be login, and status ENABLE
14026
- if (!this.data.account || !this.data.account.pubkey || this.data.account.statusId !== StatusIds.ENABLE)
14014
+ if (!this._data.account || !this._data.account.pubkey || this._data.account.statusId !== StatusIds.ENABLE)
14027
14015
  return false;
14028
- if (!this.data.account.department || !this.data.account.department.id) {
14016
+ if (!this._data.account.department || !this._data.account.department.id) {
14029
14017
  console.warn('User account has no department ! Unable to check write right against recorderDepartment');
14030
14018
  return false;
14031
14019
  }
14032
14020
  // Same recorder department: OK, user can write
14033
- if (this.data.account.department.id === recorderDepartment.id)
14021
+ if (this._data.account.department.id === recorderDepartment.id)
14034
14022
  return true;
14035
14023
  // Else, check if supervisor (or more)
14036
- return PersonUtils.hasUpperOrEqualsProfile(this.data.account.profiles, 'SUPERVISOR');
14024
+ return PersonUtils.hasUpperOrEqualsProfile(this._data.account.profiles, 'SUPERVISOR');
14037
14025
  };
14038
14026
  AccountService.prototype.register = function (data) {
14039
14027
  return __awaiter(this, void 0, void 0, function () {
@@ -14048,7 +14036,7 @@
14048
14036
  throw new Error('Missing required username or password');
14049
14037
  if (this._debug)
14050
14038
  console.debug('[account] Register new user account...', data.account);
14051
- this.data.loaded = false;
14039
+ this._data.loaded = false;
14052
14040
  now = Date.now();
14053
14041
  _b.label = 1;
14054
14042
  case 1:
@@ -14062,29 +14050,29 @@
14062
14050
  data.account.settings.locale = this.settings.locale;
14063
14051
  data.account.settings.latLongFormat = this.settings.latLongFormat;
14064
14052
  data.account.department.id = data.account.department.id || this.environment.defaultDepartmentId;
14065
- this.data.keypair = keypair;
14053
+ this._data.keypair = keypair;
14066
14054
  return [4 /*yield*/, this.saveRemotely(data.account, keypair)];
14067
14055
  case 3:
14068
14056
  account = _b.sent();
14069
14057
  // Default values
14070
14058
  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;
14059
+ this._data.mainProfile = PersonUtils.getMainProfile(account.profiles);
14060
+ this._data.account = account;
14061
+ this._data.pubkey = account.pubkey;
14074
14062
  // Try to auth on pod
14075
14063
  return [4 /*yield*/, this.authenticate(data)];
14076
14064
  case 4:
14077
14065
  // Try to auth on pod
14078
14066
  _b.sent();
14079
- this.data.loaded = true;
14067
+ this._data.loaded = true;
14080
14068
  return [4 /*yield*/, this.saveLocally()];
14081
14069
  case 5:
14082
14070
  _b.sent();
14083
14071
  console.debug("[account] Account successfully registered in " + (Date.now() - now) + "ms");
14084
14072
  // Emit events
14085
- this.onLogin.next(this.data.account);
14086
- this.onChange.next(this.data.account);
14087
- return [2 /*return*/, this.data.account];
14073
+ this.onLogin.next(this._data.account);
14074
+ this.onChange.next(this._data.account);
14075
+ return [2 /*return*/, this._data.account];
14088
14076
  case 6:
14089
14077
  error_1 = _b.sent();
14090
14078
  console.error(error_1 && error_1.message || error_1);
@@ -14106,22 +14094,22 @@
14106
14094
  // Basic auth
14107
14095
  if (tokenType === 'basic' || tokenType === 'basic-and-token') {
14108
14096
  // Generate the authBasic, if used
14109
- if (!this.data.authBasic) {
14097
+ if (!this._data.authBasic) {
14110
14098
  // Skip if token already provided
14111
- if (!(this.data.authToken && tokenType === 'basic-and-token')) {
14099
+ if (!(this._data.authToken && tokenType === 'basic-and-token')) {
14112
14100
  if (!data || !data.username || !data.password)
14113
14101
  throw new Error('Missing username and password');
14114
- this.data.authBasic = this.cryptoService.encodeBase64(data.username + ":" + data.password);
14102
+ this._data.authBasic = this.cryptoService.encodeBase64(data.username + ":" + data.password);
14115
14103
  }
14116
14104
  }
14117
- this.onAuthBasicChange.next(this.data.authBasic);
14105
+ this.onAuthBasicChange.next(this._data.authBasic);
14118
14106
  }
14119
14107
  if (!(tokenType === 'token' || tokenType === 'basic-and-token')) return [3 /*break*/, 5];
14120
14108
  _c.label = 2;
14121
14109
  case 2:
14122
14110
  _c.trys.push([2, 4, , 5]);
14123
- _b = this.data;
14124
- return [4 /*yield*/, this.authenticateAndGetToken(this.data.authToken)];
14111
+ _b = this._data;
14112
+ return [4 /*yield*/, this.authenticateAndGetToken(this._data.authToken)];
14125
14113
  case 3:
14126
14114
  _b.authToken = _c.sent();
14127
14115
  return [3 /*break*/, 5];
@@ -14134,7 +14122,7 @@
14134
14122
  case 5:
14135
14123
  // Forget authBasic, to switch to authToken
14136
14124
  if (tokenType === 'basic-and-token') {
14137
- this.data.authBasic = undefined;
14125
+ this._data.authBasic = undefined;
14138
14126
  this.onAuthBasicChange.next(undefined);
14139
14127
  }
14140
14128
  return [2 /*return*/];
@@ -14165,18 +14153,18 @@
14165
14153
  throw { code: ErrorCodes$2.UNKNOWN_ERROR, message: 'ERROR.SCRYPT_ERROR' };
14166
14154
  case 4:
14167
14155
  // Store pubkey+keypair
14168
- this.data.pubkey = Base58.encode(keypair.publicKey);
14169
- this.data.keypair = keypair;
14156
+ this._data.pubkey = Base58.encode(keypair.publicKey);
14157
+ this._data.keypair = keypair;
14170
14158
  return [4 /*yield*/, this.storage.get(TOKEN_STORAGE_KEY)];
14171
14159
  case 5:
14172
14160
  previousToken = _b.sent();
14173
- previousToken = previousToken && previousToken.startsWith(this.data.pubkey) && previousToken || null;
14161
+ previousToken = previousToken && previousToken.startsWith(this._data.pubkey) && previousToken || null;
14174
14162
  offline = this.settings.hasOfflineFeature() && (this.network.offline || data.offline === true);
14175
14163
  if (!offline) return [3 /*break*/, 6];
14176
- this.data.authToken = previousToken;
14164
+ this._data.authToken = previousToken;
14177
14165
  // Make sure network if set as offline
14178
14166
  this.network.setForceOffline(true, { showToast: false });
14179
- console.info("[account] Login [OK] {pubkey: " + this.data.pubkey.substr(0, 8) + "}, {offline: true}");
14167
+ console.info("[account] Login [OK] {pubkey: " + this._data.pubkey.substr(0, 8) + "}, {offline: true}");
14180
14168
  return [3 /*break*/, 9];
14181
14169
  case 6:
14182
14170
  _b.trys.push([6, 8, , 9]);
@@ -14236,9 +14224,9 @@
14236
14224
  throw error_5;
14237
14225
  case 21:
14238
14226
  // 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];
14227
+ this.onLogin.next(this._data.account);
14228
+ this.onChange.next(this._data.account);
14229
+ return [2 /*return*/, this._data.account];
14242
14230
  }
14243
14231
  });
14244
14232
  });
@@ -14248,7 +14236,7 @@
14248
14236
  return __generator(this, function (_b) {
14249
14237
  switch (_b.label) {
14250
14238
  case 0:
14251
- if (!this.data.pubkey)
14239
+ if (!this._data.pubkey)
14252
14240
  throw new Error('User not logged');
14253
14241
  if (this.network.offline)
14254
14242
  throw new Error('Cannot check account in offline mode');
@@ -14260,9 +14248,9 @@
14260
14248
  _b.sent();
14261
14249
  console.debug('[account] Successfully reload account');
14262
14250
  // 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];
14251
+ this.onLogin.next(this._data.account);
14252
+ this.onChange.next(this._data.account);
14253
+ return [2 /*return*/, this._data.account];
14266
14254
  }
14267
14255
  });
14268
14256
  });
@@ -14277,27 +14265,27 @@
14277
14265
  return __generator(this, function (_b) {
14278
14266
  switch (_b.label) {
14279
14267
  case 0:
14280
- if (!this.data.pubkey)
14268
+ if (!this._data.pubkey)
14281
14269
  return [2 /*return*/, Promise.reject('User not logged')];
14282
- if (this.data.pubkey !== account.pubkey)
14270
+ if (this._data.pubkey !== account.pubkey)
14283
14271
  return [2 /*return*/, Promise.reject('Not user account')];
14284
- return [4 /*yield*/, this.saveRemotely(account, this.data.keypair)];
14272
+ return [4 /*yield*/, this.saveRemotely(account, this._data.keypair)];
14285
14273
  case 1:
14286
14274
  account = _b.sent();
14287
14275
  // Set defaults
14288
14276
  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;
14277
+ this._data.mainProfile = PersonUtils.getMainProfile(account.profiles);
14278
+ this._data.account = account;
14279
+ this._data.loaded = true;
14292
14280
  // Save locally (in storage)
14293
14281
  return [4 /*yield*/, this.saveLocally()];
14294
14282
  case 2:
14295
14283
  // Save locally (in storage)
14296
14284
  _b.sent();
14297
14285
  // Send event
14298
- this.onLogin.next(this.data.account);
14299
- this.onChange.next(this.data.account);
14300
- return [2 /*return*/, this.data.account];
14286
+ this.onLogin.next(this._data.account);
14287
+ this.onChange.next(this._data.account);
14288
+ return [2 /*return*/, this._data.account];
14301
14289
  }
14302
14290
  });
14303
14291
  });
@@ -14308,9 +14296,9 @@
14308
14296
  return __generator(this, function (_b) {
14309
14297
  switch (_b.label) {
14310
14298
  case 0:
14311
- hadAuthToken = this.data.authToken && true;
14312
- hadAuthBasic = this.data.authBasic && true;
14313
- pubkey = this.data && this.data.pubkey;
14299
+ hadAuthToken = this._data.authToken && true;
14300
+ hadAuthBasic = this._data.authBasic && true;
14301
+ pubkey = this._data && this._data.pubkey;
14314
14302
  this.resetData();
14315
14303
  if (!!this.settings.hasOfflineFeature()) return [3 /*break*/, 2];
14316
14304
  // Remove all data from the local storage
@@ -14382,9 +14370,9 @@
14382
14370
  case 1:
14383
14371
  json = _b.sent();
14384
14372
  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)];
14373
+ json = json && this._data.pubkey && (json.pubkey === this._data.pubkey) && json || null;
14374
+ if (!(!json && this._data.pubkey)) return [3 /*break*/, 3];
14375
+ return [4 /*yield*/, this.storage.get(ACCOUNT_STORAGE_KEY + '#' + this._data.pubkey)];
14388
14376
  case 2:
14389
14377
  json = _b.sent();
14390
14378
  json = json && (typeof json === 'string') && JSON.parse(json) || json;
@@ -14515,7 +14503,8 @@
14515
14503
  });
14516
14504
  };
14517
14505
  AccountService.prototype.listenChanges = function () {
14518
- if (!this.data.pubkey)
14506
+ var _this = this;
14507
+ if (!this._data.pubkey)
14519
14508
  return rxjs.Subscription.EMPTY;
14520
14509
  var self = this;
14521
14510
  console.debug('[account] [WS] Listening account changes');
@@ -14531,15 +14520,14 @@
14531
14520
  }).subscribe({
14532
14521
  next: function (_b) {
14533
14522
  var data = _b.data;
14534
- var _a;
14535
- return __awaiter(this, void 0, void 0, function () {
14536
- var existingUpdateDate;
14523
+ return __awaiter(_this, void 0, void 0, function () {
14524
+ var _a, existingUpdateDate;
14537
14525
  return __generator(this, function (_b) {
14538
14526
  switch (_b.label) {
14539
14527
  case 0:
14540
14528
  if (!data)
14541
14529
  return [2 /*return*/];
14542
- existingUpdateDate = toDateISOString((_a = self.data.account) === null || _a === void 0 ? void 0 : _a.updateDate);
14530
+ existingUpdateDate = toDateISOString((_a = self._data.account) === null || _a === void 0 ? void 0 : _a.updateDate);
14543
14531
  if (!(existingUpdateDate !== data.updateDate)) return [3 /*break*/, 2];
14544
14532
  console.debug("[account] [WS] Detected update on {" + data.updateDate + "}");
14545
14533
  return [4 /*yield*/, self.refresh()];
@@ -14551,32 +14539,30 @@
14551
14539
  });
14552
14540
  });
14553
14541
  },
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
- });
14542
+ error: function (err) { return __awaiter(_this, void 0, void 0, function () {
14543
+ return __generator(this, function (_b) {
14544
+ switch (_b.label) {
14545
+ case 0:
14546
+ if (!(err && +err.code === ServerErrorCodes.NOT_FOUND)) return [3 /*break*/, 2];
14547
+ console.info('[account] Account not exists anymore: force user to logout...', err);
14548
+ return [4 /*yield*/, self.logout()];
14549
+ case 1:
14550
+ _b.sent();
14551
+ return [3 /*break*/, 5];
14552
+ case 2:
14553
+ if (!(err && +err.code === ServerErrorCodes.UNAUTHORIZED)) return [3 /*break*/, 4];
14554
+ console.info('[account] Account not authorized: force user to logout...', err);
14555
+ return [4 /*yield*/, self.logout()];
14556
+ case 3:
14557
+ _b.sent();
14558
+ return [3 /*break*/, 5];
14559
+ case 4:
14560
+ console.warn('[account] [WS] Received error:', err);
14561
+ _b.label = 5;
14562
+ case 5: return [2 /*return*/];
14563
+ }
14578
14564
  });
14579
- },
14565
+ }); },
14580
14566
  complete: function () {
14581
14567
  console.debug('[account] [WS] Completed');
14582
14568
  }
@@ -14633,15 +14619,26 @@
14633
14619
  this._$additionalFields.next(values.concat(field));
14634
14620
  };
14635
14621
  /* -- protected method -- */
14622
+ AccountService.prototype.resetData = function () {
14623
+ this._data.loaded = false;
14624
+ this._data.keypair = null;
14625
+ this._data.authToken = null;
14626
+ this._data.authBasic = null;
14627
+ this._data.pubkey = null;
14628
+ this._data.mainProfile = null;
14629
+ this._data.account = new exports.Account();
14630
+ this._data.person = null;
14631
+ this._data.department = null;
14632
+ };
14636
14633
  AccountService.prototype.loadData = function (opts) {
14637
14634
  return __awaiter(this, void 0, void 0, function () {
14638
14635
  var account, error_6;
14639
14636
  return __generator(this, function (_b) {
14640
14637
  switch (_b.label) {
14641
14638
  case 0:
14642
- if (!this.data.pubkey)
14639
+ if (!this._data.pubkey)
14643
14640
  throw new Error('User not logged');
14644
- this.data.loaded = false;
14641
+ this._data.loaded = false;
14645
14642
  _b.label = 1;
14646
14643
  case 1:
14647
14644
  _b.trys.push([1, 3, , 4]);
@@ -14654,16 +14651,16 @@
14654
14651
  account.settings.locale = account.settings.locale || this.settings.locale;
14655
14652
  account.settings.latLongFormat = account.settings.latLongFormat || this.settings.latLongFormat || 'DDMM';
14656
14653
  // Read main profile
14657
- this.data.mainProfile = PersonUtils.getMainProfile(account.profiles);
14654
+ this._data.mainProfile = PersonUtils.getMainProfile(account.profiles);
14658
14655
  // Update, instead of replace it
14659
- if (this.data.account) {
14660
- this.data.account.fromObject(account);
14656
+ if (this._data.account) {
14657
+ this._data.account.fromObject(account);
14661
14658
  }
14662
14659
  else {
14663
- this.data.account = account;
14660
+ this._data.account = account;
14664
14661
  }
14665
- this.data.loaded = true;
14666
- return [2 /*return*/, this.data.account];
14662
+ this._data.loaded = true;
14663
+ return [2 /*return*/, this._data.account];
14667
14664
  case 3:
14668
14665
  error_6 = _b.sent();
14669
14666
  this.resetData();
@@ -14702,9 +14699,9 @@
14702
14699
  return [2 /*return*/];
14703
14700
  if (this._debug)
14704
14701
  console.debug("[account] Account restoration...");
14705
- this.data.authToken = token;
14706
- this.data.pubkey = pubkey;
14707
- this.data.keypair = seckey && {
14702
+ this._data.authToken = token;
14703
+ this._data.pubkey = pubkey;
14704
+ this._data.keypair = seckey && {
14708
14705
  publicKey: Base58.decode(pubkey),
14709
14706
  secretKey: Base58.decode(seckey)
14710
14707
  } || null;
@@ -14715,7 +14712,7 @@
14715
14712
  return [4 /*yield*/, this.authenticate()];
14716
14713
  case 3:
14717
14714
  _b.sent();
14718
- if (!this.data.authToken && !this.data.authBasic)
14715
+ if (!this._data.authToken && !this._data.authBasic)
14719
14716
  throw new Error('Authentication failed');
14720
14717
  return [3 /*break*/, 5];
14721
14718
  case 4:
@@ -14748,14 +14745,14 @@
14748
14745
  return [2 /*return*/];
14749
14746
  account = exports.Account.fromObject(jsonAccount);
14750
14747
  // Update data
14751
- this.data.account = account;
14752
- this.data.mainProfile = PersonUtils.getMainProfile(account.profiles);
14753
- this.data.loaded = true;
14748
+ this._data.account = account;
14749
+ this._data.mainProfile = PersonUtils.getMainProfile(account.profiles);
14750
+ this._data.loaded = true;
14754
14751
  // Emit event
14755
- this.onLogin.next(this.data.account);
14756
- this.onChange.next(this.data.account);
14752
+ this.onLogin.next(this._data.account);
14753
+ this.onChange.next(this._data.account);
14757
14754
  if (this._debug)
14758
- console.debug("[account] Account restoration [OK] {pubkey: " + pubkey.substr(0, 8) + "}, {profile: " + this.data.mainProfile + "}");
14755
+ console.debug("[account] Account restoration [OK] {pubkey: " + pubkey.substr(0, 8) + "}, {profile: " + this._data.mainProfile + "}");
14759
14756
  return [2 /*return*/, account];
14760
14757
  }
14761
14758
  });
@@ -14771,12 +14768,12 @@
14771
14768
  return __generator(this, function (_b) {
14772
14769
  switch (_b.label) {
14773
14770
  case 0:
14774
- if (!this.data.pubkey)
14771
+ if (!this._data.pubkey)
14775
14772
  throw new Error('User not logged');
14776
14773
  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;
14774
+ console.debug("[account] Saving account {" + this._data.pubkey.substring(0, 6) + "} in local storage...");
14775
+ json = this._data.account.asObject({ keepTypename: true });
14776
+ seckey = this._data.keypair && this._data.keypair.secretKey && Base58.encode(this._data.keypair.secretKey) || null;
14780
14777
  hasAvatarUrl = json.avatar && !json.avatar.endsWith(DEFAULT_AVATAR_IMAGE) &&
14781
14778
  (json.avatar.startsWith('http://') || (json.avatar.startsWith('https://')));
14782
14779
  if (!(hasAvatarUrl && this.network.online)) return [3 /*break*/, 2];
@@ -14799,9 +14796,9 @@
14799
14796
  case 2:
14800
14797
  _b.trys.push([2, 4, , 5]);
14801
14798
  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),
14799
+ this.storage.set(PUBKEY_STORAGE_KEY, this._data.pubkey),
14800
+ this.storage.set(TOKEN_STORAGE_KEY, this._data.authToken),
14801
+ this.storage.set(ACCOUNT_STORAGE_KEY + "#" + this._data.pubkey, json),
14805
14802
  // Secret key (optional)
14806
14803
  seckey && this.storage.set(SECKEY_STORAGE_KEY, seckey) || this.storage.remove(SECKEY_STORAGE_KEY),
14807
14804
  // Remove old storage key
@@ -14878,7 +14875,7 @@
14878
14875
  return __generator(this, function (_b) {
14879
14876
  switch (_b.label) {
14880
14877
  case 0:
14881
- if (!this.data.pubkey)
14878
+ if (!this._data.pubkey)
14882
14879
  throw new Error('User not logged');
14883
14880
  if (!counter)
14884
14881
  console.info('[account] Authentication on pod...');
@@ -14905,7 +14902,7 @@
14905
14902
  if (data_1 && data_1.authenticate) {
14906
14903
  // Store the token
14907
14904
  this.onAuthTokenChange.next(token);
14908
- console.info("[account] Authentication on pod [OK] {pubkey: '" + this.data.pubkey.substr(0, 8) + "'}");
14905
+ console.info("[account] Authentication on pod [OK] {pubkey: '" + this._data.pubkey.substr(0, 8) + "'}");
14909
14906
  return [2 /*return*/, token]; // return the token
14910
14907
  }
14911
14908
  _b.label = 2;
@@ -14931,10 +14928,10 @@
14931
14928
  if (!signatureOK) {
14932
14929
  console.warn('FIXME: Bad peer signature on auth challenge !', data.authChallenge);
14933
14930
  }
14934
- return [4 /*yield*/, this.cryptoService.sign(data.authChallenge.challenge, this.data.keypair)];
14931
+ return [4 /*yield*/, this.cryptoService.sign(data.authChallenge.challenge, this._data.keypair)];
14935
14932
  case 5:
14936
14933
  signature = _b.sent();
14937
- newToken = this.data.pubkey + ":" + data.authChallenge.challenge + "|" + signature;
14934
+ newToken = this._data.pubkey + ":" + data.authChallenge.challenge + "|" + signature;
14938
14935
  return [4 /*yield*/, this.authenticateAndGetToken(newToken, (counter || 1) + 1 /* increment */)];
14939
14936
  case 6:
14940
14937
  // iterate with the new token
@@ -15264,9 +15261,8 @@
15264
15261
  Object.defineProperty(ConfigService.prototype, "config", {
15265
15262
  get: function () {
15266
15263
  // If first call: start loading
15267
- if (!this._started) {
15264
+ if (!this._started)
15268
15265
  this.start();
15269
- }
15270
15266
  return this.$data.pipe(operators.filter(isNotNil));
15271
15267
  },
15272
15268
  enumerable: false,
@@ -15281,30 +15277,49 @@
15281
15277
  console.info('[config] Starting configuration...');
15282
15278
  this._startPromise = this.graphql.ready()
15283
15279
  .then(function () { return _this.loadOrRestoreLocally(); })
15284
- .then(function () {
15280
+ .then(function (data) {
15285
15281
  _this._started = true;
15286
15282
  _this._startPromise = undefined;
15283
+ _this.$data.next(data);
15284
+ return data;
15287
15285
  })
15288
15286
  .catch(function (err) {
15289
15287
  console.error(err && err.message || err, err);
15288
+ _this._started = false;
15290
15289
  _this._startPromise = undefined;
15290
+ return null;
15291
15291
  });
15292
15292
  return this._startPromise;
15293
15293
  };
15294
15294
  ConfigService.prototype.stop = function () {
15295
- this._subscription.unsubscribe();
15296
- this._subscription = new rxjs.Subscription();
15297
- this._started = false;
15298
- this._startPromise = undefined;
15295
+ return __awaiter(this, void 0, void 0, function () {
15296
+ return __generator(this, function (_a) {
15297
+ this._subscription.unsubscribe();
15298
+ this._subscription = new rxjs.Subscription();
15299
+ this._started = false;
15300
+ this._startPromise = undefined;
15301
+ return [2 /*return*/];
15302
+ });
15303
+ });
15299
15304
  };
15300
15305
  ConfigService.prototype.restart = function () {
15301
- if (this.started)
15302
- this.stop();
15303
- return this.start();
15306
+ return __awaiter(this, void 0, void 0, function () {
15307
+ return __generator(this, function (_a) {
15308
+ switch (_a.label) {
15309
+ case 0:
15310
+ if (!this.started) return [3 /*break*/, 2];
15311
+ return [4 /*yield*/, this.stop()];
15312
+ case 1:
15313
+ _a.sent();
15314
+ _a.label = 2;
15315
+ case 2: return [2 /*return*/, this.start()];
15316
+ }
15317
+ });
15318
+ });
15304
15319
  };
15305
15320
  ConfigService.prototype.ready = function () {
15306
15321
  if (this._started)
15307
- return Promise.resolve();
15322
+ return Promise.resolve(this.$data.value);
15308
15323
  if (this._startPromise)
15309
15324
  return this._startPromise;
15310
15325
  return this.start();
@@ -15375,7 +15390,7 @@
15375
15390
  return [4 /*yield*/, this.loadDefault({ fetchPolicy: 'network-only' })];
15376
15391
  case 2:
15377
15392
  reloadedConfig = _a.sent();
15378
- defaultConfig = this.$data.getValue();
15393
+ defaultConfig = this.$data.value;
15379
15394
  if (isNotNil(defaultConfig) && reloadedConfig.label === defaultConfig.label) {
15380
15395
  // Emit update event when is default config
15381
15396
  this.$data.next(reloadedConfig);
@@ -15484,8 +15499,7 @@
15484
15499
  if (wasJustLoaded) {
15485
15500
  // TODO
15486
15501
  }
15487
- this.$data.next(data);
15488
- return [2 /*return*/];
15502
+ return [2 /*return*/, data];
15489
15503
  }
15490
15504
  });
15491
15505
  });
@@ -15664,6 +15678,7 @@
15664
15678
  this.browser = browser;
15665
15679
  this.downloader = downloader;
15666
15680
  this._started = false;
15681
+ this._downloadingPromise = new Map();
15667
15682
  this._debug = !environment.production;
15668
15683
  if (this._debug)
15669
15684
  console.debug('[platform] Creating service');
@@ -15696,6 +15711,13 @@
15696
15711
  PlatformService.prototype.isAndroidCordova = function () {
15697
15712
  return this._android && this._cordova;
15698
15713
  };
15714
+ Object.defineProperty(PlatformService.prototype, "canDownload", {
15715
+ get: function () {
15716
+ return this._android && !!this.downloader;
15717
+ },
15718
+ enumerable: false,
15719
+ configurable: true
15720
+ });
15699
15721
  PlatformService.prototype.width = function () {
15700
15722
  return this.platform.width();
15701
15723
  };
@@ -15738,7 +15760,7 @@
15738
15760
  .then(function () {
15739
15761
  _this._started = true;
15740
15762
  _this._startPromise = undefined;
15741
- console.info("[platform] Starting platform [OK] {mobile: " + _this._mobile + ", touchUi: " + _this.touchUi + "} in " + (Date.now() - now) + "ms");
15763
+ console.info("[platform] Starting platform [OK] {mobile: " + _this._mobile + ", touchUi: " + _this.touchUi + ", canDownload: " + _this.canDownload + "} in " + (Date.now() - now) + "ms");
15742
15764
  // Update cache configuration when network changed
15743
15765
  _this.networkService.onNetworkStatusChanges.subscribe(function (type) { return _this.configureCache(type !== 'none'); });
15744
15766
  // Update authentication type
@@ -15787,18 +15809,48 @@
15787
15809
  }
15788
15810
  };
15789
15811
  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
- }
15812
+ return __awaiter(this, void 0, void 0, function () {
15813
+ var promise, location, err_1;
15814
+ return __generator(this, function (_a) {
15815
+ switch (_a.label) {
15816
+ case 0:
15817
+ if (!request || !request.uri)
15818
+ throw new Error('Missing argument \'request\' or \'request.uri\'');
15819
+ if (!(this._android && this.downloader)) return [3 /*break*/, 6];
15820
+ promise = this._downloadingPromise.get(request.uri);
15821
+ if (promise)
15822
+ return [2 /*return*/, promise];
15823
+ request = Object.assign({ visibleInDownloadsUi: true, notificationVisibility: ngx$6.NotificationVisibility.VisibleNotifyCompleted, title: request.title || request.filename }, request);
15824
+ _a.label = 1;
15825
+ case 1:
15826
+ _a.trys.push([1, 3, 4, 5]);
15827
+ console.debug('[platform] Downloading, using request: ' + JSON.stringify(request));
15828
+ // Start download
15829
+ promise = this.downloader.download(request);
15830
+ // Remember this request uri
15831
+ this._downloadingPromise.set(request.uri, promise);
15832
+ return [4 /*yield*/, promise];
15833
+ case 2:
15834
+ location = _a.sent();
15835
+ console.info('[platform] File successfully downloaded at:' + location);
15836
+ return [2 /*return*/, location];
15837
+ case 3:
15838
+ err_1 = _a.sent();
15839
+ console.error('[platform] Unable to download: ' + (err_1 && err_1.message || err_1));
15840
+ throw err_1;
15841
+ case 4:
15842
+ // Forget the request
15843
+ this._downloadingPromise.delete(request.uri);
15844
+ return [7 /*endfinally*/];
15845
+ case 5: return [3 /*break*/, 7];
15846
+ case 6:
15847
+ console.warn('[platform] Cannot use Android downloader: using browser open()');
15848
+ this.open(request.uri, '_system', 'location=no', true);
15849
+ return [2 /*return*/, undefined];
15850
+ case 7: return [2 /*return*/];
15851
+ }
15852
+ });
15853
+ });
15802
15854
  };
15803
15855
  /* -- protected methods -- */
15804
15856
  PlatformService.prototype.configureCordovaPlugins = function (mobile) {
@@ -15894,7 +15946,7 @@
15894
15946
  };
15895
15947
  PlatformService.prototype.migrateStorage = function (forage) {
15896
15948
  return __awaiter(this, void 0, void 0, function () {
15897
- var canMigrate, oldForage, keys, now, toast_1, duration, err_1;
15949
+ var canMigrate, oldForage, keys, now, toast_1, duration, err_2;
15898
15950
  return __generator(this, function (_a) {
15899
15951
  switch (_a.label) {
15900
15952
  case 0:
@@ -15912,8 +15964,8 @@
15912
15964
  case 1:
15913
15965
  keys = _a.sent();
15914
15966
  if (!isEmptyArray(keys)) return [3 /*break*/, 3];
15915
- // Drop the old instance
15916
- console.info("[platform] Drop old storage {name: '" + forage.config().name + "', driver: '" + oldForage.driver() + "'}");
15967
+ // Drop the old instance (not need anymore)
15968
+ console.debug("[platform] Old storage is empty: dropping unused instance {name: '" + forage.config().name + "', driver: '" + oldForage.driver() + "'}");
15917
15969
  return [4 /*yield*/, oldForage.dropInstance()];
15918
15970
  case 2:
15919
15971
  _a.sent();
@@ -15947,8 +15999,8 @@
15947
15999
  _a.sent();
15948
16000
  return [3 /*break*/, 12];
15949
16001
  case 9:
15950
- err_1 = _a.sent();
15951
- console.error(err_1 && err_1.message || err_1, err_1);
16002
+ err_2 = _a.sent();
16003
+ console.error(err_2 && err_2.message || err_2, err_2);
15952
16004
  return [4 /*yield*/, toast_1.dismiss()];
15953
16005
  case 10:
15954
16006
  _a.sent();
@@ -17594,7 +17646,7 @@
17594
17646
  AutocompleteTestPage.decorators = [
17595
17647
  { type: i0.Component, args: [{
17596
17648
  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"
17649
+ 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
17650
  },] }
17599
17651
  ];
17600
17652
  AutocompleteTestPage.ctorParameters = function () { return [
@@ -21257,22 +21309,25 @@
21257
21309
  ANDROID_APK: 'application/vnd.android.package-archive'
21258
21310
  });
21259
21311
  var AppInstallUpgradeCard = /** @class */ (function () {
21260
- function AppInstallUpgradeCard(modalCtrl, configService, platform, cd, network, environment) {
21312
+ function AppInstallUpgradeCard(modalCtrl, configService, toastController, alertController, translate, cd, platform, network, environment) {
21261
21313
  this.modalCtrl = modalCtrl;
21262
21314
  this.configService = configService;
21263
- this.platform = platform;
21315
+ this.toastController = toastController;
21316
+ this.alertController = alertController;
21317
+ this.translate = translate;
21264
21318
  this.cd = cd;
21319
+ this.platform = platform;
21265
21320
  this.network = network;
21266
21321
  this.environment = environment;
21267
21322
  this._subscription = new rxjs.Subscription();
21268
21323
  this._showUpdateOfflineFeature = false;
21269
21324
  this.loading = true;
21325
+ this.downloading = false;
21270
21326
  this.waitingNetwork = false;
21271
21327
  this.showUpgradeWarning = true;
21272
21328
  this.showOfflineWarning = true;
21273
21329
  this.showInstallButton = false;
21274
21330
  this.onUpdateOfflineModeClick = new i0.EventEmitter();
21275
- this.isAndroidCordova = platform.isAndroidCordova();
21276
21331
  }
21277
21332
  Object.defineProperty(AppInstallUpgradeCard.prototype, "showUpdateOfflineFeature", {
21278
21333
  get: function () {
@@ -21298,18 +21353,8 @@
21298
21353
  this.offline = this.network.offline;
21299
21354
  // Listen pod config
21300
21355
  this._subscription.add(this.configService.config
21301
- .subscribe(function (config) {
21302
- console.info('[install] Checking if upgrade or install is need...');
21303
- var installLinks = _this.getAllInstallLinks(config);
21304
- // Check for upgrade
21305
- _this.updateLinks = _this.getCompatibleUpgradeLinks(installLinks, config);
21306
- // Check for install links (if no upgrade need)
21307
- _this.installLinks = !_this.updateLinks && _this.getCompatibleInstallLinks(installLinks);
21308
- setTimeout(function () {
21309
- _this.loading = false;
21310
- _this.markForCheck();
21311
- }, 2000); // Add a delay, for animation
21312
- }));
21356
+ .pipe(operators.debounceTime(1000))
21357
+ .subscribe(function (config) { return _this.checkNeedInstallOrUpdate(config); }));
21313
21358
  // Listen network changes
21314
21359
  this._subscription.add(this.network.onNetworkStatusChanges
21315
21360
  .pipe(
@@ -21317,8 +21362,10 @@
21317
21362
  //tap(() => this.waitingNetwork = false),
21318
21363
  operators.map(function (connectionType) { return connectionType === 'none'; }), operators.distinctUntilChanged())
21319
21364
  .subscribe(function (offline) {
21320
- _this.offline = offline;
21321
- _this.markForCheck();
21365
+ if (_this.offline !== offline) {
21366
+ _this.offline = offline;
21367
+ _this.markForCheck();
21368
+ }
21322
21369
  }));
21323
21370
  return [2 /*return*/];
21324
21371
  }
@@ -21327,28 +21374,83 @@
21327
21374
  };
21328
21375
  AppInstallUpgradeCard.prototype.ngOnDestroy = function () {
21329
21376
  this._subscription.unsubscribe();
21377
+ this._subscription = null;
21330
21378
  };
21331
- AppInstallUpgradeCard.prototype.downloadLink = function (event, link) {
21332
- if (!link || !link.url)
21333
- return; // Skip
21334
- this.platform.download({
21335
- uri: link.url,
21336
- filename: link.downloadFilename,
21337
- mimeType: link.mimeType
21379
+ AppInstallUpgradeCard.prototype.download = function (event, link) {
21380
+ return __awaiter(this, void 0, void 0, function () {
21381
+ var location, err_1;
21382
+ return __generator(this, function (_a) {
21383
+ switch (_a.label) {
21384
+ case 0:
21385
+ if (!link || !link.url) {
21386
+ console.error('[install-upgrade-card] Missing required argument \'link.url\'');
21387
+ return [2 /*return*/]; // Skip
21388
+ }
21389
+ this.downloading = true;
21390
+ _a.label = 1;
21391
+ case 1:
21392
+ _a.trys.push([1, 3, 4, 5]);
21393
+ console.info('[install-upgrade-card] User click to download file: ' + link.url);
21394
+ return [4 /*yield*/, this.platform.download({
21395
+ uri: link.url,
21396
+ title: link.name,
21397
+ filename: link.downloadFilename,
21398
+ mimeType: link.mimeType
21399
+ })];
21400
+ case 2:
21401
+ location = _a.sent();
21402
+ console.info('[install-upgrade-card] File downloaded at: ' + location);
21403
+ return [3 /*break*/, 5];
21404
+ case 3:
21405
+ err_1 = _a.sent();
21406
+ console.error('[install-upgrade] Download failed: ' + (err_1 && err_1.message || err_1));
21407
+ this.showToast({
21408
+ message: 'ERROR.DOWNLOAD_FAILED',
21409
+ messageParams: { error: err_1 },
21410
+ type: 'error',
21411
+ showCloseButton: true
21412
+ });
21413
+ return [2 /*return*/]; // Stop here
21414
+ case 4:
21415
+ this.downloading = false;
21416
+ this.markForCheck();
21417
+ return [7 /*endfinally*/];
21418
+ case 5:
21419
+ if (!this._subscription)
21420
+ return [2 /*return*/]; // Skip if component destroyed
21421
+ return [4 /*yield*/, this.showDownloadCompleteDialog()];
21422
+ case 6:
21423
+ _a.sent();
21424
+ return [2 /*return*/];
21425
+ }
21426
+ });
21338
21427
  });
21339
21428
  };
21340
21429
  AppInstallUpgradeCard.prototype.tryOnline = function () {
21341
- var _this = this;
21342
- this.waitingNetwork = true;
21343
- this.markForCheck();
21344
- this.network.tryOnline({
21345
- showLoadingToast: false,
21346
- showOnlineToast: true,
21347
- showOfflineToast: false
21348
- })
21349
- .then(function () {
21350
- _this.waitingNetwork = false;
21351
- _this.markForCheck();
21430
+ return __awaiter(this, void 0, void 0, function () {
21431
+ return __generator(this, function (_a) {
21432
+ switch (_a.label) {
21433
+ case 0:
21434
+ this.waitingNetwork = true;
21435
+ this.markForCheck();
21436
+ _a.label = 1;
21437
+ case 1:
21438
+ _a.trys.push([1, , 3, 4]);
21439
+ return [4 /*yield*/, this.network.tryOnline({
21440
+ showLoadingToast: false,
21441
+ showOnlineToast: true,
21442
+ showOfflineToast: false
21443
+ })];
21444
+ case 2:
21445
+ _a.sent();
21446
+ return [3 /*break*/, 4];
21447
+ case 3:
21448
+ this.waitingNetwork = false;
21449
+ this.markForCheck();
21450
+ return [7 /*endfinally*/];
21451
+ case 4: return [2 /*return*/];
21452
+ }
21453
+ });
21352
21454
  });
21353
21455
  };
21354
21456
  AppInstallUpgradeCard.prototype.getPlatformName = function (platform) {
@@ -21365,6 +21467,34 @@
21365
21467
  return value;
21366
21468
  };
21367
21469
  /* -- private method -- */
21470
+ AppInstallUpgradeCard.prototype.checkNeedInstallOrUpdate = function (config) {
21471
+ return __awaiter(this, void 0, void 0, function () {
21472
+ var installLinks;
21473
+ return __generator(this, function (_a) {
21474
+ switch (_a.label) {
21475
+ case 0:
21476
+ console.info('[install] Check if need to install or upgrade...');
21477
+ _a.label = 1;
21478
+ case 1:
21479
+ _a.trys.push([1, , 3, 4]);
21480
+ installLinks = this.getAllInstallLinks(config);
21481
+ // Check for upgrade
21482
+ this.updateLinks = this.getCompatibleUpgradeLinks(installLinks, config);
21483
+ // Check for install links (if no upgrade need)
21484
+ this.installLinks = !this.updateLinks && this.getCompatibleInstallLinks(installLinks);
21485
+ return [4 /*yield*/, sleep(500)];
21486
+ case 2:
21487
+ _a.sent(); // Add a delay, for animation
21488
+ return [3 /*break*/, 4];
21489
+ case 3:
21490
+ this.loading = false;
21491
+ this.markForCheck();
21492
+ return [7 /*endfinally*/];
21493
+ case 4: return [2 /*return*/];
21494
+ }
21495
+ });
21496
+ });
21497
+ };
21368
21498
  AppInstallUpgradeCard.prototype.getCompatibleInstallLinks = function (installLinks) {
21369
21499
  // Cordova already running: not need to install
21370
21500
  if (this.platform.is('cordova'))
@@ -21377,15 +21507,20 @@
21377
21507
  };
21378
21508
  AppInstallUpgradeCard.prototype.getCompatibleUpgradeLinks = function (installLinks, config) {
21379
21509
  var _this = this;
21380
- var appMinVersion = config.getProperty(CORE_CONFIG_OPTIONS.APP_MIN_VERSION);
21381
- var needUpgrade = appMinVersion && !VersionUtils.isCompatible(appMinVersion, this.environment.version);
21510
+ var appVersion = this.environment.version;
21511
+ var appMinVersionFromPod = config.getProperty(CORE_CONFIG_OPTIONS.APP_MIN_VERSION);
21512
+ if (!appVersion) {
21513
+ console.error('Missing value for \'environment.version\': cannot check app compatibility!');
21514
+ return undefined;
21515
+ }
21516
+ var needUpgrade = appMinVersionFromPod && !VersionUtils.isCompatible(appMinVersionFromPod, appVersion);
21382
21517
  if (!needUpgrade)
21383
21518
  return undefined;
21384
21519
  var upgradeLinks = installLinks
21385
21520
  .filter(function (link) { return _this.platform.is('mobileweb') || (link.platform && _this.platform.is(link.platform)); });
21386
21521
  // Use min version as default version
21387
21522
  upgradeLinks.forEach(function (link) {
21388
- link.version = link.version || appMinVersion;
21523
+ link.version = link.version || appMinVersionFromPod;
21389
21524
  });
21390
21525
  return isNotEmptyArray(upgradeLinks) ? upgradeLinks : undefined;
21391
21526
  };
@@ -21397,6 +21532,12 @@
21397
21532
  var url = config.getProperty(CORE_CONFIG_OPTIONS.ANDROID_INSTALL_URL);
21398
21533
  if (isNilOrBlank(url))
21399
21534
  url = this.environment.defaultAndroidInstallUrl || null;
21535
+ // Resolve relative URL
21536
+ var peer = this.network.peer;
21537
+ if (peer && (url.startsWith('./') || url.startsWith('/'))) {
21538
+ url = exports.Peer.path(peer, url);
21539
+ }
21540
+ var minVersion = config.getProperty(CORE_CONFIG_OPTIONS.APP_MIN_VERSION);
21400
21541
  // Compute App name
21401
21542
  var name = isNotNilOrBlank(url) && config.label || this.environment.defaultAppName || 'SUMARiS';
21402
21543
  if (url) {
@@ -21405,7 +21546,7 @@
21405
21546
  var mimeType = void 0;
21406
21547
  // Get file name
21407
21548
  var filename = this.getFilename(url);
21408
- version = config.getProperty(CORE_CONFIG_OPTIONS.APP_MIN_VERSION);
21549
+ version = minVersion || 'latest';
21409
21550
  // OK, this is a downloadable APK file (e.g. NOT a link to a playstore)
21410
21551
  if (filename === null || filename === void 0 ? void 0 : filename.endsWith('.apk')) {
21411
21552
  // Define mime type
@@ -21415,13 +21556,12 @@
21415
21556
  version = versionMatches && versionMatches[1] || version;
21416
21557
  // Compute a new file name, with the version
21417
21558
  if (isNotNilOrBlank(name)) {
21418
- downloadFilename = name + "-" + version + ".apk";
21559
+ downloadFilename = (name + "-v" + version + ".apk").toLowerCase();
21419
21560
  }
21420
21561
  else {
21421
21562
  downloadFilename = filename;
21422
- // Replace 'latest' with the app min version
21563
+ // Replace 'latest' with the app version, if present
21423
21564
  if (downloadFilename.indexOf('latest')) {
21424
- version = config.getProperty(CORE_CONFIG_OPTIONS.APP_MIN_VERSION);
21425
21565
  downloadFilename = downloadFilename.replace('latest', version);
21426
21566
  }
21427
21567
  }
@@ -21450,12 +21590,60 @@
21450
21590
  AppInstallUpgradeCard.prototype.markForCheck = function () {
21451
21591
  this.cd.markForCheck();
21452
21592
  };
21593
+ AppInstallUpgradeCard.prototype.showToast = function (opts) {
21594
+ return __awaiter(this, void 0, void 0, function () {
21595
+ return __generator(this, function (_a) {
21596
+ switch (_a.label) {
21597
+ case 0: return [4 /*yield*/, Toasts.show(this.toastController, this.translate, opts)];
21598
+ case 1:
21599
+ _a.sent();
21600
+ return [2 /*return*/];
21601
+ }
21602
+ });
21603
+ });
21604
+ };
21605
+ AppInstallUpgradeCard.prototype.showDownloadCompleteDialog = function () {
21606
+ return __awaiter(this, void 0, void 0, function () {
21607
+ var translations, alert;
21608
+ return __generator(this, function (_a) {
21609
+ switch (_a.label) {
21610
+ case 0: return [4 /*yield*/, this.translate.get([
21611
+ 'INFO.ALERT_HEADER',
21612
+ 'INFO.DOWNLOAD_UPDATE_SUCCEED',
21613
+ 'COMMON.BTN_CLOSE'
21614
+ ]).toPromise()];
21615
+ case 1:
21616
+ translations = _a.sent();
21617
+ return [4 /*yield*/, this.alertController.create({
21618
+ header: translations['INFO.ALERT_HEADER'],
21619
+ message: translations['INFO.DOWNLOAD_UPDATE_SUCCEED'],
21620
+ buttons: [
21621
+ {
21622
+ text: translations['COMMON.BTN_CLOSE'],
21623
+ role: 'cancel',
21624
+ cssClass: 'secondary'
21625
+ }
21626
+ ]
21627
+ })];
21628
+ case 2:
21629
+ alert = _a.sent();
21630
+ return [4 /*yield*/, alert.present()];
21631
+ case 3:
21632
+ _a.sent();
21633
+ return [4 /*yield*/, alert.onDidDismiss()];
21634
+ case 4:
21635
+ _a.sent();
21636
+ return [2 /*return*/];
21637
+ }
21638
+ });
21639
+ });
21640
+ };
21453
21641
  return AppInstallUpgradeCard;
21454
21642
  }());
21455
21643
  AppInstallUpgradeCard.decorators = [
21456
21644
  { type: i0.Component, args: [{
21457
21645
  selector: 'app-install-upgrade-card',
21458
- template: "\n\n<!-- Offline mode card-->\n<ion-card *ngIf=\"showOfflineWarning && (!loading && offline || waitingNetwork)\"\n color=\"accent\"\n class=\"main ion-no-margin\"\n @slideUpDownAnimation>\n <ion-card-content class=\"ion-no-padding\">\n <ion-grid>\n <ion-row>\n <ion-col>\n <ion-text class=\"ion-text-wrap\" *ngIf=\"!waitingNetwork; else waitingNetworkText\">\n <h4>\n <b *ngIf=\"isLogin; else notLogin\" [innerHTML]=\"'NETWORK.INFO.OFFLINE_OR_UNAUTHORIZED'| translate\"></b>\n <ng-template #notLogin>\n <b [innerHTML]=\"'NETWORK.INFO.OFFLINE'| translate\"></b>\n </ng-template>\n </h4>\n <h3>\n <small [innerHTML]=\"'NETWORK.INFO.OFFLINE_HELP'|translate\"></small>\n </h3>\n </ion-text>\n <ng-template #waitingNetworkText>\n <ion-text class=\"ion-text-wrap\" [innerHTML]=\"'NETWORK.INFO.RETRY_TO_CONNECT'| translate\"></ion-text>\n </ng-template>\n </ion-col>\n <ion-col size=\"auto\">\n\n <!-- Retry button -->\n <ion-button *ngIf=\"!waitingNetwork; else waitingSpinner\" color=\"tertiary\" class=\"ion-float-end\"\n (click)=\"tryOnline()\">\n <span translate>NETWORK.BTN_CHECK_ALIVE</span>\n </ion-button>\n\n <!-- Waiting spinner -->\n <ng-template #waitingSpinner>\n <ion-spinner *ngIf=\"waitingNetwork\" color=\"light\"></ion-spinner>\n </ng-template>\n </ion-col>\n </ion-row>\n </ion-grid>\n\n </ion-card-content>\n</ion-card>\n\n<!-- Upgrade links -->\n<ion-card *ngIf=\"showUpgradeWarning && !loading && !offline && updateLinks\"\n color=\"accent\"\n class=\"ion-no-margin\"\n @slideUpDownAnimation>\n <ion-card-content class=\"ion-no-padding\">\n <ion-grid>\n <ion-row *ngFor=\"let link of updateLinks; last as last\">\n <ion-col>\n <ion-text class=\"ion-text-wrap\">\n <h3>\n <span *ngIf=\"link.version\" [innerHTML]=\"'INFO.UPDATE_APP_TO_VERSION'| translate: link\"></span>\n <span *ngIf=\"!link.version\" [innerHTML]=\"'INFO.UPDATE_APP'| translate: link\"></span>\n </h3>\n <h4>\n <small *ngIf=\"last\" [innerHTML]=\"'INFO.UPDATE_APP_HELP'|translate\"></small>\n </h4>\n </ion-text>\n </ion-col>\n\n <ion-col size=\"auto\">\n\n <!-- Download button -->\n <ion-button *ngIf=\"link.downloadFilename; else redirectButton\"\n [download]=\"link.downloadFilename\"\n [href]=\"link.url\"\n color=\"tertiary\" class=\"ion-float-end\" >\n <ion-label translate>COMMON.BTN_DOWNLOAD</ion-label>\n </ion-button>\n\n <!-- Redirect button -->\n <ng-template #redirectButton>\n <ion-button [href]=\"link.url\" rel=\"external\" color=\"tertiary\" class=\"ion-float-end\" target=\"_blank\">\n <ion-label translate>COMMON.BTN_SHOW_MORE</ion-label>\n </ion-button>\n </ng-template>\n\n </ion-col>\n </ion-row>\n\n </ion-grid>\n\n </ion-card-content>\n</ion-card>\n\n<!-- Install links -->\n<ion-card *ngIf=\"showInstallButton && !loading && !offline && installLinks\"\n color=\"secondary\"\n class=\"ion-no-margin\"\n @slideUpDownAnimation>\n <ion-card-content class=\"ion-no-padding\">\n <ion-grid>\n <ion-row *ngFor=\"let link of installLinks; last as last\">\n <ion-col>\n <ion-text class=\"ion-text-wrap\">\n <h3 [innerHTML]=\"'INFO.DOWNLOAD_APP_TITLE'| translate: {name: link.name, platform: getPlatformName(link.platform) }\"></h3>\n <h4><small *ngIf=\"last\" [innerHTML]=\"'INFO.DOWNLOAD_APP_HELP'|translate\"></small></h4>\n </ion-text>\n </ion-col>\n <ion-col size=\"auto\">\n <ng-container *ngTemplateOutlet=\"downloadButton; context: { $implicit: link }\"></ng-container>\n </ion-col>\n </ion-row>\n\n </ion-grid>\n\n </ion-card-content>\n</ion-card>\n\n<!-- Update offline feature card-->\n<ion-card *ngIf=\"showUpdateOfflineFeature && !loading && !offline\"\n color=\"accent\"\n class=\"main ion-no-margin\"\n @slideUpDownAnimation>\n <ion-card-content class=\"ion-no-padding\">\n <ion-grid>\n <ion-row>\n <ion-col>\n <ion-text class=\"ion-text-wrap\">\n <h4>\n <b [innerHTML]=\"'NETWORK.INFO.UPDATE_OFFLINE_MODE'| translate\"></b>\n </h4>\n <h3>\n <small [innerHTML]=\"'NETWORK.INFO.UPDATE_OFFLINE_MODE_HELP'|translate\"></small>\n </h3>\n </ion-text>\n </ion-col>\n <ion-col size=\"auto\">\n\n <!-- Retry button -->\n <ion-button color=\"tertiary\" class=\"ion-float-end\"\n (click)=\"onUpdateOfflineModeClick.emit($event)\">\n <span translate>NETWORK.BTN_UPDATE</span>\n </ion-button>\n </ion-col>\n </ion-row>\n </ion-grid>\n\n </ion-card-content>\n</ion-card>\n\n<!-- Display a download button, depending on the link URL, and the device platform -->\n<ng-template #downloadButton let-link>\n\n <ng-container *ngIf=\"link.downloadFilename; else redirectButton\">\n\n <!-- Cordova download button -->\n <ion-button *ngIf=\"isAndroidCordova; else webDownloadButton\"\n (click)=\"downloadLink($event, asLink(link))\"\n color=\"tertiary\" class=\"ion-float-end\" >\n <ion-label translate>COMMON.BTN_DOWNLOAD</ion-label>\n </ion-button>\n\n <!-- Web download button -->\n <ng-template #webDownloadButton>\n <ion-button [download]=\"link.downloadFilename\"\n [href]=\"link.url\"\n color=\"tertiary\" class=\"ion-float-end\" >\n <ion-label translate>COMMON.BTN_DOWNLOAD</ion-label>\n </ion-button>\n </ng-template>\n </ng-container>\n\n <!-- Web redirect button -->\n <ng-template #redirectButton>\n <ion-button [href]=\"link.url\" rel=\"external\" color=\"tertiary\" class=\"ion-float-end\" target=\"_blank\">\n <ion-label translate>COMMON.BTN_SHOW_MORE</ion-label>\n </ion-button>\n </ng-template>\n</ng-template>\n",
21646
+ template: "\n\n<!-- Offline mode card-->\n<ion-card *ngIf=\"showOfflineWarning && (!loading && offline || waitingNetwork)\"\n color=\"accent\"\n class=\"main ion-no-margin\"\n @slideUpDownAnimation>\n <ion-card-content class=\"ion-no-padding\">\n <ion-grid>\n <ion-row>\n <ion-col>\n <ion-text class=\"ion-text-wrap\" *ngIf=\"!waitingNetwork; else waitingNetworkText\">\n <h4>\n <b *ngIf=\"isLogin; else notLogin\" [innerHTML]=\"'NETWORK.INFO.OFFLINE_OR_UNAUTHORIZED'| translate\"></b>\n <ng-template #notLogin>\n <b [innerHTML]=\"'NETWORK.INFO.OFFLINE'| translate\"></b>\n </ng-template>\n </h4>\n <h3>\n <small [innerHTML]=\"'NETWORK.INFO.OFFLINE_HELP'|translate\"></small>\n </h3>\n </ion-text>\n <ng-template #waitingNetworkText>\n <ion-text class=\"ion-text-wrap\" [innerHTML]=\"'NETWORK.INFO.RETRY_TO_CONNECT'| translate\"></ion-text>\n </ng-template>\n </ion-col>\n <ion-col size=\"auto\">\n\n <!-- Retry button -->\n <ion-button *ngIf=\"!waitingNetwork; else waitingSpinner\" color=\"tertiary\" class=\"ion-float-end\"\n (click)=\"tryOnline()\">\n <span translate>NETWORK.BTN_CHECK_ALIVE</span>\n </ion-button>\n\n </ion-col>\n </ion-row>\n </ion-grid>\n\n </ion-card-content>\n</ion-card>\n\n<!-- Upgrade links -->\n<ion-card *ngIf=\"showUpgradeWarning && !loading && !offline && updateLinks\"\n color=\"accent\"\n class=\"ion-no-margin\"\n @slideUpDownAnimation>\n <ion-card-content class=\"ion-no-padding\">\n <ion-grid>\n <ion-row *ngFor=\"let link of updateLinks; last as last\">\n <ion-col>\n <ion-text class=\"ion-text-wrap\">\n <h3>\n <span *ngIf=\"link.version\" [innerHTML]=\"'INFO.UPDATE_APP_TO_VERSION'| translate: link\"></span>\n <span *ngIf=\"!link.version\" [innerHTML]=\"'INFO.UPDATE_APP'| translate: link\"></span>\n </h3>\n <h4>\n <small *ngIf=\"last\" [innerHTML]=\"'INFO.UPDATE_APP_HELP'|translate\"></small>\n </h4>\n </ion-text>\n </ion-col>\n\n <ion-col size=\"auto\">\n <ng-container *ngTemplateOutlet=\"downloadButton; context: { $implicit: link }\"></ng-container>\n </ion-col>\n </ion-row>\n\n </ion-grid>\n\n </ion-card-content>\n</ion-card>\n\n<!-- Install links -->\n<ion-card *ngIf=\"showInstallButton && !loading && !offline && installLinks\"\n color=\"secondary\"\n class=\"ion-no-margin\"\n @slideUpDownAnimation>\n <ion-card-content class=\"ion-no-padding\">\n <ion-grid>\n <ion-row *ngFor=\"let link of installLinks; last as last\">\n <ion-col>\n <ion-text class=\"ion-text-wrap\">\n <h3 [innerHTML]=\"'INFO.DOWNLOAD_APP_TITLE'| translate: {name: link.name, platform: getPlatformName(link.platform) }\"></h3>\n <h4><small *ngIf=\"last\" [innerHTML]=\"'INFO.DOWNLOAD_APP_HELP'|translate\"></small></h4>\n </ion-text>\n </ion-col>\n <ion-col size=\"auto\">\n <ng-container *ngTemplateOutlet=\"downloadButton; context: { $implicit: link }\"></ng-container>\n </ion-col>\n </ion-row>\n\n </ion-grid>\n\n </ion-card-content>\n</ion-card>\n\n<!-- Update offline feature card-->\n<ion-card *ngIf=\"showUpdateOfflineFeature && !loading && !offline\"\n color=\"accent\"\n class=\"main ion-no-margin\"\n @slideUpDownAnimation>\n <ion-card-content class=\"ion-no-padding\">\n <ion-grid>\n <ion-row>\n <ion-col>\n <ion-text class=\"ion-text-wrap\">\n <h4>\n <b [innerHTML]=\"'NETWORK.INFO.UPDATE_OFFLINE_MODE'| translate\"></b>\n </h4>\n <h3>\n <small [innerHTML]=\"'NETWORK.INFO.UPDATE_OFFLINE_MODE_HELP'|translate\"></small>\n </h3>\n </ion-text>\n </ion-col>\n <ion-col size=\"auto\">\n\n <!-- Retry button -->\n <ion-button color=\"tertiary\" class=\"ion-float-end\"\n (click)=\"onUpdateOfflineModeClick.emit($event)\">\n <span translate>NETWORK.BTN_UPDATE</span>\n </ion-button>\n </ion-col>\n </ion-row>\n </ion-grid>\n\n </ion-card-content>\n</ion-card>\n\n<!-- Display a download button, depending on the link URL, and the device platform -->\n<ng-template #downloadButton let-link>\n\n <ng-container *ngIf=\"link.downloadFilename; else redirectButton\">\n\n <ng-container *ngIf=\"platform.canDownload; else webDownloadButton\">\n <!-- Download using platform -->\n <ion-button *ngIf=\"!downloading; else waitingSpinner\"\n (click)=\"download($event, asLink(link))\"\n [disabled]=\"downloading\"\n color=\"tertiary\" class=\"ion-float-end\">\n <ion-icon slot=\"start\" *ngIf=\"!downloading\" name=\"download\"></ion-icon>\n <ion-spinner slot=\"start\" class=\"ion-no-padding\" *ngIf=\"downloading\"></ion-spinner>\n <ion-label translate>COMMON.BTN_DOWNLOAD</ion-label>\n </ion-button>\n </ng-container>\n\n <!-- Web download button -->\n <ng-template #webDownloadButton>\n <ion-button [download]=\"link.downloadFilename\"\n [href]=\"link.url\"\n color=\"tertiary\" class=\"ion-float-end\" >\n <ion-label translate>COMMON.BTN_DOWNLOAD</ion-label>\n </ion-button>\n </ng-template>\n </ng-container>\n\n <!-- Web redirect button -->\n <ng-template #redirectButton>\n <ion-button [href]=\"link.url\" rel=\"external\" color=\"tertiary\" class=\"ion-float-end\" target=\"_blank\">\n <ion-label translate>COMMON.BTN_SHOW_MORE</ion-label>\n </ion-button>\n </ng-template>\n</ng-template>\n\n\n<!-- Waiting spinner -->\n<ng-template #waitingSpinner>\n <ion-spinner color=\"light\"></ion-spinner>\n</ng-template>\n",
21459
21647
  changeDetection: i0.ChangeDetectionStrategy.OnPush,
21460
21648
  animations: [slideUpDownAnimation],
21461
21649
  styles: ["ion-text small{font-size:85%}"]
@@ -21464,8 +21652,11 @@
21464
21652
  AppInstallUpgradeCard.ctorParameters = function () { return [
21465
21653
  { type: i1$5.ModalController },
21466
21654
  { type: ConfigService },
21467
- { type: PlatformService },
21655
+ { type: i1$5.ToastController },
21656
+ { type: i1$5.AlertController },
21657
+ { type: i1$2.TranslateService },
21468
21658
  { type: i0.ChangeDetectorRef },
21659
+ { type: PlatformService },
21469
21660
  { type: NetworkService },
21470
21661
  { type: undefined, decorators: [{ type: i0.Inject, args: [ENVIRONMENT,] }] }
21471
21662
  ]; };
@@ -27155,6 +27346,7 @@
27155
27346
  _this.filterCriteriaCount = 0;
27156
27347
  _this.statusList = StatusList;
27157
27348
  _this.statusById = StatusById;
27349
+ _this.useSticky = false;
27158
27350
  _this.referentialToString = referentialToString;
27159
27351
  _this.inlineEdition = accountService.isAdmin(); // Allow inline edition only if admin
27160
27352
  _this.canEdit = accountService.isAdmin();
@@ -27273,13 +27465,13 @@
27273
27465
  UsersPage.decorators = [
27274
27466
  { type: i0.Component, args: [{
27275
27467
  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",
27468
+ 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
27469
  providers: [
27278
27470
  { provide: ngxMaterialTable.ValidatorService, useExisting: PersonValidatorService }
27279
27471
  ],
27280
27472
  animations: [slideUpDownAnimation],
27281
27473
  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}"]
27474
+ 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
27475
  },] }
27284
27476
  ];
27285
27477
  UsersPage.ctorParameters = function () { return [
@@ -27299,6 +27491,7 @@
27299
27491
  { type: undefined, decorators: [{ type: i0.Inject, args: [ENVIRONMENT,] }] }
27300
27492
  ]; };
27301
27493
  UsersPage.propDecorators = {
27494
+ useSticky: [{ type: i0.Input }],
27302
27495
  filterExpansionPanel: [{ type: i0.ViewChild, args: [expansion.MatExpansionPanel, { static: true },] }]
27303
27496
  };
27304
27497
 
@@ -27572,6 +27765,7 @@
27572
27765
  exports.SharedTestingModule = SharedTestingModule;
27573
27766
  exports.SharedValidators = SharedValidators;
27574
27767
  exports.SocialModule = SocialModule;
27768
+ exports.StartableService = StartableService;
27575
27769
  exports.StatusById = StatusById;
27576
27770
  exports.StatusIds = StatusIds;
27577
27771
  exports.StatusList = StatusList;